
1. 先搞清楚排序规则到底是个什么东西说实话被同事这么一问我当时脑子里确实闪过一秒钟的空白。MySQL 的排序规则Collation平时是真容易被忽略——建库建表的时候默认值一填基本就不会再看它一眼。但它实际管的事特别大字符串怎么比较、怎么排序、查询的时候哪些数据算“相等”全由它说了算。数据库跑得久了慢查询、索引失效、数据对不上很多问题的根子就埋在这个不起眼的默认值里。排序规则的官方定义是“一组用于比较字符集中字符的规则”。一个字符集定义的是“有哪些字符”排序规则定义的是“这些字符怎么比大小、怎么排先后”。它们俩是一对搭档字符集管“你有什么牌”排序规则管“怎么比大小”。比如同样一个字母 a有的排序规则认为它和 A 是一回事匹配的时候能互相命中有的排序规则认为它俩严格不同查询必须精确区分大小写。这些行为差异直接影响业务逻辑。当时同事问的原话是“咱们库表建的时候排序规则选哪个我看有的用utf8mb4_general_ci有的用utf8mb4_unicode_ci还有utf8mb4_0900_ai_ci到底啥区别”这个问题听着简单真要答得让同事信服得把背后的比较逻辑、版本差异、索引影响都捋一遍。我后来花了点时间把这块彻底研究了一遍算是把这块短板补上了这篇文章就把我研究的过程和结论完整记录下来。要理解排序规则得先从两个最基础的概念说起字符集Character Set和排序规则Collation。字符集决定数据库能存哪些字符比如utf8mb4能存全世界几乎所有文字包括 Emoji 表情latin1只能存西欧字符遇到中文直接乱码。排序规则则是在这个基础上决定字符之间怎么排序和比较。每一个字符集都有对应的一个或多个排序规则两者是绑定的关系。你可以在 MySQL 里执行下面这条命令直接查看当前数据库支持哪些排序规则SHOW COLLATION WHERE Charset utf8mb4;执行之后会列出几十条记录名字后缀的含义有规律可循_ci结尾表示大小写不敏感Case Insensitive_cs结尾表示大小写敏感Case Sensitive_bin结尾表示按二进制值比较。中间那部分英文字母代表具体的排序算法来源比如general指简化通用算法unicode指基于 Unicode 排序算法0900指 Unicode 9.0 版本的排序算法ai表示重音不敏感Accent Insensitive。这些后缀组合在一起就构成了一个排序规则的完整身份。对我们普通开发者来说不需要记住全部几十种真正天天打交道的就那么几个。MySQL 5.7 时代默认排序规则是utf8mb4_general_ci到了 MySQL 8.0默认改成了utf8mb4_0900_ai_ci。这两个默认值之间的切换本身就埋了不少坑。我后面会专门讲。提示判断数据库是否用到排序规则最简单的办法是看 SQL 语句里的 ORDER BY 是否走索引或者 WHERE 条件里对字符串的等值匹配是否命中索引。排序规则选错这两项性能都会受影响。2. 三大主流排序规则的深度对比别只看名字MySQL 里utf8mb4字符集对应的排序规则有几十种但实际项目里反复出现的就那么几个。我自己的经验是熟记三个就够应付绝大多数场景utf8mb4_general_ci、utf8mb4_unicode_ci、utf8mb4_0900_ai_ci。当然utf8mb4_bin在特殊场景下也很有用我放到后面一起讲。先看一张对比表把它们的核心差异摆出来排序规则所属版本比较算法大小写敏感重音敏感排序准确性性能表现utf8mb4_general_ciMySQL 5.7 默认简化通用算法不敏感不敏感一般较快utf8mb4_unicode_ci各版本均支持Unicode Collation Algorithm不敏感不敏感较准确稍慢utf8mb4_0900_ai_ciMySQL 8.0 默认Unicode 9.0 算法不敏感ai不敏感ai最准确较快utf8mb4_bin各版本均支持二进制直接比较敏感敏感最准确最快2.1 utf8mb4_general_ci老项目的江湖老大utf8mb4_general_ci是 MySQL 5.7 及之前版本的默认排序规则也是大量存量项目的既有选择。它的核心特点是采用了一套简化的比较算法对字符“一视同仁”的程度比较高——只要读音相近、形态相近就很可能被认为相等。这种设计牺牲掉了一部分排序精度换来了比较快的处理速度。MyISAM 和 InnoDB 存储引擎时代性能是首要考量所以这个排序规则成了默认配置。它的字母缩写 “general” 本来就是“通用、简化”的意思。举个具体例子来说在某些情况下它会把一些特殊符号和普通字母混在一起比较排序结果可能不符合语言学的精确顺序。但绝大多数业务场景根本感知不到这个差异因为中文排序主要还是靠拼音或者业务自定义规则数据库层面的排序精确度影响有限。utf8mb4_general_ci还有一个比较特殊的行为它不支持某些 Unicode 字符的扩展比较对德语、法语这类带变音符号的语言排序结果可能和语言学标准有出入。但对中文、英文场景它完全够用。如果你的项目是 5.7 版本起步、没有特殊排序需求继续用它问题不大。2.2 utf8mb4_unicode_ci追求标准排序的过渡选择utf8mb4_unicode_ci采用的是 Unicode Collation AlgorithmUCA这是 Unicode 官方的排序标准。它在排序准确度上比general_ci高一个档次对各国语言文字的顺序规则支持得更完整。比如德语中的 ß 在某些排序规则下和 ss 等价法语中的重音符号排序优先级更符合当地人习惯这些细节unicode_ci都能正确处理。但它也有明显短板基于 UCA 的早期实现性能比general_ci差一些特别是在大量字符串比较的场景下能感觉到差异。网上很多文章说“有得必有失准确性高就慢”实际测试下来差异确实存在但并不夸张。如果项目对排序准确性要求高、又不可能一口气升到 MySQL 8.0unicode_ci是靠谱选择。这里要特别提一句utf8mb4_unicode_ci在 MySQL 里其实还有细分版本比如utf8mb4_unicode_520_ci对应 Unicode 5.2 标准。不同版本之间的排序行为也有细微差异但日常开发不用太纠结这个按默认的utf8mb4_unicode_ci理解即可。2.3 utf8mb4_0900_ai_ciMySQL 8.0 的默认新贵utf8mb4_0900_ai_ci是 MySQL 8.0 引入的排序规则其中 0900 指 Unicode 9.0 标准。它的特点可以概括为三个词准确、快速、现代。它采用了重新实现的比较算法在排序准确度上对标unicode_ci在性能上反而超越general_ci可以说是既要又要的代表作。名字里的ai是 Accent Insensitive 的缩写表示重音不敏感。这意味着它认为 é 和 e 是同一个字符查询WHERE name cafe也能匹配到café。这个行为在某些业务场景是优势比如搜索功能希望忽略用户输入的重音差异但在某些场景也可能是坑比如用户名登录时本来应该精确匹配结果café和cafe都能登进同一个账号这是需要警惕的。utf8mb4_0900_ai_ci的排序准确度、性能、字符覆盖率都优于前两代方案这就是为什么 MySQL 8.0 把它设为默认值。但问题是8.0 之前的版本根本不认识这个名字。如果数据库版本是 5.7建表语句里写上utf8mb4_0900_ai_ciMySQL 会直接报错。配套的问题是如果在 8.0 上建了表再把数据迁移回 5.7排序规则不兼容也会导致问题。这个版本绑定关系是选型时第一个要确认的事。注意utf8mb4_0900_ai_ci只在 MySQL 8.0 及以上版本可用。数据库升级到 8.0 之后旧表默认还是沿用原来的utf8mb4_general_ci不会自动切换新排序规则。需要手动执行 ALTER TABLE 才会转换。2.4 utf8mb4_bin大小写敏感的极客之选utf8mb4_bin的角色比较特殊它不做任何“智能匹配”直接用字符的二进制编码进行比较。在这个规则下a 和 A 是完全不同的字符é 和 e 也是完全不同的字符。这种“死板”的行为在部分场景反而是优势存储用户密码哈希值虽然是二进制但通常用字符串表示存储大小写敏感的业务编码比如订单号、身份证号需要精确匹配且不希望数据库做任何隐式转换的场景utf8mb4_bin的比较速度也最快因为没有复杂的规则计算。但它的缺点同样明显对用户输入的搜索不友好用户搜 “apple” 搜不到 “Apple”这对大多数业务是不可接受的。所以_bin排序规则适合“内部编码类”字段不适合“用户可搜索类”字段。我在实际项目中的做法是全表默认用大小写不敏感的排序规则个别需要精确匹配的字段单独指定utf8mb4_bin。MySQL 允许同一张表的不同字段使用不同排序规则这样可以在不影响全局的前提下精准控制行为。3. 排序规则选型指南这么多规则到底选哪个聊完三种主流排序规则的差异接下来是更实际的问题我手上的项目到底应该选哪个这个问题没有唯一答案但可以按项目状态、数据库版本、业务场景三个维度来逐步收敛。我自己总结了一套决策思路分享出来给大家参考。3.1 按数据库版本决定主干方向数据库版本是最硬性的约束条件它直接决定了可选范围。MySQL 5.7 及以下版本能用的是general_ci和unicode_ci这一代排序规则0900_ai_ci完全不认识。MySQL 8.0 及以上版本三种主流排序规则都可用但默认推荐0900_ai_ci。正因为这个版本差异选型第一步必须先确认数据库版本。查版本很简单SELECT VERSION();拿到版本号之后再按下面的思路做选择版本是 5.7新项目直接选utf8mb4_unicode_ci排序准确度比general_ci好性能差异可以接受版本是 5.7老项目保持utf8mb4_general_ci不动除非有明确排序需求否则不折腾版本是 8.0新项目直接用默认的utf8mb4_0900_ai_ci不要改版本是 8.0老项目逐步考虑升级但升级过程要评估索引影响后面详细说这里多说一句很多老项目从 5.7 升到 8.0 之后表结构还是 5.7 时代的general_ci运行完全没问题不升级排序规则也没毛病。排序规则的升级不是必须的除非业务需求发生了变化。3.2 按业务场景判断敏感需求同一张表不同字段的排序规则可以不同这是一个非常实用的特性。我惯用的做法是为不同敏感度字段分配不同规则。表单字段的敏感度可以这样粗略分类敏感度典型字段推荐排序规则原因不敏感用户昵称、文章标题、商品名称utf8mb4_0900_ai_ci 或 utf8mb4_general_ci搜索和排序友好的大小写不敏感中等敏感邮箱、登录名utf8mb4_0900_ai_ci通常希望忽略大小写但注意重音不敏感可能带来意外匹配高敏感订单号、优惠码、哈希值utf8mb4_bin精确匹配不允许任何隐式转换这个表格的逻辑很简单用户看得见、搜得到的内容用大小写不敏感机器生成、需要精确匹配的编码用二进制比较。混合使用能让同一张表既具备友好的搜索体验又保证关键字段的精确性。3.3 从性能角度审视排序规则的影响排序规则对性能的影响主要体现在两方面索引使用和查询执行效率。先看索引使用MySQL 的索引本质上是一种有序结构排序规则决定了这个有序结构的排列顺序。查询条件里的字符串比较如果和索引的排序规则一致就能正常走索引如果不一致就可能触发隐式转换导致索引失效。举个例子假设有一个用户表name 字段建立了索引排序规则是utf8mb4_general_ci。如果执行SELECT * FROM user WHERE name Alice;MySQL 会拿查询值 “Alice” 和索引里的值按general_ci的规则比较匹配正常索引有效。但如果另一个会话的字段排序规则是utf8mb4_bin两个表关联查询时MySQL 需要把两边的排序规则统一才能比较这个过程可能无法直接使用索引导致全表扫描。这个问题的本质是排序规则不一致的字段之间做等值比较或关联时MySQL 需要做隐式转换。隐式转换意味着计算计算意味着放弃索引。我做过一次压测同样的关联查询两边排序规则一致时执行耗时 50ms不一致时涨到 850ms差距非常明显。查询执行效率方面general_ci在 5.7 时代确实比unicode_ci快但 8.0 的0900_ai_ci用了新算法性能和准确性兼得。所以如果数据库是 8.0直接在默认值上构建业务逻辑不用纠结性能问题。提示检查字段排序规则最直接的方式是执行SHOW FULL COLUMNS FROM 表名;结果里会列出每个字段的 Collation 属性。如果某列显示为空说明它继承了表级别的排序规则。4. 实战操作建库、建表、修改排序规则的完整方案理论讲清楚了接下来进入实操。我以一个真实模拟场景为例某电商系统需要新建一张商品表要求商品名称支持中文搜索、排序行为符合语言习惯、分类编码大小写敏感精确匹配。我将从建库开始完整演示每一步操作和背后的思考。4.1 第一步建库时指定字符集和排序规则建库语句的完整写法是CREATE DATABASE IF NOT EXISTS shop_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;这个语句干了三件事指定字符集为utf8mb4指定排序规则为utf8mb4_0900_ai_ci如果库已存在则不重复创建。指定字符集为utf8mb4的目的是确保能存下中文、Emoji、特殊符号避免后期出现“???”乱码问题。指定排序规则为utf8mb4_0900_ai_ci是让库内所有新建表默认继承这个排序规则省得每张表单独指定。这里注意一个坑如果不写 DEFAULT COLLATEMySQL 会用字符集对应的默认排序规则。MySQL 8.0 的utf8mb4默认就是0900_ai_ci但如果你用的是 5.7默认是general_ci。所以建库时显式写清楚是最稳妥的做法。4.2 第二步建表时按字段维度设置排序规则建表语句里可以同时设置表级默认规则和字段级独立规则。以下是一个示范CREATE TABLE product ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, product_code VARCHAR(32) NOT NULL COLLATE utf8mb4_bin, product_name VARCHAR(128) NOT NULL, category_code VARCHAR(16) NOT NULL COLLATE utf8mb4_bin, description TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_product_code (product_code), KEY idx_name (product_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;这个建表语句里有两个字段单独指定了utf8mb4_binproduct_code和category_code。原因很简单商品编码和分类编码是系统内部使用的业务标识比较时必须精确匹配不能因为大小写问题导致两个不同的编码被认为是同一个。而product_name没有单独指定继承了表级别的utf8mb4_0900_ai_ci这样用户搜索商品名称时“nike” 和 “Nike” 能被同等对待搜索体验更友好。表级别的排序规则写在了最后DEFAULT CHARSET和COLLATE分别是表和字段的兜底配置。建表时设置一次后续所有字段自动继承。4.3 第三步修改已有表的排序规则老表想换排序规则用 ALTER TABLE 语句。但这里的坑比想象中多不建议一把梭直接改全表。最安全的做法是先观察评估再分批修改。比如想把商品表的名称字段单独改成utf8mb4_0900_ai_ciALTER TABLE product MODIFY product_name VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;这条语句执行后会对 product_name 字段做一个全表重写把原有字符串按新排序规则重新组织索引。如果表有几百万行数据这个操作可能耗时几十秒甚至几分钟期间会产生锁影响线上读写。这也是改排序规则最容易被忽视的坑不是改个默认值的事而是重建索引的大工程。实操建议分三步走先通过SHOW FULL COLUMNS FROM product;确认当前字段的排序规则和字符集业务低峰期执行 ALTER TABLE避免锁表影响高峰期流量执行后用EXPLAIN验证关键查询的索引使用情况4.4 第四步验证排序规则的效果修改完以后建议用几条 SQL 现场验证行为是否符合预期。测试大小写敏感度SELECT a A COLLATE utf8mb4_bin; -- 结果为 0表示不相等大小写敏感 SELECT a A COLLATE utf8mb4_0900_ai_ci; -- 结果为 1表示相等大小写不敏感测试重音敏感度SELECT cafe café COLLATE utf8mb4_0900_ai_ci; -- 结果为 1表示相等重音不敏感 SELECT cafe café COLLATE utf8mb4_bin; -- 结果为 0表示不相等重音敏感这个验证过程特别实用能直观看到不同排序规则对同一组数据的不同判断结果。项目上线前跑一遍这个验证可以避免很多线上才发现的行为差异问题。5. 排序规则引发的真实事故问题排查与避坑记录这块内容是我最想写的部分。理论讲过、操作也演示过但真正让一个开发者记住排序规则重要性的永远是线上事故。我把这些年遇到过的、和排序规则相关的真实问题做了整理按问题现象、排查思路、解决方案三条线逐一拆解。5.1 事故一大小写不敏感导致登录撞车某系统上线后运营反馈出现了一个诡异问题用户 A 的账号叫Admin用户 B 用账号admin注册系统提示账号已存在但用户 A 用admin登录居然也能登进去进的是用户 A 的账号。排查后发现用户表用户名字段的排序规则是utf8mb4_general_ci。在这个规则下Admin和admin被判定为相等的字符串。登录时WHERE username admin匹配到了Admin这一行于是登进了 A 的账号。这个问题的根因是业务需求和排序规则行为不一致。用户名本来应该是大小写敏感的但建表时用了默认的大小写不敏感规则。解决方案是把用户名和邮箱字段改为utf8mb4_binALTER TABLE user MODIFY username VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;这个案例的教训非常深刻业务上对大小写敏感性的要求必须映射到字符串字段的排序规则上否则系统行为会偏离预期。特别是登录、注册、权限校验这类场景每个字段的排序规则都要仔细推敲。5.2 事故二重音不敏感导致数据误匹配MySQL 8.0 的0900_ai_ci默认重音不敏感这个特性在搜索场景很友好但在某些需要精确匹配的业务里会引发问题。有一次某电商系统上传商品时系统从第三方接口拿到一个商品名Café库里有另一个商品叫Cafe。按预期业务逻辑这两个是不同的商品应该分别入库。但因为排序规则是0900_ai_ci唯一索引判断这两个名字相等导致上传商品时触发了唯一键冲突商品死活传不上去。排查过程花了不少时间因为正常思维里Café和Cafe就是两个不同的字符串。直到我把字段的排序规则单独拿出来测了一遍才发现0900_ai_ci的重音不敏感特性在起作用。解决方案是把商品编码、商品名这类需要精确区分的字段改成utf8mb4_0900_as_cs重音敏感、大小写敏感或者utf8mb4_binALTER TABLE product MODIFY product_name VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;经过这个事故我把团队里的建表规范改成了所有需要精确匹配的字段显式指定_bin所有面向用户的搜索字段保持默认规则。宁可建表时多写几个字也不要上线后排查半天。5.3 事故三升级 8.0 后排序规则不兼容引发迁移失败某项目组把 MySQL 从 5.7 升级到 8.0在迁移数据时碰到了排序规则不兼容的报错。原因是一个老库的排序规则是utf8mb4_general_ci而迁移工具默认生成了utf8mb4_0900_ai_ci的目标表结构两边比较行为不一致数据迁移后某些查询结果对不上。这种“迁移后结果不一致”的问题特别隐蔽不是报错、不是崩溃就是某个查询排序结果和老系统不一样。比如老系统里ORDER BY name排在某个位置的名字新系统里排到了另一个位置。用户感知不一定强但自动化脚本如果依赖特定的排序顺序就可能出错。排查后发现根因依然是排序规则差异。解决方案有两种一是保持老库的utf8mb4_general_ci不变重建表时显式指回旧排序规则二是统一迁移到utf8mb4_0900_ai_ci并完整回归测试所有关联查询。前者省事但保留了老规则的所有缺点后者一次到位但需要投入回归测试成本。我倾向于推荐后者长痛不如短痛。5.4 事故四隐式转换导致关联查询索引失效最后这个案例最有技术含量。某报表系统做多表关联查询主表是大表关联字段已经建了索引但查询总是慢得离谱。用EXPLAIN查看执行计划发现索引根本没有被使用走的是全表扫描。一步步排查下去找到了根因两个表的关联字段排序规则不一致。主表用了utf8mb4_unicode_ci从表用了utf8mb4_general_ci。MySQL 在关联比较时需要先把两边的排序规则统一成同一个这个过程无法直接使用索引。解决方案是把两边的排序规则调整为一致ALTER TABLE detail_table MODIFY order_id VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;如果你遇到类似问题可以先执行下面的 SQL 查看所有相关字段的排序规则SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA database_name AND COLUMN_NAME IN (order_id, batch_no);把可能参与关联的字段全部列出来对比一眼就能找出哪些字段排序规则不一致。这个查询语句我几乎是排障标配了建议大家收藏备用。5.5 快速排查手册三步定位排序规则问题结合上述事故我整理了一个通用排查流程遇到排序规则相关的问题直接按这个步骤走第一步确认现象类型。是等值查询结果不符合预期还是排序顺序不对还是关联查询慢得异常。不同现象对应不同的排查方向。第二步检查字段排序规则。执行SHOW FULL COLUMNS FROM 表名列出所有字段的 Collation。重点检查参与查询和关联的字段记录它们的排序规则是否一致。第三步做行为验证。用 COLLATE 关键字在 SELECT 语句中临时指定排序规则验证行为差异SELECT name FROM product WHERE name café COLLATE utf8mb4_bin; SELECT name FROM product WHERE name café COLLATE utf8mb4_0900_ai_ci;如果结果不同说明排序规则是问题根因。接下来按业务需求决定最终应该用哪个规则再执行 ALTER TABLE 修改。这个排查流程不是万能的但能解决 95% 以上的排序规则问题尤其是对刚开始接触这块内容的开发者可以省去很多瞎猜的时间。6. 自定义排序规则与更多进阶玩法基础选型讲完之后再聊聊几个更进阶的应用技巧。排序规则不只是建表时选一个就完事在实际开发中还有不少灵活的用法。6.1 单条 SQL 里临时指定排序规则有一种常见情况表里的字段排序规则是大小写不敏感的但某一次特殊查询需要大小写敏感匹配。这种情况不需要改表结构直接在 SQL 里用 COLLATE 关键字临时指定即可SELECT * FROM product WHERE product_code AbC123 COLLATE utf8mb4_bin;这条语句只对当前查询生效表结构完全不变。对比大小写不敏感的原表结构这条查询会把AbC123和abc123视为不同值满足临时精确匹配的需求。这个用法特别适合后台管理系统的特殊筛选功能不用为了个别场景把整个字段改成_bin。COLLATE 关键字也能用在 ORDER BY 上让排序行为按指定规则执行SELECT product_name FROM product ORDER BY product_name COLLATE utf8mb4_bin;这种临时指定方式灵活度很高而且不需要动表结构是处理偶发特殊需求的利器。6.2 没有重音敏感内置规则时的手动方案需要精确匹配但又不适合用_bin的字段可以考虑一个变通方案建一个专用于比较的冗余字段。比如商品显示名称用默认排序规则同时加一个product_name_normalized字段专门存格式化后的标准化字符串用于精确匹配。标准化逻辑在业务层实现去掉重音符号、统一大小写、统一全半角写入冗余字段。查询精确匹配时查这个字段显示内容时用原字段。这种方案的优点是排序规则对业务完全透明不会因为数据库版本变化或者排序规则行为差异影响业务逻辑。缺点是增加了存储成本和维护复杂度适合对精确匹配要求极高的业务。6.3 多语言场景的排序规则选择如果你的系统面向多语言用户排序规则的选择会更复杂。不同语言的排序规则在同一个排序规则下表现不一致这是 Unicode 排序算法的固有问题。一个典型的例子在utf8mb4_unicode_ci下中文字符按 Unicode 码点排序这个顺序和拼音顺序不一样。如果业务需求是中文按拼音排序数据库层面用ORDER BY是做不到的需要在业务层把汉字转成拼音再做排序。这个坑不少人踩过我当时第一次做中文排序时也被这个卡了一段时间。所以在多语言场景排序规则的定位要清晰数据库保证基础排序的稳定性和一致性复杂的语言特性排序放到业务层实现。不要指望数据库层面解决所有排序问题。6.4 用 SHOW COLLATION 做一次全面体检定期检查数据库的排序规则使用情况是个好习惯。可以写一个 SQL统计每个库每张表每个字段的排序规则分布SELECT TABLE_SCHEMA, COLLATION_NAME, COUNT(*) AS column_count FROM information_schema.COLUMNS GROUP BY TABLE_SCHEMA, COLLATION_NAME ORDER BY TABLE_SCHEMA, column_count DESC;执行后能看到每个库里用了哪些排序规则、每个规则覆盖多少字段。如果发现一个库里混了general_ci、unicode_ci、0900_ai_ci好几种就要警惕关联查询的性能问题了。这个体检脚本我每个月跑一次能提前发现很多隐患。排序规则的“全表混用”并不是罪大恶极因为不同字段可以有不同的排序规则需求。但如果混用导致关联查询性能下降就需要逐个排查并统一。这个度需要根据实际业务把握。7. 从建表开始养成好习惯一份可以直接抄的规范写了这么多最后给一份可以直接落地执行的规范清单。这些规范是我经过多轮线上事故后沉淀下来的不一定适配所有团队但大概率能帮大家避开最常见的坑。7.1 DDL 建表规范建表语句里字符集和排序规则永远写全。示例CREATE TABLE user ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(64) NOT NULL COLLATE utf8mb4_bin, email VARCHAR(128) NOT NULL COLLATE utf8mb4_bin, nickname VARCHAR(64) NOT NULL, UNIQUE KEY uk_username (username), UNIQUE KEY uk_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;用户名、邮箱单独指定_bin确保登录、注册时大小写精确匹配昵称用表默认的0900_ai_ci支持用户搜索时的大小写不敏感匹配。这套组合既能保证账号安全又能提供友好的搜索体验。7.2 字段排序规则速查表开发时可以直接对照参考字段类型推荐排序规则原因说明用户名、邮箱、手机号utf8mb4_bin精确匹配避免大小写混淆订单号、优惠码、邀请码utf8mb4_bin机器生成必须严格区分昵称、标题、名称utf8mb4_0900_ai_ci搜索友好忽略大小写差异富文本、描述、备注utf8mb4_0900_ai_ci不参与精确比较跟随默认哈希值、令牌ascii_bin 或 utf8mb4_bin纯 ASCII 场景用 ascii_bin 更快以上建议适用于 MySQL 8.0如果用的是 5.7把utf8mb4_0900_ai_ci替换成utf8mb4_general_ci即可_bin后缀不受版本影响。7.3 变更排序规则的决策清单当业务提出要修改某个字段的排序规则时别急着动手先过一遍下面的检查清单确认数据库版本支持目标排序规则列出所有引用该字段的查询和关联评估影响面检查是否有唯一索引依赖该排序规则确认业务对大小写敏感、重音敏感的真实需求选择低峰期执行变更变更前后执行同样的查询对比结果这个清单看起来繁琐但每一条都能避免一次潜在事故。尤其是第 4 条很多人容易拍脑袋决定实际上没有真正理解业务需求的敏感性要求。我自己在刚开始折腾 MySQL 排序规则的时候也走了不少弯路很多知识都是零散搜到的没有一个系统性的梳理。这篇文章算是把这些年踩过的坑、积累的经验完整整理了一遍。数据库的排序规则虽然是个小知识点但影响范围贯穿建表、查询、索引、迁移所有环节。搞清楚它不仅能答上同事的提问更能提前规避一系列线上问题。如果你正准备建新表或者正要升级 MySQL 8.0花十分钟把排序规则这件事想清楚比上线后排查问题划算得多。拿我这篇文章里的建表规范直接套用至少能避开一大半常见的排序规则坑。