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

本文涉及的产品
RDS AI 助手,专业版
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 本文为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知识点,百日打怪升级》 您的关注是我每日更新的动力


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

相关文章
|
5天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23324 5
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
14天前
|
缓存 人工智能 自然语言处理
我对比了8个Claude API中转站,踩了不少坑,总结给你
本文是个人开发者耗时1周实测的8大Claude中转平台横向评测,聚焦Claude Code真实体验:以加权均价(¥/M token)、内部汇率、缓存支持、模型真实性及稳定性为核心指标。
5175 25
|
10天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
3688 12
|
9天前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
3037 10
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
26天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
20969 63
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)