AirJD 焦点
AirJD

没有录音文件
00:00/00:00
加收藏

PostgreSQL中的区域和编码 by 陈华军

发布者 PostgreSQL
发布于 1437959976598  浏览 6833 关键词 PostgreSQL, 数据库 
分享到

第1页

PostgreSQL中的区域和编码

南京富士通南大软件技术有限公司 陈华军 2014/12/12



第2页

大纲


 个人介绍 
 区域及编码概述 
 PostgreSQL中的区域和编码 
 字符编码 
 排序规则 
 其它 
 总结



第3页

个人介绍

现为富士通南大软件技术有限公司高级工程师,从事数据库产品相关的开发。 
 2002年 加入富士通南大 
 2006年~ 从事富士通关系数据库Symfoware相关的开发和维护 
 2012年~ 伴随Symfoware V12 Open版接触PostgreSQL

(Symfoware V12 Open版基于PostgreSQL定制) 
 2012年~ 向PostgreSQL社区贡献了少量的 Patch

(pgsql,pgjdbc,npgsql合计10+件) 
 2013年 中国PG大会分享《HA故障转移时的应用切换方案实践》

QQ:30041724 Mail:chenhj@cn.fujitsu.com Blog:http://http://blog.chinaunix.net/uid/20726500.html



第4页

区域及编码概述


什么是区域 
区域的分类(category) 
OS的区域设置



第5页

什么是区域

区域

由于国家和本地习惯的不同,在处理文字、日期、数字、货币格式等问题 都会有所不同,计算机中“区域(Locale)”就是用来解决这个问题的。

区域名的形式

language_territory[.codeset]

例:

特殊的区域(C或者POSIX)

man setlocale The locale "C" or "POSIX" is a portable locale; its LC_CTYPE part corresponds to the 7-bit ASCII character set.



第6页

区域的分类(category)



主要的区域分类(category)



分类 LC_COLLATE



作用 字符排序顺序



LC_CTYPE



字符分类(什么是字母?是否区分大小写?)



LC_MESSAGES



消息的语言



LC_MONETARY



货币金额的格式



LC_NUMERIC LC_TIME



数值格式 6 日期和时间格式



注)字符编码一般由LC_CTYPE决定



第7页

OS的区域设置

Linux: 通过环境变量(LC_ALL/LC_*/LANG)设置



Windows:



环境变量的优先级: LC_ALL > LC_* > LANG





第8页

PostgreSQL中的区域和编码


区域相关的配置 
区域相关配置的影响 
设置区域和编码



第9页

区域相关的配置 (1)

数据库固有的区域相关属性


包括以下3种属性

Encoding Collate Ctype


可通过psql的”\l”或系统表pg_database查看


 数据库创建后,不可变更



第10页

区域相关的配置(2)

postgresql.conf中的区域相关参数


主要包括以下属性

client_encoding lc_messages lc_monetary lc_numeric lc_time DateStyle default_text_search_config


 可通过SHOW命令或系统表pg_settings查看


 可通过pg_ctl reload命令在线全局变更 
 可通过set命令修改当前会话的参数



第11页

区域相关配置的影响



数据库(pg_database)



项目



说明



E数nc据o库din(pgg_database数) 据库编码



影响 postgres进程和数据文件的字符编码



Collate



字符排序顺序



字符比较,ORDER BY,字符列的索引 使用



Ctype



字符分类(什么是字母? 是否区分大小写?)



upper, lower, initcap 大小写不敏感的模式匹配和使用了字 符分类的正则表达式匹配



postgresql.conf

项目

client_encoding lc_messages lc_monetary lc_numeric lc_time DateStyle …



说明 客户端编码 消息的语言



影响 C/S间字符编码转换 错误消息的语言



货币,数值和时间的 货币,数值和日期类型数据的输出格式



显示格式



to_char函数族





第12页

设置区域和编码 (1)



创建数据库集群时的区域设置


 initdb时可通过参数指定数据库集群的缺省区域和编码



initdb -E | --encoding --locale --lc-collate --lc-ctype --lc-messages --lc-monetary --lc-numeric --lc-time



postgres template0 template1

postgresql.conf



根据区域设置的pg_database属性: encoding datcollate datctype

根据区域设置的参数: lc_messages lc_monetary lc_numeric lc_time datestyle(lc-time) default_text_search_config(lc-ctype)




 如未明确指定区域和编码,initdb通过OS运行环境判断区域和编码

Linux下是环境变量LC_ALL > LC_* > LANG





第13页

设置区域和编码 (2)



创建数据库时的区域设置




 创建数据库时默认继承数据库模板template1的区域设置

Encoding Collate Ctype


 也可以指定其它的区域设置(必须使用template0作为模板)



