MySQL乱码问题深层分析


公司新购了一批PC,预备把几个性能较优的PC升级为数据库服务器,替换老旧的机器。公司有套POS终端软件,后台数据存储是 MySQL 3.23 版。我预备硬件升级的同时升级数据库软件。但是升级过程中碰到闻名的 MySQL 的乱码问题。经过查找资料,加上自己的摸索和经验,终于完美地解决这个问题。

MySQL 的乱码问题只存在于4.1及其以上版本。4.1之前的 MySQL 不支持多语言,所以它会将你给它的数据“原封不动”地保存,再“原封不动”地读出来。从字节的角度来看,数据在这一过程中不会产生任何变化,因此不会有乱码。

4.1及以后的版本开始支持多语言,这个所谓的多语言,就是在输入输出时 MySQL 会替你做编码转换。而这个转换规则就是由客户端编码和服务器端编码来决定的。

编码转换的规则就是,在输入数据时将编码由“客户端编码”转换为“服务器端编码”,输出时将数据由“服务器端编码”转换为“客户端编码”。 



二、乱码产生原因

MySQL 字符编码是版本4.1引入的,支持多国语言,而且一些特性已经超过了其它大多数数据库治理系统。正因为这一特性才导致 MySQL 的乱码问题。

字符集是一套符号和编码。校对规则是在字符集内用于比较字符的一套规则。让我们使用一个假想字符集的例子来区别清楚。

假设我们有一个字母表使用了四个字母:‘A’、‘B’、‘a’、‘b’。我们为每个字母赋予一个数值:‘A’=0,‘B’= 1,‘a’= 2,‘b’= 3。字母‘A’是一个符号,数字0是‘A’的编码,这四个字母和它们的编码组合在一起是一个字符集。

假设我们希望比较两个字符串的值:‘A’和‘B’。比较的最简单的方法是查找编码:‘A’为0,‘B’为1。因为0 小于1,我们可以说‘A’小于‘B’。我们做的仅仅是在我们的字符集上应用了一个校对规则。校对规则是一套规则:“对编码进行比较。”我们称这种全部可能的规则中的最简单的校对规则为一个binary校对规则。

但是,假如我们希望小写字母和大写字母是等价的,应该怎样?那么,我们将至少有两个规则:把小写字母‘a’和‘b’视为与‘A’和‘B’等价;然后比较编码。我们称这是一个大小写不敏感的校对规则。比二元校对规则复杂一些。

在实际生活中,大多数字符集有许多字符:不仅仅是‘A’和‘B’,而是整个字母表,有时候有许多种字母表,或者一个东方的使用上千个字符的书写系统,还有许多非凡符号和标点符号。并且在实际生活中,大多数校对规则有许多个规则:不仅仅是大小写不敏感,还包括重音符不敏感和多字节映射。

MySQL 4.1.x开始支持以下这

些事情

l 使用多种字符集来存储字符

l 使用多种校对规则来比较字符串

l 在同一台服务器、同一个数据库或甚至在同一个表中使用不同字符集或校对规则来混合字符串

l 答应定义任何级别的字符集和校对规则

MySQL 4.1及以上版本的字符集支持有两个方面:字符集和校对规则。 字符集和校对规则有4个级别的默认设置:服务器,数据库,数据表和连接。

MySQL 中是根据下面几个变量确定服务器端和客户端用的什么字符集:

character_set_client 客户端字符集

character_set_connection 客户端与服务器端连接采用的字符集

character_set_results SELECT查询返回数据的字符集

character_set_database数据库采用的字符集

MySQL的字符集处理是这样的:

1、发送请求。

1)客户端发送请求到服务器端。

2)服务器端会把请求的数据从客户端字符集转成服务器连接字符集。

3)然後服务器会检测存储区域的字符集,然后把数据从连接字符集转为存储区域的字符集,然後再存储或者查询。

2、返回请求。

1)服务器将存储区域的字符集转换成服务器连接字符集。

2)将服务器连接字符集转换成结果字符集,再发送到客户端。

例如,我建立一个字符集为 gbk 的数据库。

连接数据库的程序使用 gb2312 字符集,那么在执行 insert 命令时,insert 的字符串将做一个 gb2312 到 gbk 的转换。而 select 时,数据库中保存的数据会先经过 gbk 到 gb2312 的转换之后再给你。

好,那么为什么升级3.23到4.1时会乱码?举个例子说明。

例如3.23的数据库中保存的是gbk编码的数据。升级之前我将这些数据导出保存到文件里,这个文件的编码当然也是gbk的。

然后我在4.1中建立一个数据库,字符集为A;客户端字符集为B。将刚才的gbk数据导入。

1)A=gbk,B=gbk

导入数据时数据不会被转换;读出时需要set names gbk。

2)A=latin1,B=gbk

导入数据会进行gbk-latin1的转换,可能会丢失数据,产生乱码。

3)A=gbk,B=latin1

导入数据会进行latin1-gbk转换,可能会产生乱码。

4)A=latin1,B=latin1

导入数据时不会进行转换;读出时不需要set names gbk 。

大家可以看到,上面1)、4)才是正确的做法,即让A和B使用同样的字符集才不会乱码。



三、解决方案

了解了 MySQL 4.1.x 以上版本字符集处理的过程,我们就知道了怎么从原理上解决这个问题。

思路:让服务器端和客户端的字符集保持一致。

服务器端的编码是由字符集和校对规则决定的。

上面提到,MySQL 中是根据下面几个变量确定服务器端和客户端用的什么字符集:

