MySQL索引优化:从入门到实战的保姆级指南

🌟 MySQL索引优化:从入门到实战的保姆级指南


🔥 开篇必读:为什么你的索引总是无效?

真实案例:某电商平台订单查询从12秒优化到0.3秒的惊险历程
某程序员给order_no字段添加了索引,但查询依然卡顿。
问题根源order_novarchar(64)且包含时间前缀,索引长度高达20字节。
解决方案:改用bigint自增主键 + 二级索引,性能提升40倍!

本文学完你将掌握
✅ 索引底层B+树的工作原理(动画演示)
✅ EXPLAIN执行计划的20个关键细节
✅ 6大真实场景的优化方案(附可运行SQL)


📌 一、B+树索引的底层机密(图解版)

1.1 为什么索引字段越小越好?

原理剖析

  • 每个B+树节点默认16KB,可存储更多小字段
  • 对比实验:
-- 方案1:使用varchar(255)做主键  
CREATE TABLE t1 (id varchar(255) PRIMARY KEY, ...);  

-- 方案2:使用bigint做主键  
CREATE TABLE t2 (id bigint PRIMARY KEY, ...);  

性能差距:百万数据量下查询速度相差5倍!

1.2 最左前缀原则的5大误区

误区1:联合索引顺序不影响查询

-- 索引 (a,b,c)  
WHERE b=1 AND a=2  -- ✅ 命中  
WHERE c=1 AND b=2  -- ❌ 未命中  

误区2:范围查询后所有列失效

-- 索引 (age,score,city)  
WHERE age>18 AND score=90  -- ✅ score仍可用  

误区3:like模糊查询必定失效

-- 索引 (name)  
WHERE name LIKE '张%'  -- ✅ 命中  
WHERE name LIKE '%张'  -- ❌ 失效  

💣 二、索引失效的12种高危场景(附解决方案)

2.1 隐式类型转换

-- user_id是varchar类型  
SELECT * FROM users WHERE user_id = 10086;  -- ❌ 触发CAST转换  

修复方案

SELECT * FROM users WHERE user_id = '10086';  -- ✅  

2.2 对索引列使用函数

-- create_time是datetime类型  
SELECT * FROM logs WHERE DATE(create_time) = '2023-07-15';  -- ❌  

优化方案

SELECT * FROM logs  
WHERE create_time BETWEEN '2023-07-15 00:00:00' AND '2023-07-15 23:59:59';  -- ✅  

2.3 OR条件短路

-- 索引 (age)  
SELECT * FROM students WHERE age=18 OR name='张三';  -- ❌  

优化方案

SELECT * FROM students WHERE age=18  
UNION  
SELECT * FROM students WHERE name='张三';  -- ✅  

完整失效清单

  1. 使用!=<>操作符
  2. 联合索引跳过最左列
  3. 对索引列进行运算
  4. 使用NOT EXISTS子查询
  5. 不同字符集比较
  6. 使用%开头的LIKE查询
  7. 字段类型不匹配
  8. OR连接非索引列
  9. 使用全表扫描提示
  10. 索引列参与表达式
  11. 使用NOT IN条件
  12. 低选择性索引(如性别字段)

🚀 三、性能翻倍的索引设计模板

3.1 三星索引设计法(电商案例)

场景:订单表高频查询

SELECT order_id, total_price, status  
FROM orders  
WHERE user_id=123  
AND create_time > '2023-01-01'  
ORDER BY create_time DESC  
LIMIT 10;  

索引设计

ALTER TABLE orders ADD INDEX idx_usr_time_status (user_id, create_time DESC, status);  

三星达成

  1. WHERE条件⭐:user_id和create_time精准匹配
  2. ORDER BY⭐:create_time排序消除filesort
  3. SELECT⭐:覆盖所有查询字段

3.2 覆盖索引优化技巧

普通查询

EXPLAIN SELECT * FROM products WHERE category='手机';  
-- type: ref, Extra: Using index condition  

覆盖索引优化

ALTER TABLE products ADD INDEX idx_cat (category, name, price);  
EXPLAIN SELECT name, price FROM products WHERE category='手机';  
-- type: ref, Extra: Using index  

性能对比

查询类型执行时间扫描行数
回表查询8.2ms10,326
覆盖索引0.9ms10,326

🛠️ 四、实战:500万数据的分页优化

4.1 传统分页的致命缺陷

SELECT * FROM user_logs  
ORDER BY create_time DESC  
LIMIT 4800000, 20;  -- 执行时间12秒  

4.2 游标分页法

SELECT * FROM user_logs  
WHERE create_time < '2023-07-01 00:00:00'  
ORDER BY create_time DESC  
LIMIT 20;  -- 执行时间0.2秒  

4.3 延迟关联法

SELECT t1.* FROM user_logs t1  
JOIN (  
  SELECT id FROM user_logs  
  ORDER BY create_time DESC  
  LIMIT 4800000, 20  
) t2 ON t1.id = t2.id;  -- 执行时间1.8秒  

🔍 五、索引健康检查清单(每日必做)

  1. 检查冗余索引
SELECT * FROM sys.schema_redundant_indexes;  
  1. 监控未使用索引
SELECT * FROM sys.schema_unused_indexes;  
  1. 索引统计信息更新
ANALYZE TABLE orders;  

工具推荐

  • pt-index-usage:分析慢查询日志
  • pt-duplicate-key-checker:查找重复索引

💬 互动:你的索踩过哪些坑?

评论区高频问题精选
Q:UUID做主键真的性能很差吗?
A:实测v4 UUID比自增ID插入速度慢3倍,存储空间多20字节

Q:全文索引和like模糊查询哪个快?
A:10万数据量下,MATCH…AGAINST比LIKE快15倍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值