贴一个图,可以看的更清楚一点。
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)也使用相同的编码