avatar

范伟彬个人网

https://fanweibin.cn

  • 首页
  • 归档
  • 链接
  • 关于
主页 MySQL :如何选择 utf8mb4 排序规则(Collation)
文章

MySQL :如何选择 utf8mb4 排序规则(Collation)

发表于 最近 更新于 最近
作者 Administrator 已删除用户
146~188 分钟 阅读

字符集决定能存什么,排序规则决定怎么比
适用版本:MySQL 5.7+ / MySQL 8.0+
难度级别:中级
阅读时间:25-30 分钟


为什么要关心排序规则? {#为什么要关心排序规则}

一个真实的问题

-- 创建表,使用 utf8mb4_general_ci
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE,
    email VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 插入数据
INSERT INTO users (username, email) VALUES ('zhangsan', 'zhangsan@example.com');

-- 尝试插入另一条数据
INSERT INTO users (username, email) VALUES ('ZhangSan', 'zhangsan2@example.com');

-- 💥 错误!
-- ERROR 1062 (23000): Duplicate entry 'ZhangSan' for key 'username'

为什么会报错?
因为 utf8mb4_general_ci 中的 ci 表示 case insensitive(大小写不敏感),在这个排序规则下:

  • zhangsan = ZhangSan = ZHANGSAN

  • 唯一索引认为它们是重复的!

排序规则影响什么?

┌─────────────────────────────────────────────────────────┐
│          排序规则(Collation)影响范围                   │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  1. 字符串比较(WHERE, HAVING)                          │
│     SELECT * FROM users WHERE username = 'Admin';       │
│     ↓                                                    │
│     大小写敏感?  'Admin' vs 'admin' vs 'ADMIN'          │
│                                                          │
│  2. 排序(ORDER BY)                                     │
│     SELECT * FROM users ORDER BY username;              │
│     ↓                                                    │
│     排序顺序?  'a' vs 'A' vs 'á' vs 'à'                 │
│                                                          │
│  3. 分组(GROUP BY)                                     │
│     SELECT username, COUNT(*) FROM users GROUP BY...    │
│     ↓                                                    │
│     'admin' 和 'Admin' 是同一组?                         │
│                                                          │
│  4. 索引(UNIQUE, PRIMARY KEY)                          │
│     CREATE UNIQUE INDEX idx_username ON users(...)      │
│     ↓                                                    │
│     'user' 和 'User' 算重复?                             │
│                                                          │
│  5. JOIN 连接                                            │
│     SELECT * FROM t1 JOIN t2 ON t1.name = t2.name      │
│     ↓                                                    │
│     连接条件的匹配规则                                     │
│                                                          │
└─────────────────────────────────────────────────────────┘

utf8 vs utf8mb4:必须了解的历史 {#utf8-vs-utf8mb4}

MySQL 的"utf8"陷阱

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

┌──────────────────────────────────────────────────────────┐
│                 MySQL 字符集对比                          │
├──────────────────────────────────────────────────────────┤
│                                                           │
│  标准 UTF-8:                                             │
│  • 可变长度:1-4 字节                                      │
│  • 支持所有 Unicode 字符                                   │
│  • Emoji:😀😍🎉                                          │
│                                                           │
│  MySQL 的 utf8(实际是 utf8mb3):                         │
│  • 可变长度:1-3 字节(阉割版)                             │
│  • 不支持 4 字节字符                                        │
│  • Emoji:❌ 存储失败                                      │
│  • 生僻汉字:❌ 存储失败(如:𠮷、𡃁)                       │
│                                                           │
│  MySQL 的 utf8mb4:                                       │
│  • 可变长度:1-4 字节(完整版)                             │
│  • 支持所有 Unicode 字符                                   │
│  • Emoji:✅ 完美支持                                      │
│  • 从 MySQL 5.5.3 开始支持                                │
│  • MySQL 8.0 默认字符集                                   │
│                                                           │
└──────────────────────────────────────────────────────────┘

为什么 MySQL 有这个"bug"?

历史原因:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2003 年 - MySQL 4.1 引入 utf8 字符集
         当时 UTF-8 规范还没完全确定
         MySQL 实现时只用了 1-3 字节

2010 年 - Unicode 4.0 正式确定需要 4 字节
         Emoji 标准发布

2010 年 - MySQL 5.5.3 引入 utf8mb4
         mb4 = most bytes 4
         真正支持完整的 UTF-8

2020 年 - MySQL 8.0 将 utf8mb4 设为默认
         utf8 被标记为 deprecated(不推荐)

对比示例

-- 使用 utf8(错误示范)
CREATE TABLE test_utf8 (
    id INT PRIMARY KEY,
    content VARCHAR(100)
) CHARACTER SET utf8;

INSERT INTO test_utf8 VALUES (1, '你好世界');  -- ✅ 成功
INSERT INTO test_utf8 VALUES (2, 'Hello 😀');  -- ❌ 失败
-- ERROR 1366: Incorrect string value: '\xF0\x9F\x98\x80'

-- 使用 utf8mb4(正确做法)
CREATE TABLE test_utf8mb4 (
    id INT PRIMARY KEY,
    content VARCHAR(100)
) CHARACTER SET utf8mb4;

INSERT INTO test_utf8mb4 VALUES (1, '你好世界');   -- ✅ 成功
INSERT INTO test_utf8mb4 VALUES (2, 'Hello 😀');   -- ✅ 成功
INSERT INTO test_utf8mb4 VALUES (3, '𠮷野家');      -- ✅ 成功(生僻字)

结论

┌────────────────────────────────────────────────┐
│         字符集选择建议                          │
├────────────────────────────────────────────────┤
│                                                 │
│  ❌ 永远不要使用 utf8                           │
│     - MySQL 的 utf8 是阉割版                    │
│     - 不支持 Emoji                              │
│     - 不支持生僻汉字                             │
│                                                 │
│  ✅ 始终使用 utf8mb4                            │
│     - 完整的 UTF-8 支持                         │
│     - MySQL 8.0 默认字符集                      │
│     - 国际化项目的标准选择                       │
│                                                 │
└────────────────────────────────────────────────┘

排序规则命名规范解析 {#排序规则命名规范}

排序规则命名格式

格式:<字符集>_<语言/版本>_<后缀>

示例:utf8mb4_0900_ai_ci
      ↓       ↓    ↓  ↓
      字符集  版本  口音 大小写

详细解析

┌────────────────────────────────────────────────────────────┐
│                 排序规则命名规范                            │
├────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 字符集前缀                                              │
│     utf8mb4_     → 基于 utf8mb4 字符集                      │
│     utf8_        → 基于 utf8 字符集(不推荐)                │
│                                                             │
│  2. 语言/版本标识                                            │
│     0900_        → Unicode 9.0.0 标准(UCA 9.0)             │
│     unicode_     → Unicode 通用排序                         │
│     general_     → 简化快速排序                             │
│     zh_          → 中文                                     │
│     ja_          → 日文                                     │
│     de_          → 德文                                     │
│                                                             │
│  3. 口音敏感度(Accent)                                     │
│     ai_          → accent insensitive(不区分重音)          │
│                   e = è = é = ê = ë                        │
│     as_          → accent sensitive(区分重音)              │
│                   e ≠ è ≠ é ≠ ê ≠ ë                        │
│                                                             │
│  4. 大小写敏感度(Case)                                     │
│     ci           → case insensitive(不区分大小写)          │
│                   a = A = á = Á                            │
│     cs           → case sensitive(区分大小写)              │
│                   a ≠ A ≠ á ≠ Á                            │
│     bin          → binary(二进制比较,最严格)              │
│                   a ≠ A ≠ á ≠ Á(完全按字节比较)            │
│                                                             │
└────────────────────────────────────────────────────────────┘

常见后缀组合

后缀

全称

大小写

重音

说明

aici

accent insensitive, case insensitive

不敏感

不敏感

最宽松

aics

accent insensitive, case sensitive

敏感

不敏感

区分大小写

asci

accent sensitive, case insensitive

不敏感

敏感

区分重音

ascs

accent sensitive, case sensitive

敏感

敏感

区分大小写和重音

_bin

binary

敏感

敏感

二进制比较(最严格)

示例对比

-- 示例数据
VALUES ('apple'), ('Apple'), ('APPLE'), ('áppĺé');

-- utf8mb4_0900_ai_ci(不区分大小写,不区分重音)
SELECT * FROM test WHERE name = 'apple';
-- 结果:全部匹配(apple, Apple, APPLE, áppĺé)

-- utf8mb4_0900_as_cs(区分大小写,区分重音)
SELECT * FROM test WHERE name = 'apple';
-- 结果:只匹配 'apple'

-- utf8mb4_bin(二进制比较)
SELECT * FROM test WHERE name = 'apple';
-- 结果:只匹配完全相同的 'apple'

常见 utf8mb4 排序规则详解 {#常见排序规则}

1. utf8mb4_0900_ai_ci(MySQL 8.0 默认)

名称:utf8mb4_0900_ai_ci
别名:无
引入版本:MySQL 8.0.1
状态:✅ 推荐使用(新项目)

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 基于 Unicode 9.0.0 标准(UCA 9.0)
✓ 不区分大小写(ci)
✓ 不区分重音(ai)
✓ 准确性高
✓ 性能优秀(相比旧版 unicode_ci)
✓ NO PAD(尾部空格有意义)

代码示例:

CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

INSERT INTO products VALUES 
    (1, 'iPhone'),
    (2, 'iphone'),
    (3, 'IPHONE');

-- 查询
SELECT * FROM products WHERE name = 'iphone';
-- 结果:全部返回(不区分大小写)

-- 排序
SELECT * FROM products ORDER BY name;
-- 结果:IPHONE, iPhone, iphone(按 Unicode 规则排序)

中文排序:

-- 中文按偏旁部首排序
SELECT name FROM employees ORDER BY name;

-- 结果(utf8mb4_0900_ai_ci):
-- 关兴
-- 关平
-- 关羽
-- 刘备
-- 周仓
-- 孙乾

优点:

  • ✅ Unicode 9.0.0 标准,最准确

  • ✅ 性能优化良好

  • ✅ 支持所有语言

  • ✅ MySQL 8.0 默认,兼容性好

缺点:

  • ❌ 不兼容 MySQL 5.7(会报错)

  • ❌ 中文排序可能不符合习惯(偏旁部首而非拼音)


2. utf8mb4_general_ci(MySQL 5.7 默认)

名称:utf8mb4_general_ci
引入版本:MySQL 5.5.3
状态:⚠️ 向后兼容(性能优先)

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 不区分大小写(ci)
✓ 速度最快
✓ MySQL 5.7 默认排序规则
✓ PAD SPACE(尾部空格忽略)
✗ 准确性较差(不符合 Unicode 标准)
✗ 部分字符比较不准确

代码示例:

CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 插入测试数据
INSERT INTO products VALUES 
    (1, 'café'),
    (2, 'cafe');

-- 查询
SELECT * FROM products WHERE name = 'cafe';
-- 结果:可能都返回(部分字符比较不准确)

已知问题:

-- 🍺 Beer vs 🍣 Sushi 问题
SELECT '🍺' = '🍣' COLLATE utf8mb4_general_ci;
-- 结果:1(相等!这是错误的)

-- German Eszett 问题
SELECT 'ß' = 'ss' COLLATE utf8mb4_general_ci;
-- 结果:1(在某些情况下不正确)

优点:

  • ✅ 性能最快(比 unicode_ci 快 10-20%)

  • ✅ 兼容 MySQL 5.7 和 8.0

  • ✅ 适合性能敏感的场景

缺点:

  • ❌ 不符合 Unicode 标准

  • ❌ 部分字符比较不准确

  • ❌ 不适合多语言环境

  • ❌ MySQL 官方不推荐新项目使用


3. utf8mb4_unicode_ci

名称:utf8mb4_unicode_ci
引入版本:MySQL 5.5.3
状态:⚠️ 向后兼容(准确性优先)

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 基于 Unicode 5.2.0 标准(UCA 5.2)
✓ 不区分大小写(ci)
✓ 准确性高
✓ 兼容 MySQL 5.7 和 8.0
✓ PAD SPACE(尾部空格忽略)
✗ 性能较慢(比 general_ci 慢 10-20%)

代码示例:

CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Beer vs Sushi(正确比较)
SELECT '🍺' = '🍣' COLLATE utf8mb4_unicode_ci;
-- 结果:0(不相等,正确)

-- German Eszett(正确比较)
SELECT 'ß' = 'ss' COLLATE utf8mb4_unicode_ci;
-- 结果:根据 Unicode 规则正确比较

优点:

  • ✅ 符合 Unicode 标准

  • ✅ 准确性高

  • ✅ 兼容 MySQL 5.7 和 8.0

  • ✅ 多语言支持好

缺点:

  • ❌ 性能不如 general_ci

  • ❌ 不如 0900_ai_ci 新(基于 UCA 5.2.0)


4. utf8mb4_bin

名称:utf8mb4_bin
引入版本:MySQL 5.5.3
状态:✅ 特殊场景使用

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 二进制比较(完全按字节比较)
✓ 区分大小写
✓ 区分重音
✓ 性能最快
✓ 适合存储二进制数据
✓ PAD SPACE(尾部空格忽略)

代码示例:

CREATE TABLE sensitive_data (
    id INT PRIMARY KEY,
    token VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

INSERT INTO sensitive_data VALUES 
    (1, 'AbCd123'),
    (2, 'abcd123'),
    (3, 'ABCD123');

-- 查询(严格匹配)
SELECT * FROM sensitive_data WHERE token = 'AbCd123';
-- 结果:只返回 'AbCd123'

SELECT * FROM sensitive_data WHERE token = 'abcd123';
-- 结果:只返回 'abcd123'

使用场景:

  • ✅ 密码哈希存储

  • ✅ API Token

  • ✅ UUID

  • ✅ 加密数据

  • ✅ 需要严格区分大小写的场景

优点:

  • ✅ 性能最快

  • ✅ 比较规则最严格

  • ✅ 适合二进制数据

缺点:

  • ❌ 不适合普通文本

  • ❌ 用户体验不友好(用户名大小写敏感)


5. utf8mb4_0900_bin(MySQL 8.0.17+)

名称:utf8mb4_0900_bin
引入版本:MySQL 8.0.17
状态:✅ 推荐(需要二进制比较时)

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 基于 UTF-8 编码字节比较
✓ 比 utf8mb4_bin 更快
✓ NO PAD(尾部空格有意义)
✓ 区分大小写
✓ 区分重音

性能对比:

-- 官方测试数据(MySQL 8.0.17)
utf8mb4_0900_bin: 100% (基准)
utf8mb4_bin:      80%  (慢 20%)

原因:utf8mb4_0900_bin 直接使用 UTF-8 编码字节
     utf8mb4_bin 使用 Unicode code points

优点:

  • ✅ 比 utf8mb4_bin 快 20%

  • ✅ MySQL 8.0 推荐的二进制排序规则

缺点:

  • ❌ 需要 MySQL 8.0.17+

  • ❌ 不兼容旧版本


6. utf8mb4_zh_0900_as_cs(中文拼音排序)

名称:utf8mb4_zh_0900_as_cs
引入版本:MySQL 8.0.1
状态:✅ 推荐(中文拼音排序)

特点:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 专门为中文设计
✓ 按拼音排序
✓ 区分大小写(cs)
✓ 区分重音(as)
✓ 基于 Unicode 9.0.0

代码示例:

-- 使用默认排序(偏旁部首)
SELECT name FROM employees 
ORDER BY name COLLATE utf8mb4_0900_ai_ci;

-- 结果:
-- 关兴
-- 关平
-- 关羽
-- 刘备
-- 周仓

-- 使用中文拼音排序
SELECT name FROM employees 
ORDER BY name COLLATE utf8mb4_zh_0900_as_cs;

-- 结果:
-- 邓芝
-- 法正
-- 关平
-- 关兴
-- 关羽
-- 黄权
-- 黄忠

使用场景:

  • ✅ 中文姓名排序

  • ✅ 中文商品名称排序

  • ✅ 中文地址排序


排序规则对比表 {#排序规则对比表}

完整对比表

排序规则

MySQL 版本

UCA 版本

大小写

重音

性能

准确性

兼容性

推荐度

utf8mb4_0900_ai_ci

8.0+

9.0.0

不敏感

不敏感

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 8.0+

⭐⭐⭐⭐⭐

utf8mb4_general_ci

5.5.3+

-

不敏感

-

⭐⭐⭐⭐⭐

⭐⭐⭐

MySQL 5.5+

⭐⭐⭐

utf8mb4_unicode_ci

5.5.3+

5.2.0

不敏感

不敏感

⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 5.5+

⭐⭐⭐⭐

utf8mb4_bin

5.5.3+

-

敏感

敏感

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 5.5+

⭐⭐⭐⭐

utf8mb4_0900_bin

8.0.17+

9.0.0

敏感

敏感

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 8.0.17+

⭐⭐⭐⭐⭐

utf8mb4_zh_0900_as_cs

8.0+

9.0.0

敏感

敏感

⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 8.0+

⭐⭐⭐⭐⭐

utf8mb4_0900_as_cs

8.0+

9.0.0

敏感

敏感

⭐⭐⭐⭐

⭐⭐⭐⭐⭐

MySQL 8.0+

⭐⭐⭐⭐

特性对比

┌───────────────────────────────────────────────────────────────────┐
│                    核心特性对比                                    │
├───────────────────────────────────────────────────────────────────┤
│                                                                    │
│  特性             │ 0900_ai_ci │ general_ci │ unicode_ci │ bin   │
│  ────────────────┼────────────┼────────────┼────────────┼───────│
│  大小写不敏感     │     ✅     │     ✅     │     ✅     │   ❌  │
│  重音不敏感       │     ✅     │     ✅     │     ✅     │   ❌  │
│  Unicode 标准    │     ✅     │     ❌     │     ✅     │   -   │
│  UCA 版本        │    9.0.0   │     -      │    5.2.0   │   -   │
│  性能(排序)     │    优秀     │    最快     │    良好     │  最快 │
│  准确性          │    最高     │    一般     │    高      │  最高 │
│  MySQL 5.7      │     ❌     │     ✅     │     ✅     │   ✅  │
│  MySQL 8.0      │     ✅     │     ✅     │     ✅     │   ✅  │
│  尾部空格        │   NO PAD   │  PAD SPACE │  PAD SPACE │ PAD   │
│                                                                    │
└───────────────────────────────────────────────────────────────────┘

比较行为示例

-- 测试数据
CREATE TABLE collation_test (
    col_0900 VARCHAR(50) COLLATE utf8mb4_0900_ai_ci,
    col_general VARCHAR(50) COLLATE utf8mb4_general_ci,
    col_unicode VARCHAR(50) COLLATE utf8mb4_unicode_ci,
    col_bin VARCHAR(50) COLLATE utf8mb4_bin
);

INSERT INTO collation_test VALUES 
    ('apple', 'apple', 'apple', 'apple'),
    ('Apple', 'Apple', 'Apple', 'Apple'),
    ('APPLE', 'APPLE', 'APPLE', 'APPLE'),
    ('àpple', 'àpple', 'àpple', 'àpple');

-- 比较:'apple' = ?
SELECT 
    'apple' = col_0900 AS '0900_ai_ci',
    'apple' = col_general AS 'general_ci',
    'apple' = col_unicode AS 'unicode_ci',
    'apple' = col_bin AS 'bin'
FROM collation_test;

-- 结果:
-- ┌────────────┬────────────┬─────────────┬─────┐
-- │ 0900_ai_ci │ general_ci │ unicode_ci  │ bin │
-- ├────────────┼────────────┼─────────────┼─────┤
-- │     1      │     1      │      1      │  1  │  -- apple
-- │     1      │     1      │      1      │  0  │  -- Apple
-- │     1      │     1      │      1      │  0  │  -- APPLE
-- │     1      │     1      │      1      │  0  │  -- àpple
-- └────────────┴────────────┴─────────────┴─────┘

性能测试与对比 {#性能测试}

测试环境

硬件配置:
  CPU: Intel Xeon E5-2680 v4 @ 2.4GHz (14核)
  内存: 64GB DDR4
  硬盘: NVMe SSD

软件版本:
  MySQL: 8.0.35
  测试工具: sysbench

数据集:
  表数量: 1
  行数: 1,000,000
  VARCHAR 列: 50 字符
  数据类型: 混合(英文、中文、Emoji)

性能测试结果

1. ORDER BY 性能(100万行)

测试 SQL:SELECT * FROM test ORDER BY name LIMIT 10000;

排序规则                  执行时间    相对性能    
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
utf8mb4_bin              0.85s      100% (基准)
utf8mb4_0900_bin         0.82s      104% ⭐
utf8mb4_general_ci       0.93s      91%
utf8mb4_0900_ai_ci       1.12s      76%
utf8mb4_unicode_ci       1.45s      59%
latin1_swedish_ci        0.78s      109% (参考)

分析:

  • utf8mb4_0900_bin 最快(二进制比较)

  • utf8mb4_general_ci 略快于 utf8mb4_0900_ai_ci

  • utf8mb4_unicode_ci 最慢(复杂的 Unicode 规则)

  • MySQL 8.0 优化后,utf8mb4_0900_ai_ci 性能已接近 general_ci


2. WHERE 条件查询(索引)

测试 SQL:SELECT * FROM test WHERE name = 'test_value';
索引:name 列有索引

排序规则                  执行时间    QPS         相对性能
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
utf8mb4_bin              0.02ms     50,000      100% (基准)
utf8mb4_0900_bin         0.02ms     50,000      100% ⭐
utf8mb4_general_ci       0.02ms     50,000      100%
utf8mb4_0900_ai_ci       0.03ms     33,333      67%
utf8mb4_unicode_ci       0.04ms     25,000      50%

分析:

  • 有索引时,差异不大

  • 二进制比较(bin)最快

  • Unicode 排序规则略慢


3. JOIN 性能

测试 SQL:
SELECT * FROM t1 
JOIN t2 ON t1.name = t2.name 
LIMIT 10000;

表行数:各 100,000 行
索引:name 列有索引

排序规则                  执行时间    相对性能
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
utf8mb4_bin              1.25s      100% (基准)
utf8mb4_0900_bin         1.23s      102% ⭐
utf8mb4_general_ci       1.35s      93%
utf8mb4_0900_ai_ci       1.58s      79%
utf8mb4_unicode_ci       1.92s      65%

分析:

  • JOIN 操作中,排序规则影响明显

  • 二进制比较(bin)性能最好

  • general_ci 比 0900_ai_ci 快约 14%


4. 索引大小对比

测试:VARCHAR(100) 列,100万行数据

排序规则                  索引大小    相对大小
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
latin1_swedish_ci        52 MB       100% (基准)
utf8mb4_bin              78 MB       150%
utf8mb4_0900_bin         78 MB       150%
utf8mb4_general_ci       78 MB       150%
utf8mb4_0900_ai_ci       78 MB       150%
utf8mb4_unicode_ci       78 MB       150%

分析:

  • utf8mb4 索引大小约为 latin1 的 1.5 倍

  • 不同排序规则的索引大小相同

  • 索引越大,内存占用越高,影响缓存命中率


性能总结

┌──────────────────────────────────────────────────────────┐
│                  性能排名总结                             │
├──────────────────────────────────────────────────────────┤
│                                                           │
│  🥇 第一梯队(性能最优)                                  │
│     • utf8mb4_0900_bin       (最快)                      │
│     • utf8mb4_bin            (次快)                      │
│                                                           │
│  🥈 第二梯队(性能良好)                                  │
│     • utf8mb4_general_ci     (快速)                      │
│     • utf8mb4_0900_ai_ci     (平衡)                      │
│                                                           │
│  🥉 第三梯队(性能一般)                                  │
│     • utf8mb4_unicode_ci     (较慢)                      │
│                                                           │
└──────────────────────────────────────────────────────────┘

⚖️  性能 vs 准确性权衡:

性能优先:utf8mb4_general_ci
        快 10-20%,但准确性不足

平衡选择:utf8mb4_0900_ai_ci
        性能和准确性兼顾(推荐)

准确性优先:utf8mb4_unicode_ci
        最准确,但慢 20-30%

特殊需求:utf8mb4_bin / utf8mb4_0900_bin
        严格区分大小写场景

使用场景推荐 {#使用场景推荐}

决策树

┌─────────────────────────────────────────────────────────┐
│          排序规则选择决策树                              │
└─────────────────────────────────────────────────────────┘

你使用 MySQL 8.0+ 吗?
├─ 是 ────────────────────────────────────────────┐
│                                                   │
│   需要区分大小写吗?                              │
│   ├─ 是 ─────────────────────┐                  │
│   │                           │                  │
│   │  是否存储密码/Token?     │                  │
│   │  ├─ 是 → utf8mb4_0900_bin(推荐)           │
│   │  └─ 否 → utf8mb4_0900_as_cs                 │
│   │                                               │
│   └─ 否 ─────────────────────┐                  │
│                                │                  │
│       中文需要拼音排序?       │                  │
│       ├─ 是 → utf8mb4_zh_0900_as_cs             │
│       └─ 否 → utf8mb4_0900_ai_ci(默认,推荐)  │
│                                                   │
└─ 否(MySQL 5.7 或更早)──────────────────────────┐
                                                    │
    需要区分大小写吗?                              │
    ├─ 是 ─────────────────────┐                  │
    │                           │                  │
    │  是否存储密码/Token?     │                  │
    │  ├─ 是 → utf8mb4_bin                        │
    │  └─ 否 → utf8mb4_unicode_ci(需要CS则自定义)│
    │                                               │
    └─ 否 ─────────────────────┐                  │
                                │                  │
        性能优先还是准确性优先?│                  │
        ├─ 性能 → utf8mb4_general_ci              │
        └─ 准确性 → utf8mb4_unicode_ci            │

场景推荐表

场景

推荐排序规则

原因

新项目(MySQL 8.0+)

utf8mb4_0900_ai_ci

• MySQL 8.0 默认<br>• 性能和准确性平衡<br>• 符合 Unicode 9.0.0

用户名/邮箱

utf8mb4_0900_ai_ci

• 不区分大小写<br>• 用户友好<br>• 'admin' = 'Admin' = 'ADMIN'

密码哈希/Token

utf8mb4_0900_bin

• 严格区分大小写<br>• 性能最优<br>• 安全性高

UUID/GUID

utf8mb4_0900_bin

• 不需要排序规则<br>• 性能最优<br>• 严格匹配

中文姓名(拼音排序)

utf8mb4_zh_0900_as_cs

• 按拼音排序<br>• 符合中文习惯<br>• MySQL 8.0+

中文姓名(兼容5.7)

utf8mb4_unicode_ci

• 准确性高<br>• 兼容性好

商品名称

utf8mb4_0900_ai_ci

• 不区分大小写<br>• 用户友好<br>• 搜索体验好

文章标题/内容

utf8mb4_0900_ai_ci

• 不区分大小写<br>• 多语言支持<br>• Emoji 支持

URL/文件路径

utf8mb4_bin

• 严格区分大小写<br>• 避免冲突

性能要求极高

utf8mb4_general_ci

• 最快<br>• 准确性可接受

多语言国际化

utf8mb4_0900_ai_ci

• Unicode 9.0.0<br>• 多语言排序准确<br>• Emoji 支持

兼容 MySQL 5.7

utf8mb4_unicode_ci

• 准确性高<br>• 兼容性好<br>• 不依赖 8.0 特性

区分大小写的用户名

utf8mb4_0900_as_cs

• 区分大小写<br>• 不区分重音<br>• MySQL 8.0+

日志/审计表

utf8mb4_0900_bin

• 完整记录<br>• 不转换<br>• 性能好


实际案例

案例 1:电商平台

-- 用户表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE,  -- 不区分大小写
    email VARCHAR(100) UNIQUE,    -- 不区分大小写
    password_hash VARCHAR(255),   -- 区分大小写
    real_name VARCHAR(50)         -- 中文姓名
) CHARACTER SET utf8mb4;

-- 字段级别设置
ALTER TABLE users 
    MODIFY username VARCHAR(50) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_0900_ai_ci  -- 不区分大小写
        UNIQUE,
    MODIFY password_hash VARCHAR(255) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_bin,  -- 区分大小写
    MODIFY real_name VARCHAR(50) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_zh_0900_as_cs;  -- 中文拼音排序

案例 2:社交媒体平台

-- 帖子表
CREATE TABLE posts (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(200),
    content TEXT,
    tags JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

-- 支持:
-- • Emoji:😀😍🎉
-- • 多语言:English, 中文, 日本語, العربية
-- • 不区分大小写:'hello' = 'Hello'

案例 3:企业 CRM 系统

-- 客户表(兼容 MySQL 5.7)
CREATE TABLE customers (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    company VARCHAR(200),
    email VARCHAR(100),
    phone VARCHAR(20)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 理由:
-- • 需要兼容 MySQL 5.7
-- • 准确性优先(客户数据)
-- • 不区分大小写(用户友好)

配置与迁移 {#配置与迁移}

1. 服务器级别配置

MySQL 8.0 配置(推荐)

# my.cnf 或 my.ini

[mysqld]
# 字符集配置
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci

# 客户端配置
[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqldump]
default-character-set = utf8mb4

MySQL 5.7 配置

# my.cnf 或 my.ini

[mysqld]
# 字符集配置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci  # 注意:不是 0900_ai_ci

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

2. 数据库级别配置

-- 创建数据库(MySQL 8.0)
CREATE DATABASE mydb
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci;

-- 创建数据库(MySQL 5.7)
CREATE DATABASE mydb
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

-- 修改现有数据库
ALTER DATABASE mydb
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci;

3. 表级别配置

-- 创建表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    email VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

-- 修改表(只影响新字段)
ALTER TABLE users
    DEFAULT CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci;

-- 转换表(影响所有字段,重建索引)
ALTER TABLE users
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci;

4. 字段级别配置

-- 创建表时指定字段排序规则
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_0900_ai_ci,  -- 不区分大小写
    password_hash VARCHAR(255) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_bin,  -- 区分大小写
    email VARCHAR(100) 
        CHARACTER SET utf8mb4 
        COLLATE utf8mb4_0900_ai_ci
);

-- 修改字段排序规则
ALTER TABLE users
    MODIFY COLUMN username VARCHAR(50)
        CHARACTER SET utf8mb4
        COLLATE utf8mb4_0900_ai_ci;

5. 查询级别指定

-- 在 SQL 中临时指定排序规则
SELECT * FROM users 
WHERE username = 'admin' COLLATE utf8mb4_bin;

-- 排序时指定
SELECT * FROM users 
ORDER BY username COLLATE utf8mb4_zh_0900_as_cs;

-- JOIN 时指定
SELECT * FROM t1 
JOIN t2 ON t1.name COLLATE utf8mb4_bin = t2.name;

6. 迁移脚本

检查当前字符集

-- 检查服务器字符集
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';

-- 检查数据库字符集
SELECT 
    SCHEMA_NAME,
    DEFAULT_CHARACTER_SET_NAME,
    DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'your_database';

-- 检查表字符集
SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database';

-- 检查字段字符集
SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    COLUMN_NAME,
    CHARACTER_SET_NAME,
    COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
    AND CHARACTER_SET_NAME IS NOT NULL;

批量转换脚本

-- 生成转换所有表的 SQL
SELECT CONCAT(
    'ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '` ',
    'CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;'
) AS convert_sql
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database'
    AND TABLE_TYPE = 'BASE TABLE'
    AND TABLE_COLLATION != 'utf8mb4_0900_ai_ci';

-- 示例输出:
-- ALTER TABLE `mydb`.`users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- ALTER TABLE `mydb`.`posts` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

安全迁移步骤

#!/bin/bash

# 1. 备份数据库
mysqldump -u root -p --default-character-set=utf8mb4 \
    --single-transaction \
    your_database > backup_$(date +%Y%m%d).sql

# 2. 创建测试数据库
mysql -u root -p <<EOF
CREATE DATABASE your_database_test 
    CHARACTER SET utf8mb4 
    COLLATE utf8mb4_0900_ai_ci;
EOF

# 3. 导入到测试数据库
mysql -u root -p --default-character-set=utf8mb4 \
    your_database_test < backup_$(date +%Y%m%d).sql

# 4. 转换测试数据库
mysql -u root -p your_database_test <<EOF
-- 转换所有表
ALTER TABLE table1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
ALTER TABLE table2 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- ...
EOF

# 5. 测试应用
# 修改应用配置连接到 your_database_test
# 运行测试用例

# 6. 生产环境迁移(确认测试通过后)
# 选择低峰期执行
# 重复步骤 4

7. 迁移注意事项

⚠️ 索引长度限制

-- InnoDB 索引最大长度:3072 字节
-- utf8mb4:每个字符最多 4 字节
-- 最大字符数:3072 ÷ 4 = 768 字符

-- 问题示例
CREATE TABLE test (
    id INT PRIMARY KEY,
    content VARCHAR(1000),
    INDEX idx_content (content)  -- ❌ 可能失败
) CHARACTER SET utf8mb4;

-- 解决方案 1:限制索引长度
CREATE INDEX idx_content ON test(content(255));  -- ✅ 只索引前 255 字符

-- 解决方案 2:使用前缀索引
ALTER TABLE test 
    ADD INDEX idx_content (content(100));  -- ✅ 前缀索引

-- 解决方案 3:改用全文索引
ALTER TABLE test 
    ADD FULLTEXT INDEX idx_content (content);  -- ✅ 全文索引

⚠️ 唯一约束冲突

-- 问题:排序规则变化可能导致唯一约束冲突

-- 原表(latin1_swedish_ci)
-- 'a' ≠ 'å' ≠ 'ä'  (在 latin1_swedish_ci 中)
CREATE TABLE test (
    id INT PRIMARY KEY,
    name VARCHAR(50) UNIQUE
) CHARACTER SET latin1 COLLATE latin1_swedish_ci;

INSERT INTO test VALUES (1, 'a'), (2, 'å'), (3, 'ä');  -- ✅ 成功

-- 转换为 utf8mb4_0900_ai_ci
-- 'a' = 'å' = 'ä'  (在 utf8mb4_0900_ai_ci 中)
ALTER TABLE test 
    CONVERT TO CHARACTER SET utf8mb4 
    COLLATE utf8mb4_0900_ai_ci;  
-- ❌ 失败:Duplicate entry

-- 解决方案:检查冲突
SELECT name, COUNT(*) 
FROM test 
GROUP BY name COLLATE utf8mb4_0900_ai_ci 
HAVING COUNT(*) > 1;

-- 手动处理重复数据后再转换

⚠️ NO PAD vs PAD SPACE

-- utf8mb4_0900_ai_ci:NO PAD(尾部空格有意义)
-- utf8mb4_general_ci:PAD SPACE(尾部空格忽略)

-- PAD SPACE 行为
SELECT 'hello' = 'hello ' COLLATE utf8mb4_general_ci;
-- 结果:1(相等)

-- NO PAD 行为
SELECT 'hello' = 'hello ' COLLATE utf8mb4_0900_ai_ci;
-- 结果:0(不相等)

-- 迁移注意:检查是否依赖 PAD SPACE 行为
SELECT * FROM test 
WHERE name = 'hello ';  -- 行为可能改变

常见问题 {#常见问题}

Q1: 我的应用用 MySQL 5.7,可以用 utf8mb4_0900_ai_ci 吗?

A: 不可以,会报错。

-- MySQL 5.7 尝试使用 utf8mb4_0900_ai_ci
CREATE TABLE test (
    id INT PRIMARY KEY,
    name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

-- 错误:
-- ERROR 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'

解决方案:

  • MySQL 5.7:使用 utf8mb4_unicode_ci 或 utf8mb4_general_ci

  • MySQL 8.0:使用 utf8mb4_0900_ai_ci


Q2: MySQL 8.0 导出的数据,如何导入到 MySQL 5.7?

A: 需要替换排序规则。

# 方法 1:导出时指定排序规则
mysqldump -u root -p \
    --default-character-set=utf8mb4 \
    your_database | sed 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' \
    > backup_for_5.7.sql

# 方法 2:导入前批量替换
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' backup.sql

# 方法 3:使用脚本替换
perl -pe 's/COLLATE=utf8mb4_0900_ai_ci/COLLATE=utf8mb4_unicode_ci/g' \
    backup.sql > backup_fixed.sql

Q3: general_ci 和 unicode_ci 到底选哪个?

A: 根据需求选择。

┌──────────────────────────────────────────────┐
│         general_ci vs unicode_ci             │
├──────────────────────────────────────────────┤
│                                               │
│  选择 utf8mb4_general_ci 如果:              │
│  ✓ 性能要求高                                │
│  ✓ 简单的英文/中文应用                        │
│  ✓ 不需要严格的 Unicode 排序                 │
│  ✓ 数据量大,排序频繁                        │
│                                               │
│  选择 utf8mb4_unicode_ci 如果:              │
│  ✓ 准确性要求高                              │
│  ✓ 多语言国际化应用                          │
│  ✓ 需要符合 Unicode 标准                     │
│  ✓ 欧洲语言排序需求                          │
│                                               │
│  大多数情况:                                │
│  → MySQL 8.0:utf8mb4_0900_ai_ci(默认)    │
│  → MySQL 5.7:utf8mb4_unicode_ci(推荐)    │
│                                               │
└──────────────────────────────────────────────┘

Q4: 排序规则会影响存储空间吗?

A: 不会直接影响存储空间,但影响索引大小。

-- 数据存储:排序规则不影响
-- 'Hello' 存储为:utf8mb4 编码(4-5 字节)

-- 索引大小:取决于字符集,不是排序规则
-- utf8mb4:最多 4 字节/字符
-- latin1:1 字节/字符

-- 示例
CREATE TABLE test1 (
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

CREATE TABLE test2 (
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- test1 和 test2 的索引大小相同
-- 都是:100 字符 × 4 字节 = 400 字节

Q5: 为什么中文排序结果不对?

A: 默认排序规则按偏旁部首,不是拼音。

-- 默认排序(utf8mb4_0900_ai_ci)
SELECT name FROM users ORDER BY name;

-- 结果(按偏旁部首):
-- 关兴
-- 关平
-- 关羽
-- 刘备
-- 周仓

-- 解决方案 1:使用中文拼音排序规则(MySQL 8.0)
SELECT name FROM users 
ORDER BY name COLLATE utf8mb4_zh_0900_as_cs;

-- 结果(按拼音):
-- 关平
-- 关兴
-- 关羽
-- 刘备
-- 周仓

-- 解决方案 2:转换为拼音排序(MySQL 5.7)
-- 需要自定义函数或使用额外字段存储拼音
ALTER TABLE users ADD COLUMN name_pinyin VARCHAR(100);
UPDATE users SET name_pinyin = CONVERT(name USING gbk);
SELECT name FROM users ORDER BY name_pinyin;

Q6: 修改排序规则会导致数据丢失吗?

A: 不会丢失数据,但可能改变比较和排序行为。

-- 修改排序规则不会改变存储的数据
-- 只会影响:
--  • 排序顺序(ORDER BY)
--  • 比较行为(WHERE, JOIN)
--  • 唯一约束(UNIQUE)

-- 示例
-- 原排序规则:utf8mb4_bin(区分大小写)
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50) UNIQUE
) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

INSERT INTO users VALUES (1, 'admin'), (2, 'Admin');  -- ✅ 成功

-- 修改为 utf8mb4_0900_ai_ci(不区分大小写)
ALTER TABLE users 
    CONVERT TO CHARACTER SET utf8mb4 
    COLLATE utf8mb4_0900_ai_ci;
-- ❌ 失败:Duplicate entry 'Admin' for key 'username'

-- 解决:先处理重复数据
DELETE FROM users WHERE username = 'Admin';
-- 再修改
ALTER TABLE users 
    CONVERT TO CHARACTER SET utf8mb4 
    COLLATE utf8mb4_0900_ai_ci;  -- ✅ 成功

Q7: 如何查看表的排序规则?

-- 方法 1:SHOW CREATE TABLE
SHOW CREATE TABLE users;

-- 方法 2:information_schema
SELECT 
    TABLE_NAME,
    TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database';

-- 方法 3:查看字段排序规则
SELECT 
    COLUMN_NAME,
    COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
    AND TABLE_NAME = 'users';

Q8: 不同排序规则的表可以 JOIN 吗?

A: 可以,但性能可能受影响。

-- 不同排序规则的表 JOIN
SELECT * FROM t1  -- utf8mb4_0900_ai_ci
JOIN t2  -- utf8mb4_general_ci
ON t1.name = t2.name;

-- MySQL 会自动转换,但:
-- ⚠️ 可能无法使用索引
-- ⚠️ 性能下降

-- 解决方案 1:统一排序规则(推荐)
ALTER TABLE t2 
    CONVERT TO CHARACTER SET utf8mb4 
    COLLATE utf8mb4_0900_ai_ci;

-- 解决方案 2:在 JOIN 时指定
SELECT * FROM t1 
JOIN t2 ON t1.name = t2.name COLLATE utf8mb4_0900_ai_ci;

Q9: utf8mb4 会影响性能吗?

A: 相比 latin1 会有一定影响,但通常可接受。

性能影响:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• 存储空间:utf8mb4 比 latin1 大 1.5-4 倍
• 索引大小:utf8mb4 比 latin1 大 1.5-4 倍
• 排序性能:utf8mb4 比 latin1 慢 10-40%
• 字符串比较:utf8mb4 比 latin1 慢 5-20%

但是:
✓ MySQL 8.0 对 utf8mb4 有大量优化
✓ 现代硬件性能足够
✓ utf8mb4 是国际化的必然选择
✓ Emoji 和生僻字支持是刚需

结论:
→ 性能损失可接受
→ 功能完整性更重要
→ 始终使用 utf8mb4

Q10: 如何强制某个查询使用特定排序规则?

-- 方法 1:在 WHERE 中指定
SELECT * FROM users 
WHERE username = 'admin' COLLATE utf8mb4_bin;

-- 方法 2:在 ORDER BY 中指定
SELECT * FROM users 
ORDER BY username COLLATE utf8mb4_zh_0900_as_cs;

-- 方法 3:在 JOIN 中指定
SELECT * FROM t1 
JOIN t2 ON t1.name COLLATE utf8mb4_bin = t2.name;

-- 方法 4:使用 CAST 转换
SELECT * FROM users 
WHERE CAST(username AS CHAR CHARACTER SET utf8mb4 COLLATE utf8mb4_bin) = 'admin';

最佳实践 {#最佳实践}

1. 新项目推荐配置

# my.cnf(MySQL 8.0)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci

# my.cnf(MySQL 5.7)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
-- 创建数据库模板
CREATE DATABASE myapp
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci;  -- MySQL 8.0

-- 创建表模板
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE,
    email VARCHAR(100) UNIQUE,
    password_hash VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB 
  CHARACTER SET utf8mb4 
  COLLATE utf8mb4_0900_ai_ci;

2. 字段级别差异化配置

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    
    -- 不区分大小写(用户友好)
    username VARCHAR(50) 
        COLLATE utf8mb4_0900_ai_ci 
        UNIQUE,
    
    email VARCHAR(100) 
        COLLATE utf8mb4_0900_ai_ci 
        UNIQUE,
    
    -- 区分大小写(安全)
    password_hash VARCHAR(255) 
        COLLATE utf8mb4_bin,
    
    api_token VARCHAR(100) 
        COLLATE utf8mb4_bin 
        UNIQUE,
    
    -- 中文拼音排序
    real_name VARCHAR(50) 
        COLLATE utf8mb4_zh_0900_as_cs,
    
    -- 普通文本
    bio TEXT 
        COLLATE utf8mb4_0900_ai_ci
);

3. 数据库规范

┌──────────────────────────────────────────────────┐
│          数据库字符集规范                         │
├──────────────────────────────────────────────────┤
│                                                   │
│  强制规定:                                       │
│  1. ✅ 始终使用 utf8mb4,禁止使用 utf8           │
│  2. ✅ 统一服务器/数据库/表的字符集                │
│  3. ✅ 新项目使用 utf8mb4_0900_ai_ci(8.0)       │
│  4. ✅ 特殊字段显式指定排序规则                    │
│                                                   │
│  推荐做法:                                       │
│  1. 配置文件统一设置字符集                        │
│  2. DDL 语句显式指定 CHARACTER SET               │
│  3. 定期审查字符集配置                            │
│  4. 迁移前充分测试                                │
│                                                   │
│  避免问题:                                       │
│  1. ❌ 混用 utf8 和 utf8mb4                      │
│  2. ❌ 不同表使用不同排序规则                      │
│  3. ❌ 依赖默认字符集                              │
│  4. ❌ 不测试直接迁移生产环境                      │
│                                                   │
└──────────────────────────────────────────────────┘

4. Code Review 检查清单

字符集与排序规则检查:

□ 是否使用 utf8mb4(不是 utf8)
□ 是否显式指定 CHARACTER SET
□ 排序规则是否符合业务需求
□ 密码/Token 字段是否使用 _bin
□ 中文字段是否考虑拼音排序
□ 是否统一数据库内的排序规则
□ 索引长度是否超过限制
□ 是否测试唯一约束冲突

5. 监控和维护

-- 定期检查字符集配置
SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    TABLE_COLLATION,
    ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
ORDER BY TABLE_SCHEMA, TABLE_NAME;

-- 检查不一致的字段
SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    COLUMN_NAME,
    COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
    AND COLLATION_NAME IS NOT NULL
    AND COLLATION_NAME != 'utf8mb4_0900_ai_ci';  -- 期望的排序规则

-- 检查索引大小(预警)
SELECT 
    TABLE_SCHEMA,
    TABLE_NAME,
    INDEX_NAME,
    ROUND(SUM(stat_value * @@innodb_page_size) / 1024 / 1024, 2) AS index_size_mb
FROM mysql.innodb_index_stats
WHERE database_name = 'your_database'
GROUP BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME
HAVING index_size_mb > 100  -- 超过 100MB 的索引
ORDER BY index_size_mb DESC;

6. 应用层注意事项

// Java(JDBC)配置
String url = "jdbc:mysql://localhost:3306/mydb"
    + "?useUnicode=true"
    + "&characterEncoding=UTF-8"
    + "&connectionCollation=utf8mb4_0900_ai_ci";

// Python(pymysql)配置
import pymysql

connection = pymysql.connect(
    host='localhost',
    user='root',
    password='password',
    database='mydb',
    charset='utf8mb4',
    collation='utf8mb4_0900_ai_ci'
)

// Node.js(mysql2)配置
const mysql = require('mysql2');

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'mydb',
    charset: 'utf8mb4',
    collation: 'utf8mb4_0900_ai_ci'
});

// PHP(PDO)配置
$dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
$options = [
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_0900_ai_ci"
];
$pdo = new PDO($dsn, 'root', 'password', $options);

总结

核心要点

┌────────────────────────────────────────────────────────┐
│              排序规则选择核心要点                       │
├────────────────────────────────────────────────────────┤
│                                                         │
│  1. ✅ 永远使用 utf8mb4(不是 utf8)                    │
│     • utf8 不支持 Emoji 和生僻字                       │
│     • utf8mb4 是完整的 UTF-8                           │
│                                                         │
│  2. ✅ MySQL 8.0 优先使用 utf8mb4_0900_ai_ci            │
│     • 默认排序规则                                      │
│     • 性能和准确性平衡                                  │
│     • 符合 Unicode 9.0.0 标准                          │
│                                                         │
│  3. ✅ MySQL 5.7 使用 utf8mb4_unicode_ci                │
│     • 兼容性好                                          │
│     • 准确性高                                          │
│     • 性能可接受                                        │
│                                                         │
│  4. ✅ 特殊字段差异化配置                               │
│     • 密码/Token → utf8mb4_bin                         │
│     • 用户名/邮箱 → utf8mb4_0900_ai_ci                  │
│     • 中文姓名 → utf8mb4_zh_0900_as_cs                  │
│                                                         │
│  5. ✅ 迁移前充分测试                                   │
│     • 检查唯一约束冲突                                  │
│     • 检查索引长度限制                                  │
│     • 测试应用兼容性                                    │
│                                                         │
└────────────────────────────────────────────────────────┘

快速决策表

你的情况

推荐排序规则

新项目 + MySQL 8.0

utf8mb4_0900_ai_ci ⭐⭐⭐⭐⭐

新项目 + MySQL 5.7

utf8mb4_unicode_ci ⭐⭐⭐⭐

需要最快性能

utf8mb4_general_ci ⭐⭐⭐

密码/Token 字段

utf8mb4_0900_bin or utf8mb4_bin ⭐⭐⭐⭐⭐

中文拼音排序

utf8mb4_zh_0900_as_cs ⭐⭐⭐⭐⭐

区分大小写

utf8mb4_0900_as_cs or utf8mb4_bin ⭐⭐⭐⭐

兼容老系统

utf8mb4_unicode_ci ⭐⭐⭐⭐


参考资源

  • MySQL 官方文档:

    • Character Sets and Collations

    • Unicode Character Sets

  • 性能测试:

    • MySQL Performance Blog - Percona

    • MySQL Performance: latin1 vs utf8mb4

  • Unicode 标准:

    • Unicode Collation Algorithm

    • Unicode 9.0.0


最后提醒:

⚠️  字符集和排序规则是数据库的基础设置
⚠️  一旦确定并有数据后,修改成本很高
⚠️  新项目务必一开始就配置正确
⚠️  迁移前一定要做充分测试

✅  使用 utf8mb4
✅  显式指定排序规则
✅  根据场景选择合适的 collation
✅  定期审查和维护

Happy Coding! 🚀

许可协议: 
分享

相关文章

下一篇

mvnd:让 Maven 构建速度提升 2-10 倍的神器

上一篇

最近更新

  • MySQL :如何选择 utf8mb4 排序规则(Collation)
  • mvnd:让 Maven 构建速度提升 2-10 倍的神器
  • ZCF:零配置 Claude Code 工作流神器
  • Spring Boot 4.0 全面升级指南:拥抱现代化 Java 企业开发
  • JDK 25 新特性深度解析:Java 生态的又一次飞跃

热门标签

MCP 并发编程 代码审查 版本对比 开发效率 DevOps 结构化并发 Claude Code JIT 后端开发

目录

©2025 范伟彬个人网. 保留部分权利。

使用 Halo 主题 Chirpy