mysql中基字符集_Mysql中的字符集

本文介绍了字符集的基础,包括ASCII和Unicode,以及Unicode的实现方式UTF-8。接着,详细讲解了MySQL中的字符集参数,如`character_set_server`、`character_set_client`等,并通过实例展示了不同字符集参数设置对数据存储和通信的影响,强调了保持字符集一致性的重要性。
摘要由CSDN通过智能技术生成

1、字符集基础

在计算机的眼中只有0和1,但是在人类世界中却有上百种语言,每种语言又有成千上万的文字,那么如何在计算中表示人类世界中的这些文字呢?

在上个世纪60年代的时候,美国首先定义了一套规则,在这个规则中一共定义了128个字符对应的二进制编码,比如大写的字母A是65(01000001),空格是32(00100000)等等。通过这个规则,计算机也就知道了01000001表示字母A,这也就是所谓的ASCII。在计算机中使用一个字节来存储ASCII。

很显然,只有ASCII是不够的,128个字符是远远不能表示各种语言的,这个时候各个地区也就逐渐定义了自己的编码方式,比如在欧洲的一些国家就通过启用ASCII编码的最高位来拓展支持的字符,因为在ASCII中只有128个字符,一个字节有8位,那么在ASCII中的最高位肯定是0,通过启动这个闲置的最高位就把支持的字符扩展到256个了,已经完全能满足自己地区的需求。但是,随着互联网的发展,地理上隔开的世界被一根根的网线连接在一起了,如果各个国家地区都使用自己的编码方式,显然是有问题的。于是就有了unicode,个人感觉Unicode的作用就像他的名字一样unique code,几乎给世界上的每一个符号都做了定义,如果大家都使用这个定义去做,那就不存在相互看不懂编码的问题,比如:

815c42f342b633bccbe7f9bb3c00c450.png

573ef12c6751e525ccbbdefd61c3932f.png

到此为止,已经解决了编码的问题了,但是又有了新的问题。unicode只是定义了一个字符怎么去编码,没有说明怎么去存储啊。比如“龟”在unique的代码为9F9F(1001111110011111)他需要2个字节,有的符号对应的编码更大可能需要3个字节或者更多。如何区分呢?比如0001111110011111怎么知道到底是表示两个ACSII字符还是表示一个占用2个字节的Unicod字符呢?

上面的这个问题就需要交给UTF-8(或者其他)去解决了,一句话就是Unicode定义了字符的编码,UTF-8定义了这个编码的实现(存储)方式,在Unicode的编码中,最特别的一点就是他是变长的,变长的好处就是按需求占用空间,试想一下,用3个字节去存储一个单字符划算吗?在UTF-8的实现规则中只有2点:

对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

当然,这个规则不是我要了解的重点,只需要知道unicode定义了字符的编码,UTF-8是一种实现Unicode的方式就好。

2、Mysql中的字符集参数

看一下Mysql中和字符集相关的参数

mh01@3306>show variables like '%char%';

+--------------------------+---------------------------------------------------------------+

| 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 |

| character_sets_dir | /usr/local/mysql-5.7.16-linux-glibc2.5-x86_64/share/charsets/ |

+--------------------------+---------------------------------------------------------------+

8 rows in set (0.00 sec)

复制代码

2.1创建对象相关

根据上面的相关variable,先看看和创建对象相关的变量。

character_set_server,这个变量为创建DB时候的默认值,Mysql在创建对象的时候有一个阶梯状的规则。如果创建database的时候没有指定这个db的字符集,就使用character_set_server的值;如果在创建表的时候没有指定字符集就使用这个表所在db的字符集,如果创建列的时候没有指定字符集,就使用这个表的字符字符集。

2.2服务器和客户端通信相关

这个是主要的参数,这部分的参数主要有character_set_client、character_set_connection、character_set_results这几个

首先看一下character_set_client,这个定义了客户端传输的字符集,用来告诉Mysql Server,客户端使用character_set_client的值来传输数据。也就是说Mysql总是认为客户端传输的是用该参数对应的编码来写的,但是真实情况却不一定,如果这个参数设置错误,可能会导致乱码,详情可以看后续的测试部分。

character_set_connection,Mysql需要把字符集转换为character_set_connection来处理SQL语句

charcter_set_results,这个参数定义了用何种字符集返回给客户端。

下图是Mysql在处理客户端和服务器通信时候字符集的转换方式:

f7e8becd1011f7b1a1b1edecd5c649c5.png

转换规则:如果 character_set_client 和 character_set_connection 一样,或者当前的字符编码是和ASCII兼容,并且都是ASCII范围内的,就不转换,其它情况就转。

2.3其它

character_set_system是一个只读变量,说明元数据的编码方式

character_set_database表示了当前数据库的默认字符集,比如在使用use切换数据库的时候,该参数会随着当前数据库默认字符集的改变而改变。

3、修改他们会有什么影响

在一般实践中,经常会使用set names UTF8的方式去设置相关的字符集参数,这种方式一般是同时修改了三个变量分别是

SET character_set_client = UTF8;

SET character_set_results = UTF8;

SET character_set_connection = UTF8;

复制代码

