乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

字符集与排序规则

导学

你刚创建的数据库,插入中文后查询出来是乱码?ORDER BY 排序结果和预期不一致?WHERE 条件匹配时大小写敏感行为飘忽不定?这些问题的根源都在于字符集(Character Set)和排序规则(Collation)。理解 utf8mb4 与 utf8mb3 的区别、_bin 与 _ci 后缀的含义,是避免中文乱码和排序陷阱的必修课。

定义

字符集(Character Set):定义了数据库可以存储哪些字符(如 ASCII、中文、emoji),以及每个字符对应的二进制编码。排序规则(Collation):定义了字符之间如何比较和排序的规则,包括是否区分大小写、是否区分重音符号等。

核心概念

MySQL 5.7 中的字符集层级

层级设置方式影响范围
服务器级my.cnf 中 character-set-server新建数据库/表的默认字符集
数据库级CREATE DATABASE CHARACTER SET该数据库内新建表的默认字符集
表级CREATE TABLE CHARACTER SET该表所有字符串列的默认字符集
列级CREATE TABLE (col VARCHAR(20) CHARACTER SET ...)仅该列
连接级SET NAMES客户端与服务器通信时的编码

utf8mb4 vs utf8(utf8mb3)

特性utf8mb3(旧 utf8)utf8mb4
最大字节/字符3 字节4 字节
支持字符范围BMP(基本多文种平面)全部 Unicode
emoji 支持❌ 不支持✅ 支持
生僻汉字❌ 部分不支持✅ 支持
MySQL 5.7 默认是(历史遗留)推荐

重要:MySQL 5.7 中 utf8 实际上是 utf8mb3 的别名,最多 3 字节,不支持 emoji 和某些生僻汉字。生产环境必须显式使用 utf8mb4。

排序规则后缀含义

后缀含义示例
_ciCase Insensitive,不区分大小写utf8mb4_general_ci
_csCase Sensitive,区分大小写utf8mb4_bin(二进制比较)
_binBinary,按字节码比较,区分大小写和重音utf8mb4_bin
_aiAccent Insensitive,不区分重音部分排序规则支持

SQL 示例

场景一:查看当前字符集设置

执行语句:

-- 查看服务器字符集
SHOW VARIABLES LIKE 'character_set%';

-- 查看当前连接的字符集
SHOW VARIABLES LIKE 'collation%';

操作后结果(MySQL 5.7 默认安装示例):

Variable_nameValue
character_set_clientutf8mb4
character_set_connectionutf8mb4
character_set_databaselatin1
character_set_resultsutf8mb4
character_set_serverlatin1
character_set_systemutf8

结果解读:

  • character_set_client/connection/results:客户端与服务器通信的三件套,通常应保持一致
  • character_set_database/server:数据库/服务器默认字符集,MySQL 5.7 旧版本默认可能是 latin1,不支持中文
  • 如果 character_set_database = latin1,创建数据库不指定字符集时,表默认用 latin1,插入中文会乱码

场景二:创建 utf8mb4 数据库和表

执行语句:

-- 创建数据库时显式指定字符集和排序规则
CREATE DATABASE IF NOT EXISTS company
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

USE company;

-- 创建表(继承数据库字符集)
CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2)
) ENGINE=InnoDB;

-- 查看表的字符集
SHOW CREATE TABLE employees;

操作后结果(SHOW CREATE TABLE 节选):

CREATE TABLE `employees` (
  `emp_id` int(11) NOT NULL AUTO_INCREMENT,
  `emp_name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `dept` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `score` decimal(5,2) DEFAULT NULL,
  PRIMARY KEY (`emp_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

结果解读:

  • 数据库指定 utf8mb4 后,表和列自动继承该字符集
  • COLLATE utf8mb4_unicode_ci 表示使用 Unicode 排序规则,不区分大小写
  • 生产环境创建数据库必须显式指定 utf8mb4,不能依赖默认值

场景三:不同排序规则的比较行为

演示数据准备:

DROP TABLE IF EXISTS employees;

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
    dept VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin,
    score DECIMAL(5,2)
);

INSERT INTO employees (emp_name, dept, score) VALUES
('DaXiang', '技术部', 100),
('daXiang', '产品部', NULL);

当前 employees 表中的完整数据如下:

emp_idemp_namedeptscore
1DaXiang技术部100
2daXiang产品部NULL

执行语句:

-- emp_name 使用 _general_ci(不区分大小写)
SELECT * FROM employees WHERE emp_name = 'daxiang';

-- dept 使用 _bin(区分大小写,按字节比较)
SELECT * FROM employees WHERE dept = '技术部';

操作后结果:

emp_name = 'daxiang'(不区分大小写):

emp_idemp_namedeptscore
1DaXiang技术部100
2daXiang产品部NULL

结果解读:

  • utf8mb4_general_ci 不区分大小写,'daxiang' 匹配 'DaXiang' 和 'daXiang'
  • utf8mb4_bin 按字节二进制比较,区分大小写,但中文字符在 bin 和 ci 下行为一致(因为 Unicode 中文字符没有大小写概念)
  • 如果业务要求用户名大小写敏感(如 "Admin" ≠ "admin"),应使用 _bin 排序规则

场景四:连接字符集不一致导致乱码

执行语句:

-- 模拟客户端用 latin1 发送中文
SET NAMES latin1;

-- 插入中文(此时客户端按 latin1 编码发送)
INSERT INTO employees (emp_name, dept, score) VALUES ('白歌', '技术部', NULL);

-- 查询(连接字符集是 latin1,MySQL 会按 latin1 解码 utf8mb4 存储的数据)
SELECT emp_id, emp_name FROM employees WHERE emp_id = 3;

