MySQL 字符转化以及乱码原因
1、在终端 (Terminal,可以是 bash 窗口,也可以是客户端工具如 navicat) 中输入,输入的内容由 Terminal 根据其自己的字符进行编码。
2、经 Terminal 编码后的二进制流被传输到 mysql server。mysql server(mysql engine) 根据参数 character_set_client 的字符设置来对该二进制流进行解码。
3、解码之后,mysql server 再次根据目的表,即 table 的字符集来判断是否需要字符编码转换。如果 character_set_client 的字符设置和 table 定义时的 character 设置一致,则无需字符编码转换。否则进行转换,然后将转换后的二进制流存放到数据文件 (file) 中去。
总结:client ------> server(engine) -----> file 需要经过三次编码,两次编码转化。
MySQL 中取出数据时发生的编码转换过程:
1、从数据文件 (file) 中读出二进制数据流,将该数据流根据 table 定义时的 character 设置来进行解码。
2、在用 table character 对二级制数据流进行解码之后,在 mysql engine(mysql server) 中,需要根据参数 character_set_client 的字符集设置对解码后的数据库流再一次进行编码,将编码之后的二级制数据库流传输到 client 端。
3、client 端,即终端 (Terminal) 根据其自己的字符集编码来展示查询结果。
总结: file ------> server(engine) -----> client 需要经过三次编码,两次编码转化。
可能会有些疑问,在上面的分析中,数据都是以二进制流的方式在各个节点之间流动的。那么为什么需要编码转化了?
1、client 和 server(engine) 之间的转换,或者说编解码是为了对传进来的二进制流做语法和词法解析,否则你不会知道传进来的是 insert 还是 update。
2、file 和 server(engine) 之间的转换是为了在从数据文件读入数据后,在存储引擎内部进行字符级别的操作。
经过以上分析,应该很快发现导致乱码出现的原因是有以下几种:
1、数据在存入的时候和取出的时候,编码不一致。比如存入时用的 utf8,取出时用的 GBK。
2、编码转换不是无损编码转换导致乱码出现。比如 clien 是 utf8,mysql server 中的 character_set_client 设置为 gbk,表结构的字符集设置为 utf8。这里会有两次编码转化,client 到 server 时,utf8 要转为 gbk,然后 server 到 file 时,gbk 要转为 utf8。由于 gbk 到 utf8 是有损编码转化,导致了乱码出现。
无损编码转换:假设我们要把用编码 A 表示的字符 X,转化为编码 B 的表示形式,而编码 B 的字形集中并没有 X 这个字符,那么此时我们就称这个转换是有损的。
但不是任何两种字符集编码之间的转换都是有损,转换是否有损取决于以下几点:
------ 被转换的字符是否同时在两个字符集中
------ 标字符集是否能够对不支持字符,保留其原有表达形式。(比如 latin1 在遇到自己无法表示的字符时,会保留原字符集的编码数据,并跳过忽略该字符进而处理后面的数据。)
因此只要客户端,MySQL Server 的 character-set-client,table charset 的三个字符集完全一致就可以保证一定不会有乱码出现了。