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

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 本文为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知识点,百日打怪升级》 您的关注是我每日更新的动力


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

相关文章
|
2月前
|
人工智能 缓存 前端开发
Day4-5:Web 双端适配与 Admin 系统全栈落地实录
本文档整合了 Day 4 与 Day 5 的开发进展,核心涵盖 Web 端响应式 UI 复现、云端资讯 API 接入,以及 Admin 管理系统的架构设计与模块化开发步骤,打通了从用户体验到后台管理的完整链路。
|
2月前
|
数据采集 机器学习/深度学习 自然语言处理
大模型应用:大模型时代的XGBoost:传统梯度提升树与大模型的协同应用.103
本文详解XGBoost与大模型融合方法:利用大模型提取文本等非结构化数据的语义Embedding,与XGBoost处理的结构化特征拼接建模,兼顾高精度、强可解释性与语义理解能力,显著提升预测性能。
425 2
|
2月前
|
安全 Linux 网络安全
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
194 5
Metasploit Framework 6.4.132 (macOS, Linux, Windows) - 开源渗透测试框架
|
4月前
|
人工智能 关系型数据库 分布式数据库
阿里云瑶池 Data+AI 客户实践案例合集
阿里云瑶池数据库凭借云原生架构、多模融合能力与全栈技术优势,已为金融、游戏、电商、物流、内容科技等千行百业提供“量体裁衣”的 Data+AI 解决方案。本文精选多个行业标杆案例,揭秘哔哩哔哩、申通快递、鹰角网络、知乎等企业如何通过阿里云数据库实现业务突破,为更多企业提供“数据库+AI”转型的实践参考。
|
2月前
|
SQL 算法 关系型数据库
【MySQL百日打怪升级第10天】JOIN的底层原理与优化:NLJ、Hash Join 与 Merge Join
本文系统解析MySQL三大JOIN算法:NLJ(含Simple/Index/Block变体)、8.0.18引入的Hash Join(O(N+M)复杂度,专治无索引大表连接),以及面试常考但MySQL原生不支持的Sort-Merge Join,附实战EXPLAIN识别与优化指南。(239字)
289 5
|
21天前
|
存储 数据采集 消息中间件
从API调用到指标输出:AI回答采集系统的数据链路设计
本文详解AI回答采集系统的端到端数据链路设计,涵盖API调用、原始存储、清洗过滤、品牌识别、别名归一化及指标计算等关键环节,确保从杂乱文本到可信指标的可靠转化。
70 1
|
2月前
|
SQL 人工智能 关系型数据库
【第4天】每天一个MySQL知识点,百日打怪升级
本系列由10年经验DBA精心打造,系统梳理MySQL客户端常用命令:从连接参数(-u/-p/-h/-P/字符集)、快捷指令(\s/\q/\G)、数据库/表操作(SHOW/CREATE/DROP/DESC),到状态监控(PROCESSLIST/STATUS/VARIABLES)与实战排障技巧,兼顾面试考点与生产避坑,助你快速上手、底气十足。
227 2
|
2月前
|
人工智能 关系型数据库 MySQL
【第3天】每天一个MySQL知识点,百日打怪升级
本系列由10年经验DBA主理,系统讲解MySQL安装(RPM/二进制/源码)与核心配置,涵盖`my.cnf`优先级、`innodb_buffer_pool_size`调优、连接与日志参数设置、四种生效方式对比,并附生产避坑指南与面试高频考点,助力快速入门与实战进阶。(239字)
250 2
|
3月前
|
存储 人工智能 关系型数据库
【第2天】每天一个MySQL知识点,百日打怪升级
【MySQL第2天】深入解析InnoDB与MyISAM核心差异:事务支持、行锁vs表锁、崩溃恢复、外键及适用场景。10年DBA实战总结,助你避开选型陷阱,面试稳拿分!无脑选InnoDB,除非只读/日志等特殊需求。(239字)
259 3
|
2月前
|
人工智能 自然语言处理 前端开发
不会开发AI Skill,你明天可能还在改自动化脚本
本文探讨AI时代测试自动化范式变革:从维护脆弱脚本转向构建“AI Skill”——以意图驱动、动态定位、自适应校验的智能测试单元。揭示脚本失效根因在于抽象层次过低,并指出2024年是测试工程师能力分水岭:定义Skill者驾驭AI,仅修脚本者将被替代。