操作后结果(乱码示例):

emp_idemp_name
3??????

修复方法:

-- 设置正确的连接字符集
SET NAMES utf8mb4;

-- 重新查询
SELECT emp_id, emp_name FROM employees WHERE emp_id = 3;

结果解读:

  • SET NAMES latin1 告诉 MySQL"客户端发送的是 latin1 编码",但客户端实际发送的是 UTF-8 编码的中文
  • MySQL 将 UTF-8 字节流当作 latin1 解析,导致存储时编码错乱,查询时显示为乱码
  • 解决方案:客户端连接后始终执行 SET NAMES utf8mb4;,或在连接字符串中指定 charset=utf8mb4
  • 如果数据已经乱码存储,修复需要知道原始编码转换路径,可能无法完全恢复

场景五:修改已有表的字符集

当前数据状态:假设某表误用 latin1 创建。

-- 模拟 latin1 表
CREATE TABLE employees_latin1 (
    id INT PRIMARY KEY,
    name VARCHAR(20)
) CHARACTER SET latin1;

-- 查看当前字符集
SHOW CREATE TABLE employees_latin1;

执行语句:

-- 修改表字符集(会转换已有数据)
ALTER TABLE employees_latin1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 验证
SHOW CREATE TABLE employees_latin1;

操作后结果(SHOW CREATE TABLE 节选):

CREATE TABLE `employees_latin1` (
  `id` int(11) NOT NULL,
  `name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

结果解读:

  • ALTER TABLE ... CONVERT TO CHARACTER SET 会转换已有数据的编码
  • 如果原表已有中文数据且存储时编码正确(只是表标记为 latin1),转换后正常
  • 如果原表数据已经乱码(如 UTF-8 字节被当作 latin1 存储),CONVERT TO 会按 latin1 解码再编码为 utf8mb4,可能更乱
  • 大表执行 CONVERT TO 会重建整张表,需要 Online DDL 或维护窗口

常见误区

误区正解
"MySQL 的 utf8 就是 UTF-8"不是。MySQL 的 utf8 是 utf8mb3 的别名,最多 3 字节,不支持 emoji 和生僻汉字。必须用 utf8mb4。
"字符集只影响存储,不影响查询"不是。字符集影响存储编码、连接传输编码、比较和排序行为。
"_ci 排序规则性能比 _bin 好"不一定。_general_ci 比较时做大小写转换,有时比 _bin 慢。_bin 直接比较字节码,最简单最快。
"SET NAMES utf8 就够了"不够。SET NAMES utf8 设置的是 utf8mb3,不支持 4 字节字符。应始终用 SET NAMES utf8mb4。
"修改表字符集用 ALTER TABLE ... CHARACTER SET"不完全。CHARACTER SET 只改表定义,不改已有数据。要转换数据用 CONVERT TO CHARACTER SET。
"中文字符集用 gbk 更好"不推荐。gbk 只支持中文和部分符号,不支持 emoji 和多语言。utf8mb4 是通用标准。

面试考点

Q:MySQL 中 utf8 和 utf8mb4 的区别?

utf8 是 utf8mb3 的别名,最多 3 字节/字符,不支持 emoji(4 字节)和部分生僻汉字。utf8mb4 最多 4 字节/字符,支持完整 Unicode(包括 emoji)。MySQL 5.7 生产环境必须显式使用 utf8mb4。

Q:排序规则 _ci、_cs、_bin 的区别?

_ci(Case Insensitive)不区分大小写,如 'A' = 'a';_cs(Case Sensitive)区分大小写;_bin 按字节二进制比较,区分大小写且按字节码排序。_bin 性能通常最好,_ci 适合用户名搜索等不区分大小写的场景。

Q:中文乱码怎么排查?

  1. 查存储字符集:SHOW CREATE TABLE 看表/列字符集是否为 utf8mb4;2. 查连接字符集:SHOW VARIABLES LIKE 'character_set%' 看 client/connection/results 是否一致且为 utf8mb4;3. 查客户端编码:确保客户端(程序、命令行)实际发送的是 UTF-8;4. 修复:统一为 utf8mb4,必要时 CONVERT TO CHARACTER SET 转换。

Q:utf8mb4_general_ci 和 utf8mb4_unicode_ci 的区别?

两者都不区分大小写。general_ci 是 MySQL 自己实现的简化排序规则,比较速度快但精度低(如某些德语字符排序不准确);unicode_ci 基于 Unicode Collation Algorithm,排序更准确但稍慢。一般场景用 general_ci 足够,多语言精确排序用 unicode_ci。

Q:如何修改已有表的字符集?

ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。这会重建表并转换数据编码。大表应在低峰期执行,或使用 pt-online-schema-change 工具避免锁表。

小结

  • 字符集决定存储什么字符,排序规则决定如何比较和排序
  • MySQL 5.7 中 utf8 = utf8mb3(3 字节),**必须用 utf8mb4(4 字节)**才能支持 emoji 和完整 Unicode
  • 排序规则 _ci 不区分大小写,_bin 按字节比较区分大小写
  • 乱码三要素:存储字符集、连接字符集(SET NAMES)、客户端实际编码,三者必须一致且为 utf8mb4
  • 创建数据库/表时显式指定 CHARACTER SET utf8mb4,不要依赖默认值

下一章引子:字符集解决了数据存储的编码问题,但当单表数据量达到千万级时,查询性能会急剧下降。分区表(Partitioning)通过将大表拆分为多个物理分区,让查询只扫描必要的数据片段。

上一页
my.cnf 核心参数