KingbaseES 中文乱码、排序不对?区域和字符集的问题,一次说清
"表里存的中文查出来是乱码""ORDER BY 排序结果和我想的不一样""导入数据报字符集不支持"------这些问题的根源,八成都在数据库的区域(locale)和字符集(encoding)配置上。
KingbaseES 的本地化支持手册把这套机制讲得很细,但文档偏枯燥。这篇用人话把核心讲明白,顺便说说怎么避坑。
区域是什么,影响什么
区域(locale)是数据库对"文化习惯"的遵循:字母表怎么排、数字怎么写、日期怎么显示、消息用什么语言。
KingbaseES 通过操作系统提供的 ISO C 和 POSIX 区域机制来实现,初始化数据库时(initdb)如果不指定,就用当前环境变量里的区域设置。
区域有六个分类,各自管一摊:
| 分类 | 管什么 |
|---|---|
| LC_COLLATE | 字符串排序顺序 |
| LC_CTYPE | 字符分类(大小写、字母数字判断) |
| LC_MESSAGES | 消息语言 |
| LC_MONETARY | 货币格式 |
| LC_NUMERIC | 数字格式 |
| LC_TIME | 日期时间格式 |
最关键的两个是 LC_COLLATE 和 LC_CTYPE,因为它们在建库时就固定了,之后改不了(除非用排序规则绕过)。其他四个分类可以通过配置参数在运行时改。
建库时怎么指定:
bash
initdb --locale=zh_CN.UTF-8
# 或者混合指定
initdb --locale=fr_CA --lc-monetary=en_US
建库层面也能指定:
sql
CREATE DATABASE mydb WITH ENCODING 'UTF8' LC_COLLATE='zh_CN' LC_CTYPE='zh_CN' TEMPLATE=template0;
注意:LC_COLLATE 和 LC_CTYPE 一旦建库定了,之后没法改。索引排序也依赖它。所以建库前想清楚,别图省事随便选,后面想改只能重建库。
特殊区域 C / POSIX
C 或 POSIX 是"传统 C 行为"区域------不做任何本地化处理,按字节二进制比较排序。
特点:性能最好,但排序结果就是字节序,中文按编码二进制排,不是按拼音或笔画。如果你只是存数据不做中文排序,C 区域性能最优;要中文排序就得用带区域的配置。
字符集:存什么编码,得先想好
KingbaseES 支持单字节(ISO 8859 系列)和多字节(EUC、UTF-8 等)编码。
关键规则:每个数据库的字符集必须和 LC_CTYPE/LC_COLLATE 兼容。
- C/POSIX 区域允许任意字符集
- 其他区域通常只允许一种配套编码(比如 zh_CN 区域配 UTF8 或 EUC_CN)
- UTF-8 兼容性最广
建库时指定字符集:
sql
CREATE DATABASE korean WITH ENCODING 'EUC_KR' LC_COLLATE='ko_KR' TEMPLATE=template0;
查看现有库的字符集:
bash
ksql -l
服务端和客户端编码自动转换
客户端连上来,编码和服务端不一致怎么办?KingbaseES 支持自动转换,转换关系存在 sysconversion 表里。
客户端可以这样指定自己的编码:
sql
SET client_encoding TO 'UTF8';
SET NAMES 'GBK';
或者用环境变量 KINGBASE_CLIENTENCODING、配置文件里设 client_encoding。
转换不了的会报错。客户端用 SQLASCII 时禁用转换。
这就是乱码最常见的原因 :服务端 UTF8,客户端工具默认 GBK 或反过来,两边没对齐,显示就乱。解决思路就一句话------统一编码,或者显式指定 client_encoding。
排序:为什么 ORDER BY 结果"不对"
"中文按拼音排才对啊,怎么出来是乱的?"------因为默认排序走的是建库时的 LC_COLLATE。
- 建库用的 C 区域:按字节二进制排,中文自然"乱"
- 建库用 zh_CN 之类的区域:按区域规则排
如果建库时定死了 C 区域,后来想按中文排怎么办?两个办法:
- 查询时指定排序规则:
ORDER BY name COLLATE "zh_CN" - 建索引时指定排序规则:
CREATE INDEX idx_name ON t (name COLLATE "zh_CN")
性能的账,得算清楚
非 C/POSIX 区域有性能代价:
- 字符处理速度变慢
LIKE之类的模式匹配用不了普通索引(可以建自定义操作符类或 C 排序规则索引来缓解)
所以如果业务不依赖本地化排序,用 C 区域最划算;确实要中文排序,就接受性能代价或者有针对性地建索引。
几个容易踩的坑
- 别用 SQLASCII 存非 ASCII 数据。手册明确不建议,非 ASCII 数据容易出问题。
- JDBC 等客户端接口对部分字符集支持有限,比如 MULE INTERNAL 这种,客户端可能连不上或乱码。生产环境统一用 UTF8 最省心。
- 建库后 LC_COLLATE/LC_CTYPE 改不了,索引排序依赖它,选错只能重建库。
- 客户端和服务端区域不同,报错消息语言可能不一样------这也是官方建议程序判断用错误代码、别依赖消息文本的原因之一。
结尾
区域和字符集属于"建库时半小时、建库后要人命"的配置。动手建库前,花两分钟想清楚三件事:
- 业务数据是什么语言?→ 决定字符集(建议 UTF8)
- 要不要按本地化规则排序?→ 决定 LC_COLLATE(要就选对应区域,不要就 C)
- 客户端用什么编码连?→ 统一编码或显式 SET NAMES
想清楚了再建库,能省掉后面无数个"怎么又乱码了"的深夜。