为什么要同时这个这三个参数,如果这个三个参数不一致会发生什么情况。

先创建一个表,用UTF8的方式插入一些数据

CREATE TABLE `char_test` (

`id` int(11) DEFAULT NULL,

`name` varchar(20) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8

set names utf8;

mh01@3306>insert into char_test (1,'哈哈');

mh01@3306>select id,name,hex(name) from char_test;

+------+--------+--------------+

| id | name | hex(name) |

+------+--------+--------------+

| 1 | 哈哈 | E59388E59388 |

+------+--------+--------------+

1 row in set (0.00 sec)

复制代码

使用UTF8的终端(也就意味中客户端的字符集都是UTF8)插入了一条数据,发现目前显示都正确。

3.1修改character_set_client的值

mh01@3306>set character_set_client=gbk;

Query OK, 0 rows affected (0.00 sec)

mh01@3306>insert into char_test values (2,'哈哈');

Query OK, 1 row affected (0.01 sec)

mh01@3306>select id,name,hex(name) from char_test;

+------+-----------+--------------------+

| id | name | hex(name) |

+------+-----------+--------------------+

| 1 | 哈哈 | E59388E59388 |

| 2 | 鍝堝搱 | E98D9DE5A09DE690B1 |

+------+-----------+--------------------+

2 rows in set (0.00 sec)

复制代码

从测试中可以看出,现在已经出现乱码了。为什么?

客户端使用的是UTF8,那么在发送“哈哈”的时候就会使用“E59388E59388”的编码去发送,这里的“哈哈”占用了6个字节。

当Mysql Server接收到这个字符的时候,根据character_set_client的值,Mysql认为客户端是用GBK做编码的,这里就出现问题了因为实际上客户端使用的是UTF8的编码。根据上面提到的转换规则(character_set_client的值和character_set_connection的值不一样)就会发生字符集的转换。因为Mysql认为这个编码是GBK的编码,所以就认为“E59388E59388”是三个字符(对应的汉子就是“鍝堝搱”),然后把“鍝堝搱”三个字符转换为UTF8,所以最终会发现id=2的值居然多了3字节。因为Mysql从一开始就是把UTF8的编码当成了GBK的编码去理解,理解错了自然最终的存储也就错了。

3.2修改character_set_connection

mh01@3306>set names utf8;

Query OK, 0 rows affected (0.00 sec)

mh01@3306>set character_set_connection=gbk;

Query OK, 0 rows affected (0.00 sec)

mh01@3306>insert into char_test values (3,'哈哈');

Query OK, 1 row affected (0.01 sec)

mh01@3306>select id,name,hex(name) from char_test;

+------+-----------+--------------------+

| id | name | hex(name) |

+------+-----------+--------------------+

| 1 | 哈哈 | E59388E59388 |

| 2 | 鍝堝搱 | E98D9DE5A09DE690B1 |

| 3 | 哈哈 | E59388E59388 |

+------+-----------+--------------------+

3 rows in set (0.00 sec)

复制代码

正常显示,为什么?客户端的编码是UTF8,Mysql Server收到后根据character_set_client的设置认为是UTF8编码(答对了),但是因为character_set_connection的编码是GBK,需要进行转换。和第一个例子不一样的地方是,在这里Mysql的理解是正确的(上一个例子中Mysql是误把“哈哈”理解为“鍝堝搱”),所以Mysql会正确的把UTF8编码转换为GBK编码。最后,由于表的编码是UTF8,所以Mysql又把GBK转换为UTF8编码存储起来,这个过程无非就是做了一些无谓的转换,但是结果是正常的。

3.3只修改character_set_results

mh01@3306>set character_set_results=gbk;

Query OK, 0 rows affected (0.00 sec)

mh01@3306>insert into char_test values (4,'哈哈');

Query OK, 1 row affected (0.01 sec)

mh01@3306>select id,name,hex(name) from char_test;

+------+--------+--------------------+

| id | name | hex(name) |

+------+--------+--------------------+

| 1 | | E59388E59388 |

| 2 | 哈哈 | E98D9DE5A09DE690B1 |

| 3 | | E59388E59388 |

| 4 | | E59388E59388 |

+------+--------+--------------------+

4 rows in set (0.00 sec)

复制代码

因为character_set_connection、character_set_client、客户端、表都是使用UTF8编码,所以在存储的时候存储的是正确的编码,“哈哈”=E59388E59388。因为character_set_results是GBK,所以做了一次转换,注意这次转换也是正常的转换,问题出在客户端这里,Mysql使用GBK编码把结果返回给客户端,但是客户端的使用的UTF8的,客户端没那么聪明,他会认为自己收到的都是UTF8的编码,但是实际上他收到的编码是GBK的编码,所以这回是客户端理解错误了,也就是显示出了错误的结果。

有意思的是,为什么id=2的显示正常?因为在id=2的插入过程中,使用了UTF8-->错误理解为GBK编码--->转换为UTF8的方式,而返回的时候,因为server会将存的UTF8又给转回GBK,然后客户端又拿着这个GBK误以为是UTF8解析,实际上就是一个逆向过程,类似负负得正~

4 参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值