Character Sets and Collations in MySQL

最近遇到一个关于MySQL字符乱码的问题,比较好解决,但是为了更好地理解MySQL中的字符系统,参考MySQL的文档整理一下关于MySQL中字符集合字符序的问题。

1. 什么是字符集和字符序

MySQL是存储数据的数据库,既然是存储数据的,那么数据就需要一个编码的规则,因为计算机对于所有的数据都是通过二进制编码的方式存储的。字符集(Character Sets),就是字符到二进制编码映射的集合。由于不同的语言所包含的字符不同,所以为了支持不同的语言,MySQL定义了许许多多不同的字符集。

举一个简单的例子,假如我们有一种语言只有四个字符:A, B, a, b

我们规定了这四个字符到二进制编码的规则:

Symbol Encoding
A 0
B 1
a 2
b 3

在这里,A就是一个字符,而数字0就是字符A的编码,这四个字符和编码组合在一起就是字符集。

接下来看看什么是字符序(Collations)。

对于上面的字符集,我们需要有一个比较的规则。比如,我们想比较AB,当然结果有三种:等于、小于或大于。但我们熟悉的还是A<B。一种简单的方式就是比较他们的编码数字。因为0<1,所以对应的字符A<B

这样就构成了一条字符比较的规则。这样的一条规则就可以构成一个字符序。

进一步,如果我们规定大写字母和对应的小写字母相等呢?这样就又多了一条规则:

a=A, b=B

这样,两条比较的规则就又构成了一个字符序。

通过这个简单的例子,我们可以知道:

  1. 字符集就是字符到二进制编码的映射集合;
  2. 字符序是比较字符集的规则集合;
  3. 一个字符集可能有多个字符序。

在MySQL中,定义了许多字符集合字符序。这些字符集合字符序可以帮助我们处理很多字符相关的问题。

2. MySQL中的字符集合字符序

在MySQL中,我们可以使用如下的语句来查看字符集:

SHOW CHARACTER SET;

这会列出所有的字符集。如果想筛选的话,可以加上LIKE条件:

SHOW CHARATER SET LIKE 'utf%';

结果如下:

+---------+------------------+--------------------+--------+
| Charset | Description      | Default collation  | Maxlen |
+---------+------------------+--------------------+--------+
| utf16   | UTF-16 Unicode   | utf16_general_ci   |      4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci |      4 |
| utf32   | UTF-32 Unicode   | utf32_general_ci   |      4 |
| utf8    | UTF-8 Unicode    | utf8_general_ci    |      3 |
| utf8mb4 | UTF-8 Unicode    | utf8mb4_0900_ai_ci |      4 |
+---------+------------------+--------------------+--------+

结果返回了字符集的名称、描述、默认字符序以及单个字符最大长度等信息。

每一个字符集都有一个默认的字符序,有的字符集有多个字符序,可以通过下面的语句查看一个字符集的字符序:

SHOW COLLATION WHERE Charset = 'utf8mb4';

返回的结果会列出所有的字符序以及相关的信息,这里就不展示了。

字符序有下面的特点:

  1. 两个不同的字符集不会有相同的字符序。也就是每个字符集都有自己单独的字符序小弟,大家不会有交集;
  2. 每一个字符集都有一个默认的字符序;
  3. 字符序的名字是以对应的字符集名称为前缀的。

3. 在MySQL中使用字符集

MySQL中有很多可以控制字符集的选项,这些选项过于复杂很容易混淆。不过记住一点:

只有基于字符的值才真正的“有”字符集的概念。

对于其他类型的值,字符集只是一个设置,用来指定用哪一种字符进行比较或操作。基于字符的值能存放在某列中、查询的字符中、表达式的计算结果中或者某个用户变量中,等等。

MySQL中字符集的设置可以分为两类:创建对象时的默认值、在服务器和客户端通信时的设置。

3.1 创建对象时的默认设置

在MySQL中,从上到下一共有四层字符集的设置,分别是服务器(Server)、数据库(Database)、表(Table)和列(Column),在每一层都可以指定一个默认的字符集:

其中,服务器和数据库的字符集设置有对应的参数:character_set_servercharacter_set_database,表和列的字符集设置在对应的DDL语句中。

由于真正存放数据的是列,所以更高层次的字符集设置仅仅是指定一个默认值,如果在创建列时没有知道字符集,就会从下到上寻找设置的字符集。如果指定了一个字符集,那么上面所有层次的设置都没有效果了。

在MySQL 5.5、5.6和5.7中,默认的字符集是latin1,在最新的MySQL 8中,默认的字符集是utf8。

3.2 服务器和客户端通信时的设置

当服务器和客户端进行通信时,可能各自使用不同的字符集。这时,服务器需要进行字符的转换工作。这涉及到MySQL中的三个参数:character_set_clientcharacter_set_connectioncharacter_set_results

这三个参数的影响效果如下图所示:

这里服务器进行了两次翻译过程:

  1. SQL语句从客户端离开时的字符集是character_set_client
  2. SQL语句进入服务器后服务器转换成character_set_connection
  3. 服务器处理完SQL语句后,将结果的字符集设置成了character_set_results

根据需要,可以使用SET NAMES或者SET CHARACTER SET语句来改变上面的设置:

SET NAMES 'utf8';

这样,会将character_set_clientcharacter_set_connectioncharacter_set_results都设置成utf8。

不过在服务器上使用这个命令只能改变服务器端的设置,客户端程序也需要设置正确的字符集才能避免出现问题。

3.3 小结

可以通过SHOW VARIABLES LIKE 'character%'命令来查看这些参数的值:

+--------------------------+-----------------------------------------------------------+
| Variable_name            | Value                                                     |
+--------------------------+-----------------------------------------------------------+
| character_set_client     | latin1                                                    |
| character_set_connection | latin1                                                    |
| character_set_database   | utf8mb4                                                   |
| character_set_filesystem | binary                                                    |
| character_set_results    | latin1                                                    |
| character_set_server     | utf8mb4                                                   |
| character_set_system     | utf8                                                      |
| character_sets_dir       | /usr/local/mysql-8.0.13-macos10.14-x86_64/share/charsets/ |
+--------------------------+-----------------------------------------------------------+

4. 字符集的选择

使用不同的字符集会带来更多的CPU操作,可能也会消耗更多的内存和磁盘空间。因此,为了方便,最好先为服务器(或者数据库)设置一个合理的字符集,然后根据不同的情况让某些列选择合适的字符集,

如果统一使用utf8字符集,整个世界都清净了。这也是很常见的一种做法。不过有的时候并不需要使用utf8,使用utf8之后会增加磁盘空间的消耗。


 Previous
File to Redis File to Redis
问题是这样的,现在需要把一个文件里的所有数据上传到redis的set中,文件里存储的就是ID,每行一个: head id.list 10000 10001 10002 10003 10004 10005 10006 10007 10008
2019-12-10
Next 
What is physics What is physics
这篇文章是阅读《费恩曼物理学讲义 第一卷 新千年版》第1-3章的阅读笔记。 1. 从一片沙滩开始我们生活在一个多姿多彩的世界中。 想象一下,去年的某一天,我们正在一个美丽的海边沙滩享受着没有工作烦恼的美妙时光。你躺在柔软细密的沙滩上,不
2019-12-07
  You Will See...