贴一个图,可以看的更清楚一点。
PostgreSQL各个进程的启动过程与区域设置:
1. 错误消息的语言
根据上面的图,当postgresql.conf中的loacle参数省略时,消息语言受环境loacle影响。下面验证一下。
当前的环境loacle是zh_CN.utf8,但postgresql.conf中没有设置loacle。
- -bash-4.1$ grep lc_ postgresql.conf
- #lc_messages = 'zh_CN.utf8' # locale for system error message
- #lc_monetary = 'zh_CN.utf8' # locale for monetary formatting
- #lc_numeric = 'zh_CN.utf8' # locale for number formatting
- #lc_time = 'zh_CN.utf8' # locale for time formatting
- -bash-4.1$ locale
- LANG=en_US.UTF-8
- LC_CTYPE="zh_CN.utf8"
- LC_NUMERIC="zh_CN.utf8"
- LC_TIME="zh_CN.utf8"
- LC_COLLATE="zh_CN.utf8"
- LC_MONETARY="zh_CN.utf8"
- LC_MESSAGES="zh_CN.utf8"
- LC_PAPER="zh_CN.utf8"
- LC_NAME="zh_CN.utf8"
- LC_ADDRESS="zh_CN.utf8"
- LC_TELEPHONE="zh_CN.utf8"
- LC_MEASUREMENT="zh_CN.utf8"
- LC_IDENTIFICATION="zh_CN.utf8"
- LC_ALL=zh_CN.utf8
- -bash-4.1$ pg_ctl -D `pwd` start
- 正在启动服务器进程
- -bash-4.1$ < 2014-09-19 20:18:11.582 CST >日志: redirecting log output to logging collector process
- < 2014-09-19 20:18:11.582 CST >提示: Future log output will appear in directory "pg_log".
再看下日志文件,也是中文的。
- -bash-4.1$ tail -f pg_log/postgresql-Fri.log
- < 2014-09-19 20:18:11.584 CST >日志: 数据库上次关闭时间为 2014-09-19 20:17:42 CST
- < 2014-09-19 20:18:11.588 CST >日志: 数据库系统准备接受连接
- < 2014-09-19 20:18:11.588 CST >日志: 已启动autovacuum
用psql连接上去,发现lc_messages是空的,其他几个lc_*是C,并且获得的服务端消息语言也是继承了服务端环境loacle(即中文消息)。
- -bash-4.1$ psql
- psql (9.3.4)
- 输入 "help" 来获取帮助信息.
- postgres=# show lc_messages;
- lc_messages
- -------------
-
- (1 行记录)
- postgres=# show lc_monetary;
- lc_monetary
- -------------
- C
- (1 行记录)
- postgres=# show lc_numeric;
- lc_numeric
- ------------
- C
- (1 行记录)
- postgres=# show lc_time;
- lc_time
- ---------
- C
- (1 行记录)
- postgres=# ss;
- 错误: 语法错误 在 "ss" 或附近的
- 第1行ss;
- ^
设置消息为C,服务端过来的消息变成了英文。
- postgres=# set lc_messages="C";
- SET
- postgres=# ss2;
- ERROR: syntax error at or near "ss2"
- 第1行ss2;
- ^
日志中的消息也变成了英文(前一条中文消息是上次的)。
- < 2014-09-19 20:23:03.068 CST >错误: 语法错误 在 "ss" 或附近的 第 1 个字符处
- < 2014-09-19 20:23:03.068 CST >语句: ss;
- < 2014-09-19 20:33:30.811 CST >ERROR: syntax error at or near "ss2" at character 1
- < 2014-09-19 20:33:30.812 CST >STATEMENT: ss2
但是用set只影响这个会话。开个新的会话,还是中文的消息。
- -bash-4.1$ psql
- psql (9.3.4)
- 输入 "help" 来获取帮助信息.
- postgres=# ss3;
- 错误: 语法错误 在 "ss3" 或附近的
- 第1行ss3;
- ^
如果想全局修改消息语言,可以修改postgresql.conf后用pg_ctl reload动态加载。
- -bash-4.1$ vi postgresql.conf
- lc_messages = 'C'
- -bash-4.1$ pg_ctl -D `pwd` reload
- server signaled
服务端日志已经切换到英文了
- < 2014-09-19 20:41:49.012 CST >日志: 接收到 SIGHUP, 重载配置文件
- < 2014-09-19 20:41:49.013 CST >LOG: parameter "lc_messages" changed to "C"
查看之前一直打开着的会话,也已经切换到英文了。
- postgres=# ss4;
- ERROR: syntax error at or near "ss4"
- 第1行ss4;
- ^
2. 错误消息的编码
总的来说消息的语言决定于postgresql.conf中的lc_messages,postgresql.conf未指定时取决于启动postgres的环境loacle。那么消息的编码呢?
gettext取得的消息的编码取决于LC_CTYPE,而不是LC_MESSAGES。所以,在下面的设置中,本地化消息的编码是GBK而不是UTF8。
- [chenhj@hanode1 ~]$ export LC_ALL=
- [chenhj@hanode1 ~]$ export LC_CTYPE=zh_CN.gbk
- [chenhj@hanode1 ~]$ export LC_MESSAGES=zh_CN.utf8
- [chenhj@hanode1 ~]$ ls xx
- ls: ?·¨·??x: ???????倂?
为了便于区分,我的终端软件编码一直设置为UTF8,所以当你看到了乱码,就表示它不是UTF8编码,后面的测试也是一样。
根据之前的图,postmaster和辅助进程的LC_TYPE应该取决于环境locale,postgres进程的LC_TYPE则取决于数据库的编码。下面也实测一下。
修改postgresql.conf中的lc_messages值为zh_CN.utf8,环境变量LC_CTYPE设为zh_CN.gb2312。
- -bash-4.1$ vi postgresql.conf
- lc_messages = 'zh_CN.utf8'
- -bash-4.1$ export LC_CTYPE="zh_CN.gb2312"
- -bash-4.1$ locale
- LANG=en_US.UTF-8
- LC_CTYPE=zh_CN.gb2312
- LC_NUMERIC="en_US.UTF-8"
- LC_TIME="en_US.UTF-8"
- LC_COLLATE="en_US.UTF-8"
- LC_MONETARY="en_US.UTF-8"
- LC_MESSAGES="en_US.UTF-8"
- LC_PAPER="en_US.UTF-8"
- LC_NAME="en_US.UTF-8"
- LC_ADDRESS="en_US.UTF-8"
- LC_TELEPHONE="en_US.UTF-8"
- LC_MEASUREMENT="en_US.UTF-8"
- LC_IDENTIFICATION="en_US.UTF-8"
- LC_ALL=
重启服务器,输出的消息中出现了个gb2312编码的字符(这部分消息是postmaster进程输出的)。
- -bash-4.1$ pg_ctl -D `pwd` restart
- waiting for server to shut down.... done
- server stopped
- server starting
- -bash-4.1$ < 2014-09-19 21:09:25.882 CST >??: redirecting log output to logging collector process
- < 2014-09-19 21:09:25.882 CST >??: Future log output will appear in directory "pg_log".
用psql连进来看下,消息是正常的(这部分消息来自postgres进程)。
- -bash-4.1$ locale
- LANG=en_US.UTF-8
- LC_CTYPE="zh_CN.utf8"
- LC_NUMERIC="zh_CN.utf8"
- LC_TIME="zh_CN.utf8"
- LC_COLLATE="zh_CN.utf8"
- LC_MONETARY="zh_CN.utf8"
- LC_MESSAGES="zh_CN.utf8"
- LC_PAPER="zh_CN.utf8"
- LC_NAME="zh_CN.utf8"
- LC_ADDRESS="zh_CN.utf8"
- LC_TELEPHONE="zh_CN.utf8"
- LC_MEASUREMENT="zh_CN.utf8"
- LC_IDENTIFICATION="zh_CN.utf8"
- LC_ALL=zh_CN.utf8
- -bash-4.1$ psql
- psql (9.3.4)
- 输入 "help" 来获取帮助信息.
- postgres=# ss5;
- 错误: 语法错误 在 "ss5" 或附近的
- 第1行ss5;
- ^
日志中的消息也是UTF8。而且即使像这样修改客户端的编码为gb2312。
- -bash-4.1$ export LC_ALL=zh_CN.gb2312
- -bash-4.1$ psql
- psql (9.3.4)
- ?? "help" 4?衰?х?.
- postgres=# ss6;
- ??: ?·¨?? ? "ss6" ?载
- ???ss6;
- ^
- < 2014-09-19 21:09:25.884 CST >??: ?????ι??±?厪 2014-09-19 21:09:24 CST
- < 2014-09-19 21:09:25.890 CST >??: ??????±??????
- < 2014-09-19 21:09:25.891 CST >??: ????autovacuum
- < 2014-09-19 21:14:34.095 CST >错误: 语法错误 在 "ss5" 或附近的 第 1 个字符处
- < 2014-09-19 21:14:34.095 CST >语句: ss5;
- < 2014-09-19 21:26:52.511 CST >错误: 语法错误 在 "ss6" 或附近的 第 1 个字符处
- < 2014-09-19 21:26:52.511 CST >语句: ss6
创建一个gb2312编码的数据库,连上去。错误消息就是gb2312了。
- -bash-4.1$ export LC_ALL=zh_CN.utf8
- -bash-4.1$ createdb -T template0 -E EUC_CN --locale=C euccn
- -bash-4.1$ psql euccn
- psql (9.3.4)
- 输入 "help" 来获取帮助信息.
- euccn=# ss7;
- 错误: 语法错误 在 "ss7" 或附近的
- 第1行ss7;
- ^
- < 2014-09-19 21:38:42.203 CST >语句: CREATE DATABASE euccn ENCODING 'EUC_CN' LC_COLLATE 'C' LC_CTYPE 'C';
-
- < 2014-09-19 21:40:31.478 CST >??: ?·¨?? ? "ss7" ?载? 1 ?薷
- < 2014-09-19 21:40:31.478 CST >??? ss7
3. 结论
输出本地语言消息时服务端日志文件中可能会混合不同编码的消息。为了避免这种事发生,需要注意以下两点1)同一数据库集群(cluster)中的多个数据库使用同一种编码
2)启动数据库时的环境locale(LC_CTYPE)也使用相同的编码