character_set_client 客户端字符集

character_set_connection 客户端与服务器端连

接采用的字符集

character_set_results SELECT查询返回数据的字符集

character_set_database数据库采用的字符集

也就是说,只要保证这几个变量采用一致的字符集,就不会出现乱码问题了。

查看系统的字符集用下面的命令:

1 mysql SHOW VARIABLES LIKE ‘character_set_%‘;
2 +--------------------------+-----------------------------------------+
3 Variable_name Value
4 +--------------------------+-----------------------------------------+
5 character_set_client utf8
6 character_set_connection utf8
7 character_set_database utf8
8 character_set_filesystem binary
9 character_set_results utf8
10 character_set_server utf8
11 character_set_system utf8
12 character_sets_dir E:“usr“MySQL Server 5.0“share“charsets“
13 +--------------------------+-----------------------------------------+
14 8 rows in set
15


可以看到,我的这几个变量都是一致的。但假如不一致呢?网上许多教程告诉你“你set names下就解决了”。

那么set names是什么呢? set names实际上就是同时设置了 character_set_client ,character_set_connection和 character_set_results 这三个系统变量。

例如在mysql命令行上输入 set names ‘gbk‘ 命令等同于:

SET character_set_client = gbk;
SET character_set_connection = gbk;
SET character_set_results = gbk;


很多情况下,这样设置了之后就能把乱码问题解决了。但是还是不能完全避免出现乱码的可能,为什么呢?

因为character_set_client ,character_set_connection 这两个变量仅用于保证与 character_set_database 编码的一致,而 character_set_results 则用于保证 SELECT 返回的结果与程序的编码一致。

例如,你的数据库用的是 utf8 的字符集,那么你就要保证 character_set_client ,character_set_connection 也是utf8的字符集。

而你的程序也许采用的并不是utf8 ,比如你的程序用的是gbk ,那么你若把 character_set_results 也设置为 utf8 的话就会出现乱码问题。此时你应该把 character_set_results 设置为gbk。这样就能保证数据库返回的结果与你的程序的编码一致。

到此应该就可以解决绝大多数我们碰到的乱码问题了,另外还必须强调的是,有时候乱码的出现有可能是以上几种原因混合造成的。

总而言之,我们应当尽量的保证数据库中的数据是正确的,就是客户端到服务器端或者服务器端到客户端转换的过程中不要产生乱码,那么问题处理起来就相对简单了。



四、总结

为便于大家记忆,总结为以下四点:

1、要保证发送的数据与数据库的字符集一致,即 character_set_client,character_set_connection 与character_set_database 一致。

2、要保证数据库中存储的数据与数据库编码一致,即数据的编码与character_set_database一致。

3、要保

证 SELECT 的返回与程序的编码一致,即 character_set_results 与程序编码一致。

4、要保证程序编码与浏览器编码一致,即程序编码与 一致。

1、MySQL服务器能够支持多种字符集。可以使用SHOW CHARACTER SET语句列出可用的字符集:

mysql show character set;
+----------+-----------------------------+---------------------+--------+
Charset Description Default collation Maxlen
+----------+-----------------------------+---------------------+--------+
big5 Big5 Traditional Chinese big5_chinese_ci 2
dec8 DEC West European dec8_swedish_ci 1
cp850 DOS West European cp850_general_ci1
hp8 HP West European hp8_english_ci1
koi8r KOI8-R Relcom Russian koi8r_general_ci1
latin1 cp1252 West European latin1_swedish_ci 1
latin2 ISO 8859-2 Central European latin2_general_ci 1
swe7 7bit Swedish swe7_swedish_ci 1
ascii US ASCII ascii_general_ci1
ujis EUC-JP Japanese ujis_japanese_ci3
sjis Shift-JIS Japanese sjis_japanese_ci2
hebrew ISO 8859-8 Hebrew hebrew_general_ci 1
tis620 TIS620 Thai tis620_thai_ci1
euckr EUC-KR Korean euckr_korean_ci 2
koi8u KOI8-U Ukrainian koi8u_general_ci1
gb2312 GB2312 Simplified Chinese gb2312_chinese_ci 2
greek ISO 8859-7 Greek greek_general_ci1
cp1250 Windows Central European cp1250_general_ci 1
gbk GBK Simplified Chinese gbk_chinese_ci2
latin5 ISO 8859-9 Turkish latin5_turkish_ci 1
armscii8 armSCII-8 armenian armscii8_general_ci 1
utf8 UTF-8 Unicode utf8_general_ci 3
ucs2 UCS-2 Unicode ucs2_general_ci 2
cp866 DOS Russian cp866_general_ci1
keybcs2 DOS Kamenicky Czech-Slovak keybcs2_general_ci1
macce Mac Central European macce_general_ci1
macroman Mac West European macroman_general_ci 1
cp852 DOS Central European cp852_general_ci1
latin7 ISO 8859-13 Baltic latin7_general_ci 1
cp1251 Windows Cyrillic cp1251_general_ci 1
cp1256 Windows Arabic cp1256_general_ci 1
cp1257 Windows Baltic cp1257_general_ci 1
binary Binary pseudo charset binary1
geostd8 GEOSTD8 Georgian geostd8_general_ci1
cp932 SJIS for Windows Japanese cp932_japanese_ci 2
eucjpms UJIS for Windows Japanese eucjpms_japanese_ci 3
+----------+-----------------------------+---------------------+--------+
36 rows in set


相关文档
最新文档