当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 生成列索引为什么会受字符排序规则影响

MySQL 生成列索引为什么会受字符排序规则影响

来源:17golang原创 2026-09-27 06:53:56 0浏览 收藏

我排查生成列索引时,最容易把两个现象混在一起:一是 utf8mb4_0900_ai_ci 与 utf8mb4_bin 对大小写、重音的比较结果不同;二是查询虽然写了相似表达式,优化器却没有把它改写成生成列索引查找。MySQL 生成列索引不是“只要建了就一定命中”,表达式要一致,结果类型也要一致,字符串的字符集和排序规则则要固定下来。

官方资料:https://dev.mysql.com/doc/refman/8.4/en/generated-column-index-optimizations.html

先用 SHOW CREATE TABLE 看生成列的定义,再用 CHARSET()、COLLATION() 检查查询值,最后用 EXPLAIN 判断是否真的走了生成列索引。不要只凭“查询返回了正确行”判断索引生效。
要点速览
  • 排序规则先影响字符串“相不相等”,再影响表达式与查询是否具备可匹配的字符属性。
  • 生成列建议显式声明字符集和 COLLATE,JSON 字符串提取通常配合 JSON_UNQUOTE()。
  • SHOW CREATE TABLE、CHARSET/COLLATION、EXPLAIN 是一组连续的定位证据。

先区分排序规则变化和索引未命中

排序规则不是索引开关。它描述字符串比较、排序和等值判断的规则;例如不区分大小写的排序规则可能把 Abc 与 abc 看成相等,而二进制排序规则会按编码值区分它们。索引是否被采用,则还要看优化器能否把查询表达式识别为生成列定义。

这两个问题的排查顺序应该分开:先比较结果语义,再看执行计划。生成列定义为 JSON_UNQUOTE(JSON_EXTRACT(doc, '$.alias')) 时,查询若换成不同的函数顺序、不同的返回类型,或者只写了一个看似等价但实际属性不同的表达式,优化器可能无法匹配。此时即使两个写法在少量数据上返回相同结果,也不能证明索引已经被使用。

MySQL 生成列索引中字符集、排序规则与字符串比较结果的关系说明图
图1:MySQL 生成列索引的字符属性关系说明图,展示排序规则如何影响比较语义;这是原创静态说明图,不是运行截图。

为生成列固定字符集与排序规则

下面用 JSON 中的 alias 作为示例。重点不是 JSON 本身,而是把生成列最终暴露给索引的字符串类型写清楚。表默认值可以作为兜底,但关键列不建议依赖数据库或连接的隐式默认值。

-- 显式固定生成列的字符集和排序规则,避免依赖表默认值
CREATE TABLE customer_profile (
    id BIGINT PRIMARY KEY,
    doc JSON NOT NULL,
    alias_key VARCHAR(64)
        CHARACTER SET utf8mb4
        COLLATE utf8mb4_0900_ai_ci
        AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$.alias'))) STORED,
    INDEX idx_alias_key (alias_key)
);

-- 用一个包含大小写差异的值,观察当前排序规则下的比较语义
INSERT INTO customer_profile (id, doc)
VALUES (1, JSON_OBJECT('alias', 'Abc'));

这里使用 STORED 让生成值保存下来,索引直接建立在 alias_key 上。若业务需要区分大小写,应改成适合业务的二进制或区分大小写排序规则,并同步修改所有查询;不能只改查询常量而保留旧索引语义。

检查查询常量与表达式属性

连接字符集和排序规则会影响字符串字面量。排查时先把表定义和列属性打印出来,再用一个明确的查询值对照。CHARSET() 与 COLLATION() 只负责告诉你表达式当前拥有什么属性,不会替你修正不一致。

-- 先确认生成列和索引看到的真实定义
SHOW CREATE TABLE customer_profile;

-- 检查列表达式的字符集与排序规则
SELECT
    CHARSET(alias_key) AS key_charset,
    COLLATION(alias_key) AS key_collation
FROM customer_profile
LIMIT 1;

-- 显式让查询常量采用与生成列一致的字符属性
SELECT id
FROM customer_profile
WHERE alias_key = CONVERT('abc' USING utf8mb4)
                 COLLATE utf8mb4_0900_ai_ci;