CREATE DATABASE newdb TEMPLATE=template0 ENCODING='UTF8' LC_COLLATE='zh_CN.utf8' LC_CTYPE='zh_CN.utf8'



newdb template0




 字符编码,LC_COLLATE和LC_CTYPE在数据库创建后不能变更





第14页

设置区域和编码 (3)



启动PostgreSQL进程时的区域设置




 PostgreSQL各个进程的启动过程与区域设置

1)根据环境变量设置区域属性 LC_COLLATE, LC_CTYPE,LC_MESSAGES

2 )设置以下区域属性为”C” LC_MONETARY, LC_NUMERIC, LC_TIME

3)根据postgresql.conf设置区域属性和PostgreSQL区域相关的参数 LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME

postmaster



5 )客户端连接时fork postgres进程

ppoposoststgtgrgreresess

6 )根据pg_database设置 LC_COLLATE, LC_CTYPE



4) fork 辅助进程



辅助进程

writer wal writer

logger stats collector checkpointer autovacuum launcher





第15页

设置区域和编码 (4)




 最终的PostgreSQL进程的区域相关属性由以下因素决定



 postgres进程及个别辅助进程(autovacuum, background worker)



LC_COLLATE



LC_CTYPE



LC_MESSAGES LC_MONETARY LC_NUMERIC



pg_database



Y



Y



postgresql.conf



YYY



系统环境

*) postgresql.conf中省略lc_messages时生效



Y(*)



LC_TIME Y



 postmaster及其它辅助进程



LC_COLLATE



LC_CTYPE



postgresql.conf



系统环境



YY



*) postgresql.conf中省略lc_messages时生效



LC_MESSAGES LC_MONETARY LC_NUMERIC



YY Y(*)



Y



LC_TIME Y





第16页

字符编码




字符编码和乱码




PostgreSQL支持的中文字符编码




C/S自动字符编码转换




client_encoding的设置




服务端的字符编码检查




编码检查不能完全杜绝乱码




小结





第17页

字符编码和乱码

字符编码

字符编码是字符在计算机中以二进制值表示的方法。比如’a’的ACSII码 是0x61。支持简体中文的字符编码包括: UTF8, GB2312(EUC_CN), GBK, GB18030

乱码

字符编码解码的过程中出现错误就会产生乱码。出现乱码可从以下几个方 面找原因: 显示的字体和字符编码 处理过程中的编码转换 数据的源头是否有非法字符



第18页

PostgreSQL支持的中文字符编码

服务端支持的简体中文字符编码

UTF8,EUC_CN

客户端支持的简体中文字符编码

UTF8,EUC_CN,GBK,GB18030

为什么服务端不支持GBK和GB18030?



PostgreSQL使用的flex按照ASCII编码解析SQL语句, ASCII不兼容的编码会让PostgreSQL头疼。



编码名 UTF8



编码范围

单字节:00-7F(ASCII) 多字节:80-XX



备注 ASCII兼容



EUC_CN(GB2312) 双字节:A1-F7,A1-FE



ASCII兼容



GBK GB18030



双字节:81-FE,40-7E/80-FE

单字节:00-7F(ASCII) 双字节:81-FE,40-7E/80-FE 四字节:81-FE,30-39,81-FE,30-39



ASCII不兼容 ASCII不兼容



第19页

C/S自动字符编码转换

C/S间编码不一致时服务端自动进行字符编码转换



Application GBK



set client_encoding to 'GBK'



postgres



GBK



UTF8



编码转换



UTF8



客户端通过client_encoding通知服务端自己的编码

设置client_encoding到的几种方式(越往下优先级越高)  postgresql.conf  连接包(StartupMessage)的参数  set client_encoding to ‘XXX’



第20页

client_encoding的设置



几个常见客户端设置client_encoding的不同方法



客户端(驱动)

libpq/ecpg/psql

pgjdbc Npgsql psqlODBC(unicode)

psqlODBC(ansi)



设置client_encoding的方法 1)客户端区域环境 2)PGCLIENTENCODING 3)连接参数client_encoding 4)API调用PQsetClientEncoding() 注)越往下优先级越高

UTF8

UTF8

UTF8

1)客户端区域环境 2)PGCLIENTENCODING 3) ConnSettings=set client_encoding to ‘XXX‘ 注)越往下优先级越高





第21页

服务端的字符编码检查(1)

PostgreSQL服务端对不符合数据库编码的字符报错

试图在EUC_CN的数据库上使用EUC_CN不支持的“丂”字时PostgreSQL报错



第22页

服务端的字符编码检查(2)

在UTF8的数据库上可以使用“丂”字

在UTF8的数据库上使用非法字符(\xff)同样报错



第23页

编码检查不能完全阻止非法字符(1)

通过convert_from插数据可以跳过数据库的编码检查

