【第6天】每天一个MySQL知识点,百日打怪升级

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
云数据库 PolarDB MySQL 版,列存表分析加速 8核16GB
RDS AI 助手,专业版
简介: 本文为DBA老兵总结的索引优化实战指南:聚焦“何时建、何时不建”核心问题。详解索引选择性(唯一值/总行数)、失效场景(低区分度、函数运算、隐式转换)及建索引黄金法则——WHERE/JOIN/ORDER BY/GROUP BY高频字段优先,状态类、低选择性列坚决不建。附EXPLAIN实战分析与AI辅助诊断技巧。(239字)

索引优化:何时建索引、何时不建


大家好,我是一名拥有10年以上经验的DBA老兵。

做这个系列,源于一个朴素的愿望:把踩过的坑、总结的经验系统化输出,希望能帮到刚入行或想进阶的兄弟们。

让我们开始今天的第6天内容。


🎯 面试考点

  • 索引在哪些场景下会失效?
  • 如何判断一个字段是否需要建索引?
  • 索引的选择性(Cardinality)是什么?如何计算?
  • 为什么有些索引加了等于没加?

背景引入

💡 说白了:索引不是你想加,想加就能加

你有没有遇到过这种情况:

  • 明明加了索引,查询还是慢得像蜗牛
  • 表才几万条数据,一跑查询就卡死
  • 开发说"给我加个索引",加完发现然并卵

说实话,90%的索引问题,都源于两个问题:不该加的加了,该加的没加

今天的目标:学会判断什么时候该建索引,什么时候不该建


核心概念

什么是索引的选择性?

💡 说白了:选择性,就是看这一列的数据"有多不一样"

选择性 = 唯一值数量 / 总行数

  • 性别:只有"男""女"两个值,选择性 = 2/N ≈ 0
  • 手机号:几乎每个人都不一样,选择性 ≈ 1

面试必问

  • 什么是索引的选择性?
  • 如何计算索引的选择性?
  • 选择性低的索引为什么无效?

面试解答

Q: 什么是索引的选择性?

索引的选择性是指索引列中不重复值的数量与总行数的比值。选择性越接近1,说明数据越分散,索引效率越高;选择性越接近0,说明数据越集中,索引效率越低。

Q: 选择性低的索引为什么无效?

当选择性很低时(比如性别字段只有男女两个值),MySQL优化器倾向于全表扫描而不是走索引。因为索引的B+树只是为了快速定位一小部分数据,如果索引返回的数据量超过全表的20%-30%,优化器就认为全表扫描更划算。


何时该建索引?

记住这个公式:WHERE + JOIN + ORDER BY + GROUP BY = 索引列

场景 是否该建 原因
WHERE条件常用 ✅ 必须建 快速定位数据
JOIN关联字段 ✅ 必须建 减少嵌套循环次数
ORDER BY排序 ✅ 必须建 避免filesort
GROUP BY分组 ✅ 必须建 避免临时表
区分度高的列 ✅ 优先建 选择性接近1
区分度低的列 ❌ 别建 浪费空间,没效果

何时不该建索引?

⚠️ 真实踩过的坑:这两个坑,我踩过不下10次

坑1:区分度低的列建索引

-- 性别字段,只有"男""女""未知"
CREATE INDEX idx_gender ON user(gender);  -- 别建!选择性≈0

-- 手机号,每个用户都不同
CREATE INDEX idx_phone ON user(phone);   -- 必须建!选择性≈1

坑2:索引列参与了运算

-- 错误:索引列用了函数
SELECT * FROM user WHERE YEAR(create_time) = 2026;

-- 正确:索引列单独使用
SELECT * FROM user WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01';

实战案例

场景一:验证选择性对索引的影响

创建测试表,插入10万条数据,分别在性别和手机号上建索引,用EXPLAIN查看执行计划的区别

-- 创建测试表
CREATE TABLE test_index (
    id INT PRIMARY KEY AUTO_INCREMENT,
    gender VARCHAR(10),
    phone VARCHAR(20),
    create_time DATETIME
);