如果业务语义是大小写不敏感,第一条查询可能返回插入的 Abc;如果切换到 utf8mb4_bin,结果就可能不同。迁移中最危险的不是某一个排序规则“更快”,而是写入、索引定义和读取端对相等的理解不一致。

用 EXPLAIN 判断生成列索引是否可匹配

MySQL 官方文档说明,优化器会在查询表达式与生成列定义一致、并且结果类型相同的情况下考虑生成列索引。等值、范围、BETWEEN 和 IN() 等操作还各有匹配限制,所以要看计划而不是猜。

-- 直接引用生成列,先建立一个最清晰的基线计划
EXPLAIN SELECT id
FROM customer_profile
WHERE alias_key = CONVERT('abc' USING utf8mb4)
                 COLLATE utf8mb4_0900_ai_ci;

-- JSON 提取表达式要与生成列定义保持同样的结构
EXPLAIN SELECT id
FROM customer_profile
WHERE JSON_UNQUOTE(JSON_EXTRACT(doc, '$.alias')) =
      CONVERT('abc' USING utf8mb4) COLLATE utf8mb4_0900_ai_ci;

-- 查看优化器是否把表达式替换成了生成列
SHOW WARNINGS;

观察结果时关注 possible_keys、key 和扩展 EXPLAIN 的改写信息。若 possible_keys 没有 idx_alias_key,优先检查函数结构、返回类型和字符属性;若候选索引存在但 key 为空,再结合选择性、统计信息和其他索引判断成本,而不是继续修改 COLLATE。

MySQL 生成列表达式与 EXPLAIN 计划匹配关系说明图
图2:生成列表达式、查询谓词与 EXPLAIN 计划的匹配关系说明图,展示可匹配与需回查的边界;这是原创静态说明图,不是运行截图。

把迁移和回归边界写进清单

生产修改前可以按下面的顺序留证:第一,保存 SHOW CREATE TABLE,确认生成列的字符集、排序规则和 STORED/VIRTUAL 属性;第二,为大小写、重音、空字符串和 NULL 准备最小数据集;第三,在应用连接池的真实字符集设置下执行参数化查询;第四,对直接引用生成列和重复表达式分别跑 EXPLAIN。

现象优先检查处理方向
返回行数与预期不同列与常量的 COLLATE、大小写/重音规则统一字符集与排序规则,重新确认业务相等语义
possible_keys 没有生成列索引表达式结构、JSON_UNQUOTE、结果类型让查询表达式与生成列定义保持一致
possible_keys 有但 key 为空选择性、统计信息、其他索引成本结合 EXPLAIN ANALYZE 或索引统计继续判断

一句话总结:字符排序规则决定字符串怎么比较,表达式一致性决定生成列索引能否被优化器识别。把两条线分别验证,再把字符属性写进列定义和回归用例,问题就不会停留在“索引明明存在却没生效”的猜测上。

常见问题

只在查询里加 COLLATE,能修复生成列索引不命中吗?

不一定。它可能修正比较语义,但如果函数结构或结果类型仍与生成列定义不同,优化器依然可能无法匹配。应先对照 SHOW CREATE TABLE 和 EXPLAIN。

为什么 JSON_EXTRACT 后通常还要 JSON_UNQUOTE?

JSON_EXTRACT 返回的字符串带 JSON 引号语义,直接用于索引查找时可能和普通字符串比较的表达式不同。把 JSON_UNQUOTE 写进生成列定义,可以让索引键与字符串查询更直接地对齐。

生成列改了排序规则后要不要重建索引?

如果列定义或结果属性发生变化,应按 DDL 变更方案重建或重新生成相关索引,并重新跑大小写、重音和执行计划回归;不要假设旧索引自动具备新语义。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
78动漫用户协议怎么看?注册、内容发布与第三方工具边界78动漫用户协议怎么看?注册、内容发布与第三方工具边界
上一篇
78动漫用户协议怎么看?注册、内容发布与第三方工具边界
Go context.WithoutCancel 为什么 Deadline 返回不存在
下一篇
Go context.WithoutCancel 为什么 Deadline 返回不存在
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    231次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    276次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    246次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    227次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    27次使用