通过convert_from插入了EUC_CN编码外的字符“丂”(\x8140)到EUC_CN编码的数据库



但是,查询数据时现了形



BUG



第24页

编码检查不能完全阻止非法字 符(2)

在convert_from函数中使用SQL_ASCII,同样可以插入非法字符(\xff)到UTF8数据库 查询时也没有报错

不报错只是暂时的,强迫PostgreSQL做C/S转码,就会报错了



第25页

编码检查不能完全阻止非法字 符(3)


 前面的例子中暴露的问题  SQL_ASCII向其它编码转换时,PostgeSQL的一些地方没有检查字符代码是否 在目的编码范围内。  原编码和目的编码间没有定义默认的转换函数时,PostgeSQL没有报错。 (PostgreSQL中没有定义GBK/GB18030/EUC_CN间的默认转码函数)

上面2个问题已在PostgreSQL 9.4rc1中被修复!

9.4确保进入数据库的字符都经过了严格的编码检查。

参考:http://blog.163.com/digoal@126/blog/static/16387704020141139372877/



第26页

编码检查不能完全阻止非法字 符(4)

PostgreSQL不能检查出合法编码范围内的乱码

终端软件的编码是UTF8,但把client_encoding设成GBK



Xshell的终端编码设置



真的对吗?

数据插进去了,并且查出来也是”对的”。 换一个设置正确的终端查看数据(终端软件编码和client_encoding都是UTF8)

已经凌乱了



第27页

编码检查不能完全杜绝乱码(5)

SQL_ASCII编码的数据库完全没有编码检查

SQL_ASCII并不是指单纯的7bit的ASCII,而是1~255通吃(0是特例),所以可以 用SQL_ASCII存0字节以外的任何东西。包括各种编码字符的混合,或者不属于任 何编码的字节序列。

强烈建议不要用SQL_ASCII作为数据库编码!

http://58.58.27.50:8079/doc/html/9.3.1_zh/multibyte.html SQL_ASCII设置与其它设置表现得相当不同。如果服务器字符集 是SQL_ASCII, 服务器根据ASCII标准解析0-127的字节值,而字节值为128255的则当作未解析的字符。 如果设置为SQL_ASCII就不会有编码转换。因 此,这个设置基本不用来声明所使用的编码, 因为这个声明会忽略编码。在 大多数情况下,如果你使用了任何非ASCII数据, 那么使用SQL_ASCII设置都 是不明智的,因为PostgreSQL 会无法帮助你转换或者校验非ASCII字符。



第28页

编码相关的注意事项


服务端使用UTF8作为数据库编码  SQL_ASCII缺少编码检查  EUC_CN的字符不够多,并且PostgreSQL没有定义EUC_CN

和GBK/GB18030间的默认转码函数 
客户端设置合适的区域环境和client_encoding  psql等工具能根据环境自动设置合适的client_encoding,因此执

行psql前需要设置好合适的区域环境。  使用libpq编程时需注意client_encoding的设置



第29页

排序规则


排序规则的影响 
排序规则的设置 
中文拼音排序规则的缺陷



第30页

排序规则的影响(1)

排序规则在PostgreSQL中的影响

 字符比较  ORDER BY  字符列的索引使用

PostgreSQL中字符比较的实现

C或POSIX 按照数据库字符编码下的字符二进制代码比较

其他区域 通过调用操作系统提供的strcoll()实现字符比较。中文区域通常按拼音比较。



第31页

排序规则的影响(2)



例1:排序



LC_COLLATE=C (按汉字编码排序)



UTF8:



LC_COLLATE=zh_CN (按汉字拼音排序)



GB2312:



NO 汉字 UTF8



GB2312



1 貂 E8 B2 82 F5 F5 2 王 E7 8E 8B CD F5



3 西 E8 A5 BF CE F7



4 杨 E6 9D A8 D1 EE



注:测试系统的OS是RHEL6



GB2312的一级汉字按拼音排序,二级汉字( 如:”貂”)按部首排序。所以按GB编码排序 可作为“近似的”拼音排序。



第32页

排序规则的影响(3)

例2:索引

环境准备: 查询的collate和索引的collate一致时会使用索引:

查询的collate和索引的collate不一致时不会使用索引:



第33页

排序规则的设置(1)

作为数据库属性的排序规则不能变更,但可以在列和表达式上指定不同的排序规则

指定列的排序规则

CREATE TABLE test1 ( a text COLLATE “C", b text COLLATE "zh_CN", ...

);

指定表达式的排序规则

create index on test1(a collate "zh_CN"); select * from test1 order by a collate "zh_CN";

Collate的优先级

表达式 > 列定义 > 数据库定义



第34页

排序规则的设置(2)

可动态指定的排序规则一览