-- 插入10万条测试数据(MySQL 8.0+ 递归CTE)
INSERT INTO test_index (gender, phone, create_time)
WITH RECURSIVE cte AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM cte WHERE n < 100000
)
SELECT 
    CASE WHEN RAND() < 0.5 THEN '男' ELSE '女' END,
    CONCAT('138', LPAD(FLOOR(RAND() * 100000000), 8, '0')),
    DATE_ADD('2026-01-01', INTERVAL FLOOR(RAND() * 365) DAY)
FROM cte;

-- 查看数据分布
SELECT gender, COUNT(*) as cnt FROM test_index GROUP BY gender;

结果预判

  • gender 字段只有两种值,选择性≈2/100000=0.00002,EXPLAIN 会显示全表扫描
  • phone 字段唯一值多,选择性≈1,会走索引

场景二:索引列参与运算导致失效

对比索引在有运算和无运算下的执行计划

ALTER TABLE test_index ADD INDEX `IDX_CREATE_TIME` (`create_time`);

-- 测试1:索引列参与运算(失效)
EXPLAIN SELECT * FROM test_index WHERE YEAR(create_time) = 2026;
+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | test_index | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100000 |    100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+--------+----------+-------------+

-- 测试2:索引列单独使用(生效)
EXPLAIN SELECT * FROM test_index WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01';
+----+-------------+------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+
| id | select_type | table      | partitions | type  | possible_keys   | key             | key_len | ref  | rows   | filtered | Extra                 |
+----+-------------+------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+
|  1 | SIMPLE      | test_index | NULL       | range | IDX_CREATE_TIME | IDX_CREATE_TIME | 6       | NULL | 100000 |   100.00 | Using index condition |
+----+-------------+------------+------------+-------+-----------------+-----------------+---------+------+--------+----------+-----------------------+

结果分析

  • 测试1:type=ALL(全表扫描),索引完全失效
  • 测试2:type=range(走索引),会根据实际扫描的数据量,动态选择是否使用索引,如果匹配到的数据很多效率同样不高

💡 AI 辅助实战

🤖 AI 能不能帮我判断索引有没有生效?

能!通过 MCP(Model Context Protocol)连接数据库,批量执行 EXPLAIN,让大模型分析执行计划:

mcp-db-analyzer 使用姿势

这是一个开源的多数据库分析 MCP Server,支持:

  • 检查索引问题(detect index problems)
  • 分析查询计划(explain query plans)
  • 检测表膨胀(analyze table bloat)

部署方式

# 通过 npx 安装
npx -y mcp-db-analyzer

配合 AI 使用

提示词示例:

"请帮我分析以下 SQL 的执行计划,识别性能问题:SELECT * FROM orders WHERE status=1 AND create_time > '2026-01-01'"

AI 会返回:
- EXPLAIN 结果:type=ALL, rows=500000, Using filesort
- 问题:全表扫描 + 文件排序
- 建议:给 status + create_time 建联合索引

避坑指南

⚠️ 真实踩过的坑:

  1. 不要给"状态类"字段建索引

    • 状态字段只有0/1、已读/未读等几种值
    • 选择性接近0,加了也是全表扫描
    • 建议:业务层面过滤,不要依赖索引
  2. 不要在长字符串上建全文索引之外的索引

    • 几百个字符的TEXT字段,索引体积太大
    • 建议:用前缀索引,或者全文索引
  3. 联合索引注意最左前缀

    • 建立了(a,b,c)索引,相当于有(a)、(a,b)、(a,b,c)三种索引
    • 但不能用(b,c),否则索引失效
    • 建议:把区分度高的放前面

思考题

🤔 互动时间:

  1. 如果一个表有1000万条数据,其中"状态"字段只有"启用"和"禁用"两种值,应该给状态字段建索引吗?
  2. 如果你发现某个查询很慢,但是WHERE条件涉及的字段都已经建了索引,可能是什么原因?
  3. 如何快速查看一个字段的选择性?

总结

🎯 面试考点

  • 索引选择性:唯一值/总行数,越接近1越好
  • 失效场景:区分度低、索引列参与运算、隐式类型转换
  • 建索引原则:WHERE/JOIN/ORDER BY/GROUP BY常用字段优先建,区分度低的别建
  • 避坑指南:状态类字段、长字符串、联合索引最左前缀

下期预告:SQL优化工具推荐 —— 面试必问!
全本合集《每天一个MySQL知识点,百日打怪升级》 您的关注是我每日更新的动力


