0.前言
假设你经营一家电商平台,某天用户突然投诉商品搜索加载时间超过10秒。技术团队紧急排查,发现一条原本执行0.1秒的查询语句,在百万级数据量下竟变成了全表扫描。这时,数据库索引犹如深夜急诊室里的救命仪器——它的存在与否,直接决定系统是起死回生还是彻底崩溃。
索引设计的底层逻辑如同城市交通规划。想象早高峰时的十字路口,无序的车流必然引发堵塞。B+树结构通过分层导航,让数据查询像ETC通道般快速通行。某金融平台曾将交易记录的查询响应时间从8秒缩短至0.2秒,秘密就在于将单列索引改造为组合索引,如同在十字路口增设定向车道。
1.索引设计原则
-
代码先行,索引后上:给数据库表添加索引,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。
-
高频查询列:优先为 WHERE、JOIN、ORDER BY、GROUP BY 等子句中频繁使用的列创建索引。
-
避免低基数列:低基数列(如性别、状态等重复值多的列)不适合创建索引,因为它们的区分度低,索引效果不明显。尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。列的唯一性越高,索引效果越好。
-
联合索引尽量覆盖条件:对于多列查询,优先使用组合索引(多列索引),但需遵循最左前缀原则。同时将查询中使用频率最高的列放在前面,同时考虑查询的过滤性,将过滤性更强的列放在前面。
-
长字符串可以采用前缀索引:尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,在搜索的时候性能也会比较好一点。对于长字符串列(如 VARCHAR),可以使用前缀索引(如 CREATE INDEX idx_name ON users(name(10))),以节省空间。
-
选择合适的索引类型:优先使用自增整数作为主键,避免使用 UUID 等无序主键。
-
控制索引数量:索引会增加写操作(INSERT/UPDATE/DELETE)的开销。建议单表索引不超过 5个,避免冗余索引。优先建联合索引。查询时调整SQL条件顺序,使其与索引列的顺序一致。
2.索引优化
分页查询优化
很多时候我们业务系统实现分页功能可能会用如下sql实现
SELECT * FROM orders ORDER BY id LIMIT 10000, 10;
表示从表 orders 中取出从 10001 行开始的 10 行记录。看似只查询了 10 条记录,实际这条 SQL 是先读取 10010条记录,然后抛弃前 10000 条记录,然后读到后面 10 条想要的数据。因此要查询一张大表比较靠后的数据,执行效率是非常低的。
优化1:根据自增且连续的主键排序的分页查询
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;
优化2:根据非主键字段排序的分页查询
SELECT * FROM orders ORDER BY name LIMIT 10000, 10;
关键是让排序时返回的字段尽可能少,所以可以让排序和分页操作先查出主键,然后根据主键查到对应的记录,SQL改写如下:
select * from orders e inner join (select id from orders order by name limit 90000,5) ed
on e.id = ed.id;
Join关联查询优化
优化1:关联字段加索引;
优化2:优先选择小表做驱动表。
一次一行循环地从第一张表(称为驱动表)中读取行,在这行数据中取到关联字段,根据关联字段在另一张表(被驱动表)里取出满足条件的行,然后取出两张表的结果合集。
当使用left join时,左表是驱动表,右表是被驱动表,当使用right join时,右表时驱动表,左表是被驱动表,
当使用join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表。
in和exsits优化
原则:小表驱动大表,即小的数据集驱动大的数据集
in:当B表的数据集小于A表的数据集时,in优于exists
select * from A where id in (select id from B)
exists:当A表的数据集小于B表的数据集时,exists优于in
将主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留
select * from A where exists (select 1 from B where B.id = A.id)
3.最后
通过合理设计索引,可以显著提升 MySQL 的查询性能,同时减少系统资源的消耗。在实际应用中,建议根据具体场景灵活调整索引设计和查询语句,以达到最佳的性能表现。