字符集详解
MySQL 字符编码集中有两套 UTF-8 编码实现:utf8
和 utf8mb4
。
如果使用 utf8
的话,存储 emoji 符号和一些比较复杂的汉字、繁体字就会出错。
为什么会这样呢?这篇文章可以从源头给你解答。
字符集是什么?
什么是字符集?
1. 定义
字符集(Character Set)是一系列字符的集合,包括各种文字、符号、数字、表情等。例如,英文字母、汉字、数字和标点符号都属于字符。字符集定义了这些字符和它们在计算机中的表示范围。
2. 字符集与计算机存储的关系
计算机只能存储和处理二进制数据(0 和 1),字符集通过字符编码的方式,将字符映射为计算机可以存储和处理的二进制数据。字符集中的每个字符都有一个唯一的编码,用来表示它对应的二进制数据。
- 字符编码:
- 将字符转换为二进制数据。
- 例如,字符
a
对应二进制01100001
。
- 字符解码:
- 将二进制数据解析为对应的字符。
- 例如,二进制
01100001
解码为字符a
。
常见字符集
1. ASCII(美国信息交换标准代码)
- 范围:只支持基本的英文字符和一些控制符,总共 128 个字符。
- 特点:
- 每个字符占用 1 个字节(7 位编码)。
- 只能表示英文字母、数字、标点符号及少量特殊字符。
- 局限性:
- 无法表示非英文字符,如汉字、日文、阿拉伯文等。
2. ISO-8859 系列
- 范围:支持西欧语言字符,例如法语、西班牙语等。
- 特点:
- 每个字符占用 1 个字节。
- 扩展了 ASCII 的范围,但仍不支持汉字等亚洲语言。
3. GB2312 和 GBK
- 范围:支持中文字符。
- 特点:
- GB2312 是中国国家标准字符集,包含常用汉字。
- GBK 是 GB2312 的扩展,支持更多的汉字和符号。
4. Unicode
- 范围:涵盖全球主要文字和符号,是一种通用字符集。
- 特点:
- 每个字符都有唯一的编码,通常用 2 字节或更多表示一个字符。
- 不同编码实现:
- UTF-8:可变长度编码,每个字符占用 1 到 4 个字节。
- UTF-16:固定或变长编码,每个字符占用 2 或 4 个字节。
- UTF-32:每个字符固定占用 4 个字节。
5. 其他字符集
- Big5:用于繁体中文。
- Shift_JIS:用于日文字符。
字符集与编码的关系
- 字符集是字符的集合。
- 字符编码是将字符集中的每个字符映射到二进制的规则。
举例说明
- 字符集:Unicode 定义了字符
a
的编码是U+0061
。 - 编码方式:
- 在 UTF-8 中,
a
表示为01100001
(1 个字节)。 - 在 UTF-16 中,
a
表示为00000000 01100001
(2 个字节)。
- 在 UTF-8 中,
字符集的重要性
多语言支持:
- 不同语言使用不同字符集,统一的字符集(如 Unicode)可以表示全球范围的字符。
数据存储与传输:
- 确保字符数据在存储和传输过程中不会丢失或错误解析。
兼容性:
- 使用标准化字符集(如 UTF-8),可以确保跨平台、跨系统的字符数据兼容性。
小结
- 字符集:定义了字符和它们的集合范围。
- 字符编码:规定字符如何以二进制数据存储和传输。
- 重要性:字符集和编码确保了文本数据的正确表示和解析,广泛应用于现代计算机和互联网系统中。
字符编码是什么?
什么是字符编码?
1. 定义
字符编码是一种规则,用于将字符集中的字符与计算机中的二进制数据进行映射和转换。通过字符编码,可以将人类语言文字和符号表示为计算机能够识别、存储和传输的二进制数据,反之亦然。
2. 为什么需要字符编码?
计算机本质上只能处理数字(0 和 1),而人类使用的是字符(如字母、数字、汉字等)。字符编码的作用是建立字符与二进制之间的对应关系,从而实现字符信息的数字化存储和传输。
字符编码的原理
字符集与编码的关系:
- 字符集是所有字符的集合。
- 字符编码是将字符集中的字符映射为二进制数据的规则。
映射过程:
- 编码:将字符转换为二进制数据。例如,字符
a
编码为二进制01100001
。 - 解码:将二进制数据解析回字符。例如,二进制
01100001
解码为字符a
。
- 编码:将字符转换为二进制数据。例如,字符
常见字符编码
1. ASCII 编码
- 范围:只包含基本的英文字符、数字、标点符号和控制字符,共 128 个字符。
- 特点:
- 每个字符用 7 位二进制表示,通常补足为 1 个字节。
- 不能表示非英文字符,如汉字。
2. GB2312、GBK、GB18030
- GB2312:
- 中国国家标准,支持 6763 个汉字和 682 个其他字符。
- 每个字符占用 2 个字节。
- GBK:
- GB2312 的扩展,支持更多汉字,兼容 GB2312。
- GB18030:
- 包含所有 Unicode 字符,全面支持中国文字及少数民族语言。
3. Unicode
- 范围:覆盖全球所有主要语言字符。
- 特点:
- 每个字符分配一个唯一的编码点(如
U+0041
表示字母A
)。 - 不同编码形式:
- UTF-8:可变长度编码,每个字符占用 1-4 字节。
- UTF-16:每个字符占用 2 或 4 字节。
- UTF-32:每个字符固定占用 4 字节。
- 每个字符分配一个唯一的编码点(如
4. Big5
- 范围:主要用于繁体中文。
- 特点:
- 每个字符占用 2 个字节。
- 主要用于港台地区,支持常用的繁体汉字。
字符编码的重要性
跨平台和跨语言兼容性:
- 不同系统和应用之间可以通过统一的编码(如 UTF-8)实现字符的无损传输。
多语言支持:
- 现代应用需要支持全球范围的多种语言字符,Unicode 和 UTF-8 是广泛应用的标准。
数据存储和处理:
- 字符编码确保字符数据在存储、检索和处理时能够准确表示和还原。
小结
- 字符编码是字符与二进制数据之间的映射规则,用于计算机存储和处理文本。
- 常见的字符编码包括 ASCII、GB2312、GBK、GB18030、UTF-8、UTF-16 等。
- 选择字符编码时需考虑兼容性、存储效率和应用场景,如互联网应用多使用 UTF-8 编码。
有哪些常见的字符集?
常见字符集及其特点
1. ASCII(American Standard Code for Information Interchange)
- 特点:
- 用于表示基本的英文字母、数字、标点符号和控制字符。
- 原始 ASCII 只使用 7 位编码,共 128 个字符。
- 扩展 ASCII 使用 8 位编码,共 256 个字符。
- 局限性:
- 仅支持现代美国英语字符,无法表示其他语言字符。
- 应用:
- 是许多字符集的基础,例如 Unicode 和 UTF-8 对英语字符的编码兼容 ASCII。
2. GB2312
- 特点:
- 中国国家标准字符集,专为中文设计。
- 收录了 6700 多个汉字和 682 个其他字符。
- 英文字母和数字与 ASCII 兼容,汉字采用 2 字节编码。
- 局限性:
- 不支持生僻字和繁体字。
- 应用:
- 早期中文操作系统和应用程序中使用。
3. GBK
- 特点:
- GB2312 的扩展版本,支持 20000 多个汉字。
- 向下兼容 GB2312,扩展了更多汉字和符号。
- 局限性:
- 不完全支持 Unicode 的所有字符。
- 应用:
- 是 Windows 系统早期中文环境的主要字符集。
4. GB18030
- 特点:
- 完全兼容 GB2312 和 GBK,同时纳入中国少数民族语言字符。
- 收录 70000 多个汉字,包括繁体字和日韩汉字。
- 支持 1 字节、2 字节和 4 字节的编码方式。
- 局限性:
- 数据量大,存储效率低于 UTF-8。
- 应用:
- 中国国家标准字符集,强制支持国内所有中文信息处理系统。
5. BIG5
- 特点:
- 专为繁体中文设计,收录 13000 多个汉字。
- 不支持简体中文,兼容性不如 GBK 或 GB18030。
- 局限性:
- 仅适用于繁体中文环境。
- 应用:
- 台湾和香港地区的计算机系统中常见。
6. Unicode
- 特点:
- 全球通用字符集,涵盖几乎所有语言字符和符号。
- 每个字符都有唯一的编码点(如
U+0041
表示字母A
)。 - 与具体存储方式无关,需结合编码形式(如 UTF-8、UTF-16)。
- 局限性:
- 原始的 Unicode 编码(UCS-2)因定长(2 字节)导致存储效率低。
- 应用:
- 是现代计算机和互联网的核心字符集。
7. UTF-8
- 特点:
- Unicode 的一种变长编码形式,每个字符占用 1-4 个字节。
- 英文字母只需 1 个字节,中文通常需 3 个字节。
- 完全兼容 ASCII,适合多语言混合场景。
- 优势:
- 存储效率高,适合互联网和跨平台应用。
- 应用:
- 被广泛用于网页、数据库和现代应用程序中。
8. UTF-16 和 UTF-32
- UTF-16:
- 使用 2 或 4 字节编码字符,适合字符较多的语言。
- 不如 UTF-8 高效,主要用于特定系统(如 Windows)。
- UTF-32:
- 每个字符固定占用 4 字节,规则简单但空间浪费严重。
字符集的区别与选择
字符集 | 编码方式 | 字符支持范围 | 应用场景 |
---|---|---|---|
ASCII | 7 位或 8 位 | 基本英文字母、数字和符号 | 早期计算机系统,现代兼容性处理 |
GB2312 | 1-2 字节 | 简体中文常用汉字 | 早期中文环境 |
GBK | 1-2 字节 | 扩展的简体中文汉字 | Windows 早期中文环境 |
GB18030 | 1-4 字节 | 全面的中文汉字和符号 | 中国国内强制支持 |
BIG5 | 2 字节 | 繁体中文 | 台湾和香港地区 |
Unicode | 依存编码形式 | 全球所有语言字符 | 通用字符集标准 |
UTF-8 | 1-4 字节 | 全球语言字符 | 互联网、网页和多语言环境 |
UTF-16 | 2 或 4 字节 | 全球语言字符 | Windows 系统和特定应用 |
UTF-32 | 4 字节 | 全球语言字符 | 极少用,适合简单字符映射需求 |
总结
- 字符集定义了字符的范围和集合,不同字符集适配不同语言需求。
- 常用的字符集如 Unicode 和 UTF-8 提供了全球化支持,解决了字符编码不一致引发的乱码问题。
- 在现代应用中,UTF-8 是最主流、最推荐的编码方式,兼容性强,存储效率高。
MySQL 字符集
MySQL 支持很多种字符集的方式,比如 GB2312、GBK、BIG5、多种 Unicode 字符集(UTF-8 编码、UTF-16 编码、UCS-2 编码、UTF-32 编码等等)。
查看 MySQL 支持的字符集
使用 SHOW CHARSET
命令
MySQL 提供 SHOW CHARSET
命令来查看当前数据库支持的字符集。可以通过指定 LIKE
或 WHERE
子句筛选特定字符集。
示例操作
查看所有支持的字符集
SHOW CHARSET;
按名称筛选字符集
- 使用
LIKE
子句筛选:SHOW CHARSET LIKE 'utf%';
- 使用
WHERE
子句筛选:SHOW CHARSET WHERE Charset = 'utf8mb4';
- 使用
查询特定字符集的详细信息
- 查看支持的校对规则(Collation):
SHOW COLLATION WHERE Charset = 'utf8mb4';
- 查看支持的校对规则(Collation):
示例输出
Charset | Description | Default collation | Maxlen |
---|---|---|---|
utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
utf8mb4 | UTF-8 Unicode (4-byte) | utf8mb4_general_ci | 4 |
latin1 | cp1252 West European | latin1_swedish_ci | 1 |
gbk | Chinese Internal Code Specification | gbk_chinese_ci | 2 |
- Charset:字符集名称。
- Description:字符集描述。
- Default collation:默认的校对规则。
- Maxlen:单字符最大字节数。
通过 SHOW CHARSET
,您可以快速了解 MySQL 支持的所有字符集,并筛选需要的信息。
默认字符集
在 MySQL5.7 中,默认字符集是 latin1
;在 MySQL8.0 中,默认字符集是 utf8mb4
字符集的层次级别
MySQL 中的字符集有以下的层次级别:
server
(MySQL 实例级别)database
(库级别)table
(表级别)column
(字段级别)
它们的优先级可以简单的认为是从上往下依次增大,也即 column
的优先级会大于 table
等其余层次的。如指定 MySQL 实例级别字符集是utf8mb4
,指定某个表字符集是latin1
,那么这个表的所有字段如果不指定的话,编码就是latin1
。
server
不同版本的 MySQL 其 server
级别的字符集默认值不同,在 MySQL5.7 中,其默认值是 latin1
;在 MySQL8.0 中,其默认值是 utf8mb4
。
当然也可以通过在启动 mysqld
时指定 --character-set-server
来设置 server
级别的字符集。
mysqld
mysqld --character-set-server=utf8mb4
mysqld --character-set-server=utf8mb4 \
--collation-server=utf8mb4_0900_ai_ci
或者如果你是通过源码构建的方式启动的 MySQL,你可以在 cmake
命令中指定选项:
cmake . -DDEFAULT_CHARSET=latin1
或者
cmake . -DDEFAULT_CHARSET=latin1 \
-DDEFAULT_COLLATION=latin1_german1_ci
此外,你也可以在运行时改变 character_set_server
的值,从而达到修改 server
级别的字符集的目的。
server
级别的字符集是 MySQL 服务器的全局设置,它不仅会作为创建或修改数据库时的默认字符集(如果没有指定其他字符集),还会影响到客户端和服务器之间的连接字符集,具体可以查看 MySQL Connector/J 8.0 - 6.7 Using Character Sets and Unicode。
database
database
级别的字符集是我们在创建数据库和修改数据库时指定的:
CREATE DATABASE db_name
[[DEFAULT] CHARACTER SET charset_name]
[[DEFAULT] COLLATE collation_name]
ALTER DATABASE db_name
[[DEFAULT] CHARACTER SET charset_name]
[[DEFAULT] COLLATE collation_name]
如前面所说,如果在执行上述语句时未指定字符集,那么 MySQL 将会使用 server
级别的字符集。
可以通过下面的方式查看某个数据库的字符集:
USE db_name;
SELECT @@character_set_database, @@collation_database;
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'db_name';
table
table
级别的字符集是在创建表和修改表时指定的:
CREATE TABLE tbl_name (column_list)
[[DEFAULT] CHARACTER SET charset_name]
[COLLATE collation_name]]
ALTER TABLE tbl_name
[[DEFAULT] CHARACTER SET charset_name]
[COLLATE collation_name]
如果在创建表和修改表时未指定字符集,那么将会使用 database
级别的字符集。
column
column
级别的字符集同样是在创建表和修改表时指定的,只不过它是定义在列中。下面是个例子:
CREATE TABLE t1
(
col1 VARCHAR(5)
CHARACTER SET latin1
COLLATE latin1_german1_ci
);
如果未指定列级别的字符集,那么将会使用表级别的字符集。
连接字符集
前面说到了字符集的层次级别,它们是和存储相关的。而连接字符集涉及的是和 MySQL 服务器的通信。
连接字符集与下面这几个变量息息相关:
character_set_client
:描述了客户端发送给服务器的 SQL 语句使用的是什么字符集。character_set_connection
:描述了服务器接收到 SQL 语句时使用什么字符集进行翻译。character_set_results
:描述了服务器返回给客户端的结果使用的是什么字符集。
它们的值可以通过下面的 SQL 语句查询:
SELECT * FROM performance_schema.session_variables
WHERE VARIABLE_NAME IN (
'character_set_client', 'character_set_connection',
'character_set_results', 'collation_connection'
) ORDER BY VARIABLE_NAME;
SHOW SESSION VARIABLES LIKE 'character\_set\_%';
如果要想修改前面提到的几个变量的值,有以下方式:
1、修改配置文件
[mysql]
# 只针对MySQL客户端程序
default-character-set=utf8mb4
2、使用 SQL 语句
set names utf8mb4
# 或者一个个进行修改
# SET character_set_client = utf8mb4;
# SET character_set_results = utf8mb4;
# SET collation_connection = utf8mb4;
JDBC 对连接字符集的影响
不知道你们有没有碰到过存储 emoji 表情正常,但是使用类似 Navicat 之类的软件的进行查询的时候,发现 emoji 表情变成了问号的情况。这个问题很有可能就是 JDBC 驱动引起的。
根据前面的内容,我们知道连接字符集也是会影响我们存储的数据的,而 JDBC 驱动会影响连接字符集。
mysql-connector-java
(JDBC 驱动)主要通过这几个属性影响连接字符集:
characterEncoding
characterSetResults
以 DataGrip 2023.1.2
来说,在它配置数据源的高级对话框中,可以看到 characterSetResults
的默认值是 utf8
,在使用 mysql-connector-java 8.0.25
时,连接字符集最后会被设置成 utf8mb3
。那么这种情况下 emoji 表情就会被显示为问号,并且当前版本驱动还不支持把 characterSetResults
设置为 utf8mb4
,不过换成 mysql-connector-java driver 8.0.29
却是允许的。
具体可以看一下 StackOverflow 的 DataGrip MySQL stores emojis correctly but displays them as?这个回答。
UTF-8 使用
通常情况下,我们建议使用 UTF-8 作为默认的字符编码方式。
不过,这里有一个小坑。
MySQL 字符编码集中有两套 UTF-8 编码实现:
utf8
:utf8
编码只支持1-3
个字节 。 在utf8
编码中,中文是占 3 个字节,其他数字、英文、符号占一个字节。但 emoji 符号占 4 个字节,一些较复杂的文字、繁体字也是 4 个字节。utf8mb4
:UTF-8 的完整实现,正版!最多支持使用 4 个字节表示字符,因此,可以用来存储 emoji 符号。
为什么有两套 UTF-8 编码实现呢? 原因如下:
因此,如果你需要存储emoji
类型的数据或者一些比较复杂的文字、繁体字到 MySQL 数据库的话,数据库的编码一定要指定为utf8mb4
而不是utf8
,要不然存储的时候就会报错了。
演示一下吧!(环境:MySQL 5.7+)
建表语句如下,我们指定数据库 CHARSET 为 utf8
。
CREATE TABLE `user` (
`id` varchar(66) CHARACTER SET utf8mb3 NOT NULL,
`name` varchar(33) CHARACTER SET utf8mb3 NOT NULL,
`phone` varchar(33) CHARACTER SET utf8mb3 DEFAULT NULL,
`password` varchar(100) CHARACTER SET utf8mb3 DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
当我们执行下面的 insert 语句插入数据到数据库时,果然报错!
INSERT INTO `user` (`id`, `name`, `phone`, `password`)
VALUES
('A00003', 'guide哥😘😘😘', '181631312312', '123456');
报错信息如下:
Incorrect string value: '\xF0\x9F\x98\x98\xF0\x9F...' for column 'name' at row 1
参考
- 字符集和字符编码(Charset & Encoding):https://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html
- 十分钟搞清字符集和字符编码:http://cenalulu.github.io/linux/character-encoding/
- Unicode-维基百科:https://zh.wikipedia.org/wiki/Unicode
- GB2312-维基百科:https://zh.wikipedia.org/wiki/GB_2312
- UTF-8-维基百科:https://zh.wikipedia.org/wiki/UTF-8
- GB18030-维基百科: https://zh.wikipedia.org/wiki/GB_18030
- MySQL8 文档:https://dev.mysql.com/doc/refman/8.0/en/charset.html
- MySQL5.7 文档:https://dev.mysql.com/doc/refman/5.7/en/charset.html
- MySQL Connector/J 文档:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-charsets.html