有问题欢迎评论区交流,明天见!

相关文章
|
1月前
|
人工智能 缓存 前端开发
Day4-5:Web 双端适配与 Admin 系统全栈落地实录
本文档整合了 Day 4 与 Day 5 的开发进展,核心涵盖 Web 端响应式 UI 复现、云端资讯 API 接入,以及 Admin 管理系统的架构设计与模块化开发步骤,打通了从用户体验到后台管理的完整链路。
|
1月前
|
数据采集 机器学习/深度学习 自然语言处理
大模型应用:大模型时代的XGBoost:传统梯度提升树与大模型的协同应用.103
本文详解XGBoost与大模型融合方法:利用大模型提取文本等非结构化数据的语义Embedding,与XGBoost处理的结构化特征拼接建模,兼顾高精度、强可解释性与语义理解能力,显著提升预测性能。
352 2
|
1月前
|
安全 Linux 网络安全
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
152 5
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
|
1月前
|
数据采集 自然语言处理 算法
可计算元认知文本分析:肿瘤生物物理学语义基线的构建与边界信号检测
本研究首次为肿瘤生物物理学提供可计算的语义基线,揭示该学科围绕力学信号与细胞行为的核心知识结构,并量化了力学/黏附/成像阈值作为学科边界信号。相比传统综述,本工作从“学科如何说话”的元认知视角实现了可复现、可扩展、跨层次对齐的计量基准,为肿瘤生物物理学在精准医学、组织工程及材料科学中的跨学科协作提供了方法学支撑。
|
1月前
|
人工智能 自然语言处理 前端开发
不会开发AI Skill,你明天可能还在改自动化脚本
本文探讨AI时代测试自动化范式变革:从维护脆弱脚本转向构建“AI Skill”——以意图驱动、动态定位、自适应校验的智能测试单元。揭示脚本失效根因在于抽象层次过低,并指出2024年是测试工程师能力分水岭:定义Skill者驾驭AI,仅修脚本者将被替代。
|
1月前
|
人工智能 JSON 测试技术
AI 都会写代码了,自动化测试还值得学吗?
AI能写自动化脚本,但无法替代测试人的核心能力:懂业务、会设计、善定位、精维护。自动化测试的本质是“做测试”,而非“写代码”。越依赖AI,越需夯实测试设计、工程架构与质量保障体系能力——这才是不可替代的竞争力。
|
29天前
|
人工智能
他能看见所有年纪的我,却触碰不到任何一个《星际穿越》——撰写意图共鸣科技
《幽灵家书》:一曲跨越维度的父爱史诗。十岁墨菲目睹“幽灵”推书留信“STAY”,终其一生破译父亲库珀在五维空间用引力拨动秒针写就的时空家书——那不是神迹,而是人类以爱为引力,自我拯救的壮丽闭环。
171 3
|
29天前
|
数据采集 JSON 缓存
【剪映小助手】链接提取接口
本接口为链接提取服务,当前版本采用直通逻辑:接收字符串输入(output),原样返回,不进行实际链接识别。接口基于FastAPI构建,具备标准请求/响应模型、统一异常处理与日志机制,架构清晰、扩展性强,专为后续集成链接识别能力预留接口规范。(239字)
|
1月前
|
边缘计算 缓存 安全
AIWCLOUD:免备案CDN,大陆节点,在远程浏览器隔离(RBI)场景下
本文提出一种专为远程浏览器隔离(RBI)设计的免备案CDN架构,通过QUIC多路复用、矢量绘制指令边缘缓存与差分压缩、JWT令牌及设备指纹边缘验证、mTLS加密与DLP过滤,解决跨境RBI卡顿与安全矛盾,在未备案前提下构建低延迟、高安全的“云端浏览通道”。
167 3
|
1月前
|
存储 人工智能 关系型数据库
【第5天】每天一个MySQL知识点,百日打怪升级
本文是MySQL索引核心篇,由10年经验DBA精讲B+树原理。深入剖析为何选B+树而非B树/二叉树/哈希表,详解其非叶节点仅存key、叶子双向链表、聚簇与二级索引结构,并结合回表、覆盖索引、页分裂等实战场景,直击面试高频考点。(239字)
188 2