数据库学习7
来源:互联网 发布:js done方法 编辑:程序博客网 时间:2024/05/29 05:07
- character_set_client 是指客户端发送过来的语句的编码;
- character_set_connection 是指mysqld收到客户端的语句后,要转换到的编码;
mysql> show variables like 'character%';+--------------------------+-------------------------------------+| Variable_name | Value |+--------------------------+-------------------------------------+| character_set_client | utf8 || character_set_connection | utf8 || character_set_database | utf8 || character_set_filesystem | binary || character_set_results | utf8 || character_set_server | utf8 || character_set_system | utf8 |
server端的3个编码设置都是utf8。
另外,客户端是标准 mysql client,使用的编码是utf8,和sever端编码是一致的。建一张表作为测试
CREATE TABLE t1(id INT, name VARCHAR(200) CHARSET utf8) engine=InnoDB;INSERT INTO t1 VALUES(0, '你好');mysql> SELECT id, name, hex(name) FROM t1;+------+--------+--------------+| id | name | hex(name) |+------+--------+--------------+| 0 | 你好 | E4BDA0E5A5BD |+------+--------+--------------+
下面我们分别改变这3个值,来看下结果会有什么变化
Case 1 只改变 character_set_client
SET character_set_client=gbk;INSERT INTO t1 VALUES(1, '你好');mysql> SELECT id, name, hex(name) FROM t1;+------+-----------+--------------------+| id | name | hex(name) |+------+-----------+--------------------+| 0 | 你好 | E4BDA0E5A5BD || 1 | 浣犲ソ | E6B5A3E78AB2E382BD |+------+-----------+--------------------+2 rows in set (0.00 sec)
可以看到返回的数据已经乱码了,并且数据库里存的确实和第一条记录不一样。
case 2 只改变 character_set_connection
SET names utf8;SET character_set_connection = gbk;INSERT INTO t1 VALUES(2, '你好');mysql> SELECT id, name, hex(name) FROM t1;+------+-----------+--------------------+| id | name | hex(name) |+------+-----------+--------------------+| 0 | 你好 | E4BDA0E5A5BD || 1 | 浣犲ソ | E6B5A3E78AB2E382BD || 2 | 你好 | E4BDA0E5A5BD |+------+-----------+--------------------+3 rows in set (0.00 sec)
case 3 只改变 character_set_results
SET names utf8;SET character_set_results = gbk;INSERT INTO t1 VALUES(3, '你好');mysql> select id, name, hex(name) from t1;+------+--------+--------------------+| id | name | hex(name) |+------+--------+--------------------+| 0 | | E4BDA0E5A5BD || 1 | 你好 | E6B5A3E78AB2E382BD || 2 | | E4BDA0E5A5BD || 3 | | E4BDA0E5A5BD |+------+--------+--------------------+4 rows in set (0.00 sec)
再改回原样,看下结果
SET names utf8;mysql> SELECT id, name, hex(name) FROM t1;+------+-----------+--------------------+| id | name | hex(name) |+------+-----------+--------------------+| 0 | 你好 | E4BDA0E5A5BD || 1 | 浣犲ソ | E6B5A3E78AB2E382BD || 2 | 你好 | E4BDA0E5A5BD || 3 | 你好 | E4BDA0E5A5BD |+------+-----------+--------------------+4 rows in set (0.00 sec)
分析
我们先理下字符集在整个过程中是怎样变化的,然后再分析上面的case
客户发送请求时:
A1 客户端发送出语句(总是以utf8)------> A2 sever收到语句解析(按character_set_client指定编码) | vA4 数据进入mysqld内部存储<--------- A3 sever判断是否需要转换编码(以character_set_connection 目标编码)
server返回结果时:
B1 server返回结果(按character_set_results 指定编码) ----->B2客户端解析编码显示(总是以utf8)
A3步是否需要转换编码,代码中的逻辑是这样的,在sql_yacc.yy文件中:
LEX_STRING tmp; THD *thd= YYTHD; const CHARSET_INFO *cs_con= thd->variables.collation_connection; const CHARSET_INFO *cs_cli= thd->variables.character_set_client; uint repertoire= thd->lex->text_string_is_7bit && my_charset_is_ascii_based(cs_cli) ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; if (thd->charset_is_collation_connection || (repertoire == MY_REPERTOIRE_ASCII && my_charset_is_ascii_based(cs_con))) tmp= $1; else { if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli)) MYSQL_YYABORT; } $$= new (thd->mem_root) Item_string(tmp.str, tmp.length, cs_con, DERIVATION_COERCIBLE, repertoire); if ($$ == NULL) MYSQL_YYABORT;
如果
character_set_client
和character_set_connection
一样,或者当前的字符编码是和ASCII兼容,并且都是ASCII范围内的,就不转换,其它情况就转。对于case1
实际上客户端发过来是UTF8的,但A2步骤server认为客户端的编码是GBK的,就按GBK来解析,同时满足A3步骤的转换条件,所以就误将UTF8编码认为是GBK,然后又给转成了UTF8。你好
的UTF8编码是E4BDA0E5A5BD
6个字节,每个字符3个字节,按GBK来解析的话,因为GBK是固定2个字节,就认为有3个字符,然后转成UTF8,虽然UTF8是变长的,但是这里的3个GBK字符按值都是要占3个字节的,转出来一共9个字节。所以case1看到的实际存储的值一共9个字节,比原来的大。
在返回时,是按UTF8返回的,因为存了3个UTF8字符,所以客户端看到的就是3个。对于case2
A2步骤没问题,问题是出在A3,按照转换逻辑,此时需要把UTF8转成GBK,这里因为character_set_client
是正确的,所以转换的源不会识别错,转换成GBK自然也不会错,后面存储成UTF8时,再从GBK转成UTF8,也没错,因为UTF8和GBK字符集里都包含 ‘你’和’好’,所以相互转换也不会出错,只是多了2次转换。对于case3
错在返回字符集设置的和客户端不匹配,在返回时,server将所有字符转成GBK的,结果客户端一根筋的认为是UTF8,就解析错了。
比较有意思的是第二条记录,即case1错误插进去的,显示出来是对的。
为什么呢,因为在case1中存的时候,是按UTF8->强制解析为GBK->然后转为UTF8
这个逻辑存下去的,而返回的时候,因为server会将存的UTF8又给转回GBK,然后客户端又拿着这个GBK误以为是UTF8解析,实际上是case1的逆向过程,虽然2个方向都是错的,最终显示是好的,所谓的负负得正吧,哈哈。对于case2 ,数据从客户端进入server的时候,多做了2次转换,最终显示还是对的,但不是所有场景都是这样,如下面这种
set names utf8;set character_set_connection = latin1;INSERT INTO t1 VALUES(4, '你好');set names utf8;mysql> SELECT id, name, hex(name) FROM t1;+------+-----------+--------------------+| id | name | hex(name) |+------+-----------+--------------------+| 0 | 你好 | E4BDA0E5A5BD || 1 | 浣犲ソ | E6B5A3E78AB2E382BD || 2 | 你好 | E4BDA0E5A5BD || 3 | 你好 | E4BDA0E5A5BD || 4 | ?? | 3F3F |+------+-----------+--------------------+5 rows in set (0.00 sec)
为什么呢,因为在 UTF8转latin1时,信息丢失了,latin1字符编码所能表达的字符集是远小于utf8的,
而 character_set_results 是指server执行语句后,返回给客户端的数据的编码。你
和好
就不在其中,这2个字符在转换中被转成了?
和?
,之后存储转换成UTF8时,?
只有一个字节3F
,还原回去还是3F
。
- JAVA学习数据库7
- 数据库学习7
- 数据库学习---7
- 数据库学习7- 子查询
- 学习数据库
- 学习数据库
- 数据库学习
- 数据库学习
- 数据库学习
- 学习数据库
- 数据库学习
- 数据库学习
- 数据库学习
- 数据库学习
- 数据库学习
- 数据库学习
- 数据库学习
- 数据库学习
- 常用的操作符
- 136. Single Number
- SpringMVC(七)国际化
- 并发insert情况下会发生重复的数据插入问题
- MySQL索引
- 数据库学习7
- package is corrupt, exception while verifying: undefined method `size' for nil:NilClass (NoMethodErr
- 玛丽奥吃金币
- iOS UITextFiled 用中文键盘输英文出现空格的解决方案
- openstack【Kilo】入门 【keystone篇】九: 创建openstack客户端环境变量脚本
- 设置Redis最大占用内存
- 不用数组,解决众数问题(前提 :众数出现的次数必须大于n/2)
- Python 之大数据量的可视化----Matplotlib
- 1.1 网络编程与套接字实例