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

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS Agent(兼容OpenClaw),2核4GB
简介: 本文为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知识点,百日打怪升级》 您的关注是我每日更新的动力


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

相关文章
|
11天前
|
人工智能 缓存 前端开发
Day4-5:Web 双端适配与 Admin 系统全栈落地实录
本文档整合了 Day 4 与 Day 5 的开发进展,核心涵盖 Web 端响应式 UI 复现、云端资讯 API 接入,以及 Admin 管理系统的架构设计与模块化开发步骤,打通了从用户体验到后台管理的完整链路。
|
16天前
|
SQL 关系型数据库 MySQL
EXPLAIN 执行计划:一眼看穿你的SQL慢在哪
数据库小学妹带你轻松掌握SQL性能诊断!通过EXPLAIN查看执行计划,精准识别索引失效、全表扫描(ALL)、key为NULL等瓶颈。聚焦type、key、rows等6个关键字段,结合实战案例与避坑指南(如函数滥用、最左前缀破坏),让优化有的放矢。学完即用,告别盲目调优!
|
11天前
|
数据采集 机器学习/深度学习 自然语言处理
大模型应用:大模型时代的XGBoost:传统梯度提升树与大模型的协同应用.103
本文详解XGBoost与大模型融合方法:利用大模型提取文本等非结构化数据的语义Embedding,与XGBoost处理的结构化特征拼接建模,兼顾高精度、强可解释性与语义理解能力,显著提升预测性能。
179 2
|
19天前
|
存储 算法 数据挖掘
【数据库】向量数据库:核心原理、主流产品(Milvus、Pinecone)、索引类型(IVF、HNSW)、RAG中的应用
本文系统构建向量数据库完整知识体系:从基础定义、核心原理(ANN检索、存算分离架构)、主流索引(IVF/HNSW深度对比)、主流产品(Milvus/Pinecone等选型指南),到RAG落地实践与前沿趋势,兼顾理论深度与工程实战,助力高效构建企业级语义检索系统。
|
12天前
|
人工智能 运维 架构师
我在 AIP 智能体平台踩过的坑,都在这篇企业 AI 落地经验里了
软件架构师罗小东分享企业AI落地实战经验:聚焦AIP智能体平台建设中的真实坑点与解法——涵盖智能体全生命周期管理、多源知识库语义检索、MCP工具集成及多模型中立架构设计,强调“解决问题”而非堆砌功能。(239字)
|
11天前
|
人工智能 自然语言处理 前端开发
不会开发AI Skill,你明天可能还在改自动化脚本
本文探讨AI时代测试自动化范式变革:从维护脆弱脚本转向构建“AI Skill”——以意图驱动、动态定位、自适应校验的智能测试单元。揭示脚本失效根因在于抽象层次过低,并指出2024年是测试工程师能力分水岭:定义Skill者驾驭AI,仅修脚本者将被替代。
|
11天前
|
人工智能 API iOS开发
阿里云/本地零基础喂饭级部署 Hermes Agent / OpenClaw +配置免费大模型API+集成Obsidian CLI,让AI用你的知识库创作
2026年,个人知识库的价值已从“个人备忘”升级为“AI创作资产”——Obsidian凭借本地Markdown存储、双向链接、标签体系,成为知识管理的首选工具,但长期以来存在一个核心痛点:AI Agent(如OpenClaw)无法高效复用这些精心整理的知识。传统方案需将笔记向量化存入向量数据库,不仅丢失笔记结构与关系,还存在检索不透明、维护复杂等问题。
265 0
|
11天前
|
人工智能 开发工具 开发者
[理论篇-9]Skill系统与能力封装
用最直白的话讲清楚 **Skill(技能)** 是什么、为什么 2025 年下半年它从一个小众概念变成了 AI 行业的新基建,以及它会怎么改变你和 AI 的相处方式——不管你是开发者、产品经理、运营、还是只想让 AI 多帮自己干点活的普通用户。
162 4
|
4天前
|
人工智能 自然语言处理 安全
AI 智能体 OpenClaw Windows 安装配置与功能使用教程
OpenClaw(小龙虾)是2026年爆火的开源AI智能体,GitHub星标超28万。本教程提供Windows一键部署方案,零代码、全图形化操作,10分钟即可搭建本地数字员工,支持文件整理、浏览器自动化等办公任务,隐私安全、新手友好。(239字)
|
5天前
|
人工智能 数据可视化 网络安全
阿里云/本地一键部署OpenClaw(Clawdbot)教程
OpenClaw(原Clawdbot)作为轻量级AI自动化代理工具,2026年版本在部署灵活性上实现重大升级,既支持本地私有化部署(满足数据隐私、内网使用需求),也适配阿里云一键部署方案(兼顾便捷性与云端稳定性)。本文将完整拆解两种部署方式的核心流程,从环境准备、安装配置到功能验证,包含实操代码命令与避坑技巧,无论你是需要本地私有化部署的企业用户,还是追求零门槛的个人用户,都能快速完成OpenClaw的落地使用。
229 1