可以在列和表达式上指定的”COLLATE”是定义在pg_collation中的对象,相当 于LC_COLLATE+LC_CTYPE组合的别名。



第35页

中文拼音排序规则的缺陷


zh_CN字符比较的性能比C差很多

恢复数据:性能差1倍 vacuum analyze:性能差5倍 建索引:性能差5倍 参考: http://bbs.pgsqldb.com:8079/client/post_show.php?zt_auto_bh=57394 http://my.oschina.net/quanzl/blog/143167 
某些多音字的拼音排序结果不一定符合预期 
依赖于操作系统实现,不同OS下结果可能不一样

考虑到性能,可以把数据库的Collate设成C,只在真正需要按拼音排序的列或者 表达式上使用Collate “zh_CN”。



第36页

其它


字符分类 
错误消息 
数据输出格式



第37页

字符分类(1)



影响的功能


 upper(),lower(),initcap() 
 大小写不敏感的模式匹配 
 使用了字符分类的正则表达式匹配

例:

Collate为“C”



Collate为“zh_CN”





第38页

字符分类(2)

正则表达式不支持全角英文和数字的分类:

但是OS上是支持的

PostgreSQL的正则表达式实现出于性能原因,缓存了unicode代码点0~0x7FF的字符 的字符分类,全角英文和数字不在这个范围内。

参考:http://blog.chinaunix.net/uid-20726500-id-4680122.html



第39页

错误消息 (1)

影响的功能

 Log中输出消息的语言  客户端捕获的服务端消息的语言



例:

LC_MESSAGES=“C”



LC_MESSAGES=“zh_CN”





第40页

错误消息 (2)

输出中文消息时的注意点

 服务端消息日志可能会包含不同字符编码的消息

不同字符编码的多个数据库的postgres进程输出的消息编码不同。

Postmaster及部分辅助进程可能输出的消息编码和postgres进程不同。  客户端得到的连接错误消息可能是乱码

连接时服务端还不知道客户端的client_encoding,如果客户端和服务库编码不一致可能 会导致乱码。同理,数据库名,用户名和密码中如果有中文,也可能会导致客户端无法建 立连接。

参考:http://blog.chinaunix.net/uid-20726500-id-4675473.html



第41页

数据输出格式 (1)

LC_MONETARY影响money数据类型的输出

例:

LC_MONETARY=“C”



LC_MONETARY=“zh_CN”



money类型比较鸡肋 不如使用numeric





第42页

数据输出格式 (2)

LC_NUMERIC和LC_TIME似乎对数据输出没有影响



例:

LC_NUMERIC =“C”



LC_NUMERIC =“zh_CN”



LC_TIME=“C”



LC_TIME =“zh_CN”





第43页

数据输出格式 (3)

DateStyle影响日期数据的输出 例:

DateStyle =“C”

DateStyle =sql

注) 其它一些影响数据输出格式的参数可参考PostgreSQL手册 http://www.postgresql.org/docs/9.4/static/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-FORMAT



第44页

数据输出格式 (4)



数据输出格式依赖于区域的弊端



 数据输出格式如果不确定将不利于数据处理,所以很多客户端接口会在连 接建立时就重新设置这些参数。比如Npgsql:



NpgsqlConnector.cs:



var parameters = new Dictionary<String, String> {



{ "database",



databaseName },



{ "user",



username },



{ "DateStyle",



"ISO" },



{ "client_encoding", "UTF8" },



{ "lc_monetary", "C"



},



};



 使用libpq编程时需要开发者自己留意数据输出格式





第45页

总结



相关建议



 服务端使用UTF8作为数据库编码  客户端设置合适的区域环境和client_encoding  数据库的Collate设成C

然后只在真正需要按拼音排序的列或者表达式上使用Collate“zh_CN”。  使用标准的区域无关的数据输出格式

例:

创建数据库集群:

initdb -E UTF8 --locale=C data



启动数据库:

export LC_ALL=zh_CN.UTF8

pg_ctl -D data -l logfile start



客户端建立连接:

export LC_ALL=zh_CN.UTF8

psql





第46页

参考

• http://www.postgresql.org/docs/9.4/static/runtime-config-client.html#RUNTIME-CONFIGCLIENT-FORMAT

• http://www.postgresql.org/docs/9.4/static/charset.html • http://blog.163.com/digoal@126/blog/static/16387704020141139372877/ • http://blog.163.com/digoal@126/blog/static/163877040201173003547236/ • http://bbs.pgsqldb.com:8079/client/post_show.php?zt_auto_bh=57394 • http://my.oschina.net/quanzl/blog/143167 • http://www.unisys.co.jp/services/atlasbase/pdf/dbm_1001_postresql.pdf



第47页

47 47



支持文件格式:*.pdf
上传最后阶段需要进行在线转换,可能需要1~2分钟,请耐心等待。