引言

提出问题

使用MySQL时,经常遇到字符编码问题:

  • 中文乱码:插入"你好"变成"???"
  • emoji存储失败:插入😀报错
  • utf8和utf8mb4有什么区别?
  • 为什么查询"a"和"A"结果一样?

这就是字符集(Character Set)和校对规则(Collation)的问题!


字符集基础

什么是字符集?

字符集:定义了字符与二进制编码的映射关系。

示例

  • ASCII:A0x41(65)
  • UTF-8:0xE4BDA0
  • UTF-8:😀0xF09F9880

MySQL常用字符集

字符集字节数支持字符推荐使用
latin11字节西欧字符❌ 不支持中文
gbk1-2字节简体中文⚠️ 老项目
utf81-3字节多语言❌ 不支持emoji
utf8mb41-4字节多语言+emoji推荐

utf8 vs utf8mb4

⚠️ 重要:MySQL的utf8不是真正的UTF-8!

  • utf8:最多3字节,不支持emoji(😀需要4字节)
  • utf8mb4:最多4字节,支持emoji和生僻字
-- ❌ 错误:utf8存储emoji
CREATE TABLE test (name VARCHAR(50)) CHARACTER SET utf8;
INSERT INTO test VALUES ('你好😀');  -- ERROR 1366: Incorrect string value

-- ✅ 正确:utf8mb4存储emoji
CREATE TABLE test (name VARCHAR(50)) CHARACTER SET utf8mb4;
INSERT INTO test VALUES ('你好😀');  -- 成功

查看字符集

-- 查看数据库字符集
SHOW VARIABLES LIKE 'character_set%';

-- 输出示例:
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4                    |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8                       |
+--------------------------+----------------------------+

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

校对规则(Collation)

什么是校对规则?

校对规则:定义字符的比较和排序规则。

