MySQL中混沌码问题的最终解决方案
mysql中混沌码的产生原因要明白为什么有随机码,我们首先要了解:从客户端请求,MySQL存储数据,然后下一次从表中检索客户端,连接将编码/解码的行为。为了更好地解释的过程中,作者做了两个流程图,这分别存放在了两阶段。
存储在MySQL中的编码转换过程
有3个编码/解码过程(红色箭头)在上面的图中,三个红色箭头对应:客户端代码,MySQL服务器解码和编码表的客户转换。终端可以是一个bash,网页或应用程序。在本文中,我们假设Bash是我们的终端,用户端的输入和显示界面。以下行为的每一帧图:
终端输入法输入
终端根据字符编码转换成二进制流。
二进制流通过MySQL客户机发送到MySQL服务器。
服务器通过字符集客户端解码
确定字符集的客户与目标表的字符集一致
一个字符从客户端字符集表字符集转换编码是否一致
转换后的字符编码二进制流存储在文件中。
MySQL表中数据经验的编码转换过程
上面的图有3个编码/解码过程(红色箭头),上面的三个红色箭头分别对应于客户端解码器。MySQL服务器的转换是基于字符集客户端编码和表编码的字符集客户端编码。
从文件中读出二进制数据流。
用表字符集编码解码
将数据转换为字符集客户机
使用字符集客户端编码作为二进制流
服务器通过网络传输到远程客户机。
客户机通过对配置的字符编码显示查询结果。
MySQL乱码的原因
当1时对应链接的编码不一致。存储并取出
这将是明显的造成混乱。我们总共使用的字符集在三编解码器在贮藏期为C1,C2,C3(从左到右图一),和三个字符集时取出分别编号为C1,C2,C3的(从左至右)当时,bash C1使用UTF-8编码。当我们拿出C1,我们使用Windows终端(默认是GBK编码),那么结果几乎是随机码。或SET NAMES UTF8(C2)当你把它放在,当你把它拿出来,你用SET NAMES GBK(C2),那么结果一定是混乱的代码。
三2步编码不一致性。单流程
在上述任何图同一方向三步,只要两个或两个以上的代码是不可能出现编码错误。如果没有无损编码转换的差异之间的两个字符集(如在下面详细描述),会有一个随机码。例如,我们的外壳是UTF8编码,mysql的客户端设置为GBK字符集,和表结构的字符集utf8,所以毫无疑问会有乱码。
下面是一个简单的演示:
{ } { }掌握本地msandbox(测试)>创建表charset_test_utf8(ID为主键auto_increment,50));
查询OK,0行受影响(0.04秒)
{ } { }掌握本地msandbox(测试)> SET NAMES GBK;
查询OK,0行受影响(0秒)
{ } { }掌握本地msandbox(测试)>插入charset_test_utf8(char_col)值(中国人的);
查询OK行,1受影响,1警告(0.01秒)
{ } { }掌握本地msandbox(测试)>显示警告;
+ + + + --------- ------ ---------------------------------------------------------------------------
级别代码信息| | | |
+ + + + --------- ------ ---------------------------------------------------------------------------
|警告| |不正确的字符串值: XAD XE6 X96 x87 '1366为column'char_col排1 |
+ + + + --------- ------ ---------------------------------------------------------------------------
1行集(0秒)
{ } { }掌握本地msandbox(测试)>选择ID,HEX(char_col),char_col从charset_test_utf8;
+ -- + + + ---------------- ----------
| | ID Hex(char_col)char_col | |
+ -- + + + ---------------- ----------
| 1 | e6b6933fe69e83 | |
+ -- + + + ---------------- ----------
1行集(0.01秒)
关于MySQL的编码/解码
由于系统是根据二进制流传输的,所以直接将二进制流字符串直接插入到表文件中是很好的。为什么要在存储之前执行两个编解码器操作呢
客户机到服务器编解码器的原因是MySQL需要对传入的二进制流进行语法和词法分析,如果我们不进行编码解析和检查,我们甚至不知道二进制流的字符串是插入还是更新。
文件到引擎的编解码器是要知道二进制流中的分词。我们举一个简单的例子:我们想从内部和外部取出字段的前两个字符,并执行一个句子,如从表中选择左(山口,2)。存储引擎从文件中读取的值的列e4b8ade69687。此时,如果我们的价值分为e4b8,ade69687三词根据GBK,然后返回到客户端的价值应该e4b8ade6。如果我们将UTF8到e4b8ad和e69687根据UTF8,那么我们应该回到e4b8ade69687两个字。可以看出,如果数据是从数据文件中读取,它是不可能执行的字符级别的操作在存储引擎没有编解码器。
错误输入和错误
MySQL中最常见的疾病的原因是错误的神话。所谓错误的输入错误,客户端的字符编码(Web或壳)不同于最后的表的字符编码格式,但它仍然可以得到输出没有随机码只要我们保证两字符集编码的一致性。但是,错误谬误是不合法的字符集编码两任意组合。我们假设客户端代码是C,和字符集的MySQL表编码是美国为了可以去错了,我们需要满足以下两个条件:
当MySQL接收到请求时,由C编码的二进制流在S解码时可以是无损的。
MySQL返回由C编码的二进制流在C解码时可以无损的数据。
编码的无损转换
那么什么是有损转换,什么是无损转换呢假设我们想将编码A所编码的字符x转换成编码B的形式,而编码B不包含x字符,那么我们称这种转换是有损的。那么,为什么两个代码可以代表一组字符呢如果你之前有看过博客的文章十分钟的字符集和字符编码,或理解的字符编码,你应该知道每个字符集支持的字符数是有限的,并且有不同的字符集覆盖文本之间的差异。字符UTF8和GBK可以代表数如下:
GBK单字符编码值的范围是8140 -铁,其中不包括××7E和字符总数约为27000
utf8单字符编码,按字节数,取值范围为:
可以用UTF-8编码来表示字符的数量远远多于GBK。然后我们可以很容易地从UTF8 GBK找到有损编码转换。我们使用字符映射(见下文)找到一个字符,显然不在GBK编码表,并试图把它变成一个GBK编码表,在有损转换的行为再看一次
字符信息是:结合字母:u + 0a05 Unicode,UTF-8:E0 A8 85
MySQL中存储的详细信息如下:
{ } { }掌握本地msandbox(测试)>创建表charset_test_gbk(ID为主键auto_increment,50));
查询OK,0行受影响(0秒)
Master {localhost} {msandbox} (test) > set names utf8;
查询OK,0行受影响(0秒)
{ } { }掌握本地msandbox(测试)>插入charset_test_gbk(char_col)值();
查询OK行,1受影响,1警告(0.01秒)
{ } { }掌握本地msandbox(测试)>显示警告;
+ + + + --------- ------ -----------------------------------------------------------------------
级别代码信息| | | |
+ + + + --------- ------ -----------------------------------------------------------------------
|警告| |不正确的字符串值: xe0 xa8 X85 '1366为column'char_col排1 |
+ + + + --------- ------ -----------------------------------------------------------------------
1行集(0秒)
{ } { }掌握本地msandbox(测试)>选择ID,HEX(char_col),char_col,char_length(char_col)从;
+ -- + + + + --------------- ---------- -----------------------
| | ID Hex(char_col)| | char_col char_length(char_col)|
+ -- + + + + --------------- ---------- -----------------------
| 1 | 3f | | 1 |
+ -- + + + + --------------- ---------- -----------------------
1行集(0秒)
错误的一部分发生在编解码器的第三个步骤中。
可以看出,如果内部MySQL无法找到对应的utf8字符一个GBK字符,它将被转换成一个错误标志(这是一个问号),而每个字符集是在该计划的实施,其行为和转换规则,当出现这种情况时是一致的。例如,如果不能找到相应的字符UTF8,如果你不把错误的字符,替换字符(U + fffd)
那么,两个字符集编码之间的转换是否有害呢并非如此,转换是否损坏取决于以下几点:
转换字符是否同时包含在两个字符集中。
目标字符集是否不能支持字符并保留其原始表达式
第一点是通过实验来解释。这是造成损害的转型第二因素解释。从前面的例子中,我们可以看到,当处理GBK字符你不能表达的行为:取代它的错误识别,即0x3f。一些字符集(如latin1)保留编码数据的原始字符集时遇到的,他们无法表达的特点,并跳过字符忽略数据。如果目标字符集有这样一个特点,它能够将错误,在这一部分中首先提到的实现。
让我们来看看下面的例子:
{ } { }掌握本地msandbox(测试)>创建表charset_test(ID为主键auto_increment,50));
查询OK,0行受影响(0.03秒)
{ } { }掌握本地msandbox(测试)>集名称latin1;
查询OK,0行受影响(0秒)
{ } { }掌握本地msandbox(测试)>插入charset_test(char_col)值(中国人的);
查询OK,1行受影响(0.01秒)
{ } { }掌握本地msandbox(测试)>选择ID,HEX(char_col),char_col从charset_test;
+ -- + + + --------------- ----------
| | ID Hex(char_col)char_col | |
+ -- + + + --------------- ----------
| 2 | e4b8ade69687 |中国|
+ -- + + + --------------- ----------
2行(0秒)
详细流程如下。可以看出,由MySQL服务器接收后,代码不一致,实际发生的,但由于设置本身以外的人物范围latin1字符不做任何处理,但保留了原有的价值,这样的行为也使得可能的误区。
如何避免随机代码
了解了以上内容,这是很容易避免的代码。只要我们实现三位一体,即客户端,MySQL字符集客户表的字符集,三字符集是完全一样的,这是肯定的,不会有乱码,已经搞砸了的数据,或已经损失了转码,它会有点难解决。在下一节中,我们将详细说明的具体方法。
如何修复已编码和损坏的数据
在介绍正确的方法之前,我们首先看看在互联网上流传的所谓正确方法的严重后果。
错误的方法1
从语法或字面意义上说:修改表…字符集= XXX无疑是最像乱码治愈!事实上,他对您已经损坏的数据没有什么帮助,即使是已经在表上创建的默认字符集也不能更改。
{ } { }掌握本地msandbox(测试)>显示创建表charset_test;
-------------- -------------------------------- + + +
表创建表| | |
-------------- -------------------------------- + + +
charset_test创建表(` charset_test ` | |
` ID ` int(11)不为空auto_increment,
` char_col ` varchar(50)默认为空,
主键(id)
InnoDB引擎= = 3 auto_increment默认的字符集| latin1)
-------------- -------------------------------- + + +
1行集(0秒)
{ } { }掌握本地msandbox(测试)>修改表charset_test = GBK字符集;
查询OK,0行受影响(0.03秒)
记录:0个重复:0个警告:0
{ } { }掌握本地msandbox(测试)>显示创建表charset_test;
-------------- -------------------------------- + + +
表创建表| | |
-------------- -------------------------------- + + +
charset_test创建表(` charset_test ` | |
` ID ` int(11)不为空auto_increment,
` char_col ` varchar(50)字符集latin1默认为空,
主键(id)
InnoDB引擎= = 3 auto_increment默认的字符集GBK |)
-------------- -------------------------------- + + +
1行集(0秒)
可以看出,该语法严格修改了表的默认字符集,也就是说,它只会影响后面创建的列的默认字符集,但不会改变现有的列和数据。
错误的方法
将表转换为字符集……阶段比方法更致命,因为正式文档的功能是对表的数据进行编码:
更改表默认字符集和所有字符列。
修改表tbl_name
转换字符集charset_name {整理collation_name };
事实上,这种语法只适用于当前,并且没有随机代码,也不是错误的方法保存的表。对于错误编码的表,它会导致更坏的结果。
让我们用一个实际的例子来解释什么是SQL确实和他会做什么。假设我们有一个表,编码latin1。输入错误数据之前,我们保存UTF-8数据,但它仍然可以被显示的终端,在错误的章错章的例子,使用一段时间后,我们发现这个错误并无意改变编码表UTF-8字符集,不影响原数据的正常显示。在这种情况下,使用ALTER TABLE转换为字符集都会有这样的后果:
{ } { }掌握本地msandbox(测试)>创建表charset_test_latin1(ID为主键auto_increment,50));
查询OK,0行受影响(0.01秒)
{ } { }掌握本地msandbox(测试)>集名称latin1;
查询OK,0行受影响(0秒)
{ } { }掌握本地msandbox(测试)>插入charset_test_latin1(char_col)值(这是中国);
查询OK,1行受影响(0.01秒)
{ } { }掌握本地msandbox(测试)>选择ID,HEX(char_col),char_col,char_length(char_col)从;
+ -- + + + + -------------------------- -------------- -----------------------
| | ID Hex(char_col)| | char_col char_length(char_col)|
+ -- + + + + -------------------------- -------------- -----------------------
这是1 | | e8bf99e698afe4b8ade69687 |中国| 12 |
+ -- + + + + -------------------------- -------------- -----------------------
1行集(0.01秒)
{ } { }掌握本地msandbox(测试)>修改表charset_test_latin1转换成utf8字符集;
查询OK,1行受影响(0.04秒)
记录:1个重复:0个警告:0
{ } { }掌握本地msandbox(测试)> SET NAMES UTF8;
查询OK,0行受影响(0秒)
{ } { }掌握本地msandbox(测试)>选择ID,HEX(char_col),char_col,char_length(char_col)从;
+ -- + + + + -------------------------------------------------------- ----------------------------- -----------------------
| | ID Hex(char_col)| | char_col char_length(char_col)|
+ -- + + + + -------------------------------------------------------- ----------------------------- -----------------------
| 1 | c3a8c2bfe284a2c3a6cb9cc2afc3a4c2b8c2adc3a6e28093e280a1 |德- | 12 |
+ -- + + + + -------------------------------------------------------- ----------------------------- -----------------------
1行集(0秒)
从这个例子中我们可以看到,在错误的数据表中有错误,这个命令不仅没有从混沌效应中引入秩序,数据将完全损坏,甚至二进制数据编码也发生了变化。
正确的方法是转储重新加载。
这种方法虽然笨拙,但操作和理解也比较好,简单的理论分为以下三个步骤:
通过错误的方法,文件被导出到文件中。
用正确的字符集修改新表
将以前导出的文件引导回新表
还是上面的例子,我们使用UTF-8的错误数据成latin1编码表。现在你需要修改表码UTF-8使用以下命令
壳>就你根P D跳过设置字符集,默认字符集= utf8测试charset_test_latin1 > data.sql
#确保导出的文件不是乱码在UTF-8编码的观点的一个文本编辑器
Shell> MySQL -uroot -p -e'create table charset_test_latin1 (id int primary key)
壳> MySQL中- P -默认字符集= utf8测试< data.sql
正确方法二转换为二进制转换回
比较棘手的这种方法,使用二进制数据作为中间数据的做法来实现的。因此,MySQL不做实际的数据转换时,编码有意义的数据流转换为二进制数据没有编码的意义。当二进制数据变为编码数据,目标代码将被用来进行编码转换检查。通过这两个特性相当于MySQL内部模拟了一个错误,垃圾会拨乱反正。
还是上面的例子,我们使用UTF-8的错误数据成latin1编码表。现在你需要修改表码UTF-8使用以下命令
MySQL >修改表charset_test_latin1修改列char_col varbinary(50);
MySQL >修改表charset_test_latin1修改列char_col varchar(50)utf8字符集;