当前位置:首页 > 问答 > 正文

数据库编码那些事儿,怎么用才能不出错还挺重要

说到数据库编码,听起来像是程序员才需要关心的深奥话题,但其实它就像我们给一个多语言图书馆制定的一套图书分类和书写规则,规则定得好,中文书、英文书、甚至一些稀奇古怪的文字书都能各就各位,查找起来又快又准;规则定得不好,或者中途乱改,那可能就会出现书架上摆了一堆谁也看不懂的“天书”,或者你想找一本中文书,系统却永远告诉你“查无此书”,了解一点数据库编码的皮毛,对于确保你的数据,尤其是中文数据,能正确存储和显示,确实挺重要的。

要理解编码,我们得从最根本的说起,计算机底层只能识别0和1,那么如何用0和1来表示我们看到的文字呢?这就需要一套映射规则,比如字母“A”对应一个数字65,汉字“中”对应另一个数字20013,这个数字我们称之为“码点”,而编码,就是规定如何将这些码点转换成二进制序列存储在计算机里的方案。

数据库编码那些事儿,怎么用才能不出错还挺重要

早期计算机世界是英语的天下,于是就有了ASCII编码(美国信息交换标准代码),它用7位二进制数(后来扩展为8位)来表示英文字母、数字和一些常用符号,总共也就256个位置,这套规则简单高效,但根本容纳不下成千上万的汉字,为了解决多语言问题,各个国家和地区纷纷制定了自家的编码标准,比如中国的GB2312(后来扩展为GBK、GB18030),台湾地区的Big5等,这些编码都属于“ANSI”编码体系,其特点是:用一个字节表示英文字符(为了兼容ASCII),用两个字节表示一个汉字,这就带来了第一个大问题:“乱码”,如果你用GBK编码去打开一个用Big5编码保存的网页,原本的繁体中文就会变成一堆莫名其妙的字符,因为同一个数字在两个编码标准里代表的字完全不同,这就好比用英文词典去查一个法文单词,结果肯定是驴唇不对马嘴。

为了结束这种“战国时代”,一个叫Unicode(统一码)的字符集被创造出来,它的目标很宏大:给世界上所有语言的每个字符都赋予一个全球唯一的码点,相当于给全世界的文字都上了“身份证号”,这下好了,无论哪种语言,在Unicode里都有了自己的位置,“中”字在任何支持Unicode的系统里都是那个“中”字,从根源上解决了乱码问题。

数据库编码那些事儿,怎么用才能不出错还挺重要

光有字符集(字符和码点的映射关系)还不够,还需要具体的编码方案(如何存储这些码点),Unicode本身有多种编码实现方式,最常见的有三种:UTF-8, UTF-16和UTF-32,UTF-32最简单粗暴,每个字符都用4个字节来存储,虽然处理速度快,但空间浪费严重,UTF-16则是对大多数字符用2个字节,对生僻字用4个字节,算是一种折中。

而如今应用最广泛的,是UTF-8编码,它是一种变长的编码方案,非常聪明:对于标准的ASCII字符,它就用一个字节表示,完全兼容ASCII;对于其他字符,会用2个、3个甚至4个字节来表示,这种特性使得UTF-8在存储英文内容时极其节省空间,同时又能够支持全世界所有语言,正因为这种优越性,UTF-8已经成为互联网和软件开发领域的实际标准。

数据库编码那些事儿,怎么用才能不出错还挺重要

回到数据库,我们怎么用才能不出错呢?关键在于“统一”二字。

第一,数据库创建时就要选对编码,现在绝大多数情况下,你应该毫不犹豫地选择UTF-8系列(在MySQL中可能是utf8mb4,因为早期的utf8并不支持所有Unicode字符,utf8mb4才是完整的UTF-8),这为你未来的多语言支持打下了最好的基础,来源自MySQL官方文档的建议就明确指出,新项目应使用utf8mb4字符集。

第二,保证连接的一致性,你的应用程序(比如PHP, Java程序)在连接数据库时,也需要指定相同的字符集,也就是说,数据出去(从程序到数据库)和进来(从数据库到程序)的“翻译官”必须说的是同一种“语言”,如果连接层指定了错误的编码,即便数据库本身是UTF-8,数据在传输过程中也可能已经被“译错”了。

第三,确保数据来源的编码清晰,如果你的网页表单是UTF-8编码的,那么提交上来的数据就是UTF-8的,如果你的程序需要从一个GBK编码的旧系统或文件里导入数据,你必须先进行转码,将数据从GBK转换为UTF-8,再存入UTF-8的数据库,直接混用会导致数据库中存在编码不一致的“脏数据”,后期清理起来非常麻烦。

避免数据库编码出错的核心秘诀就是:从一而终,全线统一,数据库用UTF-8(推荐utf8mb4),程序连接用UTF-8,前后端数据传输用UTF-8,文件读写也注意编码转换,把这根“UTF-8”的弦绷紧了,你基本就能和绝大多数乱码问题说再见了,这事儿看似是小细节,但一旦出问题,排查起来犹如大海捞针,所以从一开始就做对,非常重要。