示例

  • utf8mb4_general_ci:不区分大小写(a = A
  • utf8mb4_bin:区分大小写(aA

常用校对规则

校对规则说明区分大小写性能
utf8mb4_general_ci通用,不区分大小写
utf8mb4_unicode_ciUnicode标准,不区分大小写较慢
utf8mb4_bin二进制比较,区分大小写最快
utf8mb4_0900_ai_ciMySQL 8.0默认,最准确中等

实战对比

-- 创建测试表(不区分大小写)
CREATE TABLE test_ci (
  name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

INSERT INTO test_ci VALUES ('Apple'), ('apple'), ('APPLE');

-- 查询(不区分大小写)
SELECT * FROM test_ci WHERE name = 'apple';
-- 结果:3条记录(Apple, apple, APPLE)

-- 创建测试表(区分大小写)
CREATE TABLE test_bin (
  name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

INSERT INTO test_bin VALUES ('Apple'), ('apple'), ('APPLE');

-- 查询(区分大小写)
SELECT * FROM test_bin WHERE name = 'apple';
-- 结果:1条记录(apple)

选择建议

场景推荐校对规则
用户名、邮箱utf8mb4_general_ci(不区分大小写)
密码(已加密)utf8mb4_bin(区分大小写)
文章内容utf8mb4_unicode_ci(准确排序)
新项目(MySQL 8.0+)utf8mb4_0900_ai_ci(默认)

字符集的4个层级

MySQL字符集设置有4个层级:

服务器级 → 数据库级 → 表级 → 列级

1. 服务器级(全局默认)

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

-- 修改(配置文件my.cnf)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

2. 数据库级

-- 创建数据库时指定
CREATE DATABASE mydb
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

-- 修改数据库字符集
ALTER DATABASE mydb
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

3. 表级

-- 创建表时指定
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 修改表字符集
ALTER TABLE users
  CONVERT TO CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

4. 列级

-- 列级指定(覆盖表级设置)
CREATE TABLE users (
  id INT PRIMARY KEY,
  username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin,  -- 区分大小写
  nickname VARCHAR(50)  -- 使用表级字符集
);

-- 修改列字符集
ALTER TABLE users
  MODIFY COLUMN username VARCHAR(50)
  CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

解决中文乱码

原因分析

乱码通常因为字符集不一致

客户端 → 连接 → 服务器 → 数据库 → 表 → 列
utf8mb4    utf8    utf8mb4    utf8    latin1  ❌ 不一致导致乱码

解决方案

方案1:修改配置文件(推荐)

# my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

重启MySQL:

sudo systemctl restart mysql

方案2:连接时指定字符集

-- 连接时设置
mysql -u root -p --default-character-set=utf8mb4

-- 或连接后执行
SET NAMES utf8mb4;

-- 等价于:
SET character_set_client = utf8mb4;
SET character_set_connection = utf8mb4;
SET character_set_results = utf8mb4;

方案3:修改已有数据库和表

-- 修改数据库
ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 修改所有表(批量执行)
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE orders CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

验证字符集

-- 插入中文和emoji
INSERT INTO users (username) VALUES ('张三😀');

-- 查询
SELECT * FROM users;
-- 如果显示正常,说明字符集配置正确

emoji存储问题

问题复现

-- 使用utf8(不支持emoji)
CREATE TABLE test (content VARCHAR(100)) CHARACTER SET utf8;
INSERT INTO test VALUES ('Hello😀');
-- ERROR 1366: Incorrect string value: '\xF0\x9F\x98\x80' for column 'content'

解决方案

-- 使用utf8mb4
CREATE TABLE test (content VARCHAR(100)) CHARACTER SET utf8mb4;
INSERT INTO test VALUES ('Hello😀');
-- Query OK

已有表的迁移

# 1. 导出数据
mysqldump -u root -p --default-character-set=utf8mb4 mydb > backup.sql

# 2. 修改表结构
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4;

# 3. 如果数据已乱码,重新导入
mysql -u root -p --default-character-set=utf8mb4 mydb < backup.sql

实战案例:社交平台用户表

CREATE TABLE social_users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,

  -- 用户名:不区分大小写,方便登录
  username VARCHAR(50) NOT NULL UNIQUE
    CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,

  -- 密码(已加密):区分大小写
  password VARCHAR(255) NOT NULL
    CHARACTER SET utf8mb4 COLLATE utf8mb4_bin,

  -- 昵称:支持emoji和特殊字符
  nickname VARCHAR(100)
    CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,

  -- 个性签名:支持emoji
  bio TEXT
    CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,

  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 插入测试数据
INSERT INTO social_users (username, password, nickname, bio) VALUES
  ('zhangsan', SHA2('Password123', 256), '张三😊', '热爱生活💖'),
  ('ZHANGSAN', SHA2('Password456', 256), 'Zhang San', 'Hello World🌍');

-- 查询(用户名不区分大小写)
SELECT * FROM social_users WHERE username = 'ZhangSan';
-- 结果:2条记录(zhangsan和ZHANGSAN)

性能影响

字符集对性能的影响

字符集存储空间索引空间查询性能
latin11字节/字符最小最快
utf81-3字节/字符中等较快
utf8mb41-4字节/字符较大稍慢

校对规则对性能的影响

校对规则比较方式性能
utf8mb4_bin二进制比较最快
utf8mb4_general_ci简单规则较快
utf8mb4_unicode_ciUnicode标准较慢

优化建议

-- ❌ 不推荐:所有列都用TEXT
CREATE TABLE articles (
  content TEXT CHARACTER SET utf8mb4  -- 占用大量空间
);

-- ✅ 推荐:根据实际需要选择类型
CREATE TABLE articles (
  title VARCHAR(200) CHARACTER SET utf8mb4,  -- 标题用VARCHAR
  summary VARCHAR(500) CHARACTER SET utf8mb4,  -- 摘要
  content MEDIUMTEXT CHARACTER SET utf8mb4  -- 正文用TEXT
);

最佳实践

新项目推荐配置

# my.cnf(推荐配置)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4
-- 创建数据库
CREATE DATABASE mydb
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

-- 创建表
CREATE TABLE users (
  username VARCHAR(50) COLLATE utf8mb4_general_ci,  -- 不区分大小写
  password VARCHAR(255) COLLATE utf8mb4_bin  -- 区分大小写
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

老项目迁移

-- 1. 备份数据
mysqldump -u root -p --default-character-set=utf8 mydb > backup.sql

-- 2. 修改数据库
ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 3. 批量修改表(生成SQL脚本)
SELECT CONCAT('ALTER TABLE ', table_name, ' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
FROM information_schema.tables
WHERE table_schema = 'mydb';

-- 4. 执行生成的SQL

常见问题(FAQ)

Q1:为什么MySQL的utf8不是真正的UTF-8?

A:历史遗留问题。MySQL创建utf8时,UTF-8标准还未完善,只支持到3字节。后来添加了utf8mb4(mb4=most bytes 4)支持4字节。

Q2:如何查看表的字符集?

A:

SHOW CREATE TABLE users;
-- 或
SHOW TABLE STATUS LIKE 'users';

Q3:已有数据如何从utf8迁移到utf8mb4?

A:

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Q4:字符集会影响性能吗?

A:有轻微影响。utf8mb4比utf8慢约10-15%,但为了支持emoji和生僻字,这个代价是值得的。


总结

核心要点

  1. 字符集选择

    • 新项目:utf8mb4(支持emoji)
    • 老项目:尽快迁移到utf8mb4
  2. 校对规则选择

    • 通用场景:utf8mb4_unicode_ci
    • 区分大小写:utf8mb4_bin
    • MySQL 8.0+:utf8mb4_0900_ai_ci(默认)
  3. 4个层级

    • 服务器级 > 数据库级 > 表级 > 列级
    • 低层级继承高层级设置
  4. 中文乱码解决

    • 统一字符集为utf8mb4
    • 连接时指定字符集

记忆口诀

字符集选择:utf8mb4最完美

  • utf8mb4:支持emoji和生僻字
  • 校对规则:通用用unicode_ci,密码用bin
  • 统一配置:服务器、数据库、表、列

下一步学习

  • 上一篇:《约束与完整性:主键外键唯一非空》
  • 下一篇:《数据导入导出》
  • 返回目录:MySQL从入门到精通

💡 本文是 “MySQL从入门到精通” 系列的第 8 篇(共86篇)。

📚 建议配置好字符集后,测试插入中文和emoji。


参考资料

  1. MySQL官方文档 - 字符集
  2. MySQL官方文档 - utf8mb4