数据库索引是提高查询性能的关键,但如果不正确使用,反而会拖慢整体性能。本文将总结数据库索引的使用场景、不适用的情况以及导致索引失效的常见原因。
什么时候需要创建索引?
索引并非万能,但在以下场景中,创建索引能带来巨大的性能提升:
频繁作为查询条件的字段
在WHERE
子句中经常用到的字段,特别是数据量大的表中,创建索引是首选。这能让数据库从“全表扫描”变为“索引查找”,速度提升可能是数量级的。主键字段
数据库会自动为主键创建唯一索引,这不仅保证了记录的唯一性,也使得通过主键查找记录的速度极快。用于表连接(JOIN)的字段
在JOIN
操作中,连接字段(通常是外键)的匹配效率至关重要。为这些字段创建索引,可以显著加速多表查询。需要排序(ORDER BY)的字段
当查询结果需要按某个字段排序时,如果该字段有索引,数据库可以利用索引本身有序的特性,避免额外的排序操作,从而提高效率。需要分组(GROUP BY)的字段
与排序类似,对分组字段创建索引,可以帮助数据库更快地找到相同值的记录,优化分组统计的性能。需要对字段进行 DISTINCT 操作
DISTINCT
操作的本质是去重,这与GROUP BY
非常相似。为DISTINCT
字段创建索引,可以利用索引的有序性来快速识别和提取唯一值,避免全表扫描。需要保证唯一性的字段
除了主键外,如果业务上要求某个字段(如用户邮箱、手机号)的值是唯一的,可以为其创建唯一索引(Unique Index),数据库会自动拒绝重复值的插入。
什么时候不需要创建索引?
滥用索引会占用磁盘空间,并降低写操作(INSERT, UPDATE, DELETE)的性能。以下情况应避免创建索引:
表记录太少
对于只有几百条记录的小表,全表扫描的成本已经很低,甚至可能比查找索引更快。此时创建索引没有必要。数据重复度高、选择性低的字段
像“性别”、“状态”这类字段,其可能的值非常有限(例如,男/女,启用/禁用)。为这类字段创建索引,索引的“区分度”很低,查询时仍然需要扫描大量数据,无法有效提升性能。经常被修改的字段
索引并非没有代价。每次对表进行增、删、改操作时,相关的索引也需要同步更新。如果一个字段不常用于查询,但修改操作非常频繁,那么维护索引的开销可能会超过查询带来的收益。不作为查询条件的字段
如果一个字段从未或极少出现在WHERE
、JOIN
、ORDER BY
子句中,那么为它创建索引纯属浪费存储空间和计算资源。
哪些情况下索引会失效?
精心创建的索引,有时却在查询中“失踪”,这就是索引失效。以下是几种常见的“索引杀手”:
在索引列上进行计算或使用函数
这是最常见的问题。例如WHERE age + 1 = 30
或WHERE LEFT(name, 3) = 'abc'
,优化器无法直接使用age
或name
字段的索引。
正确做法:将计算和函数操作移到等号右侧,例如WHERE age = 30 - 1
。使用
LIKE
且以通配符%
开头
查询WHERE name LIKE '%关键词'
会导致索引失效,因为索引的 B-Tree 结构是按前缀排序的,无法处理前缀不确定的情况。
正确做法:尽量使用后缀匹配,如WHERE name LIKE '关键词%'
,这样可以正常使用索引。查询条件中使用
OR
如果OR
连接的条件中,有一个列没有索引,那么整个查询的索引都可能失效,优化器会选择全表扫描。
正确做法:确保OR
两边的列都有索引,或者考虑将查询拆分为两个UNION ALL
查询。查询条件中存在类型转换
如果字段phone
是VARCHAR
类型,但查询时写成WHERE phone = 13800000000
(数字类型),数据库会进行隐式类型转换,这可能导致索引失效。
正确做法:保证查询条件中的数据类型与字段类型一致,如WHERE phone = '13800000000'
。联合索引未使用最左前缀
对于一个由(col1, col2, col3)
组成的联合索引,查询必须从最左边的列开始使用。如果查询条件是WHERE col2 = 'b'
或WHERE col3 = 'c'
,则无法利用该索引。
正确做法:查询条件应遵循最左前缀原则,例如WHERE col1 = 'a'
或WHERE col1 = 'a' AND col2 = 'b'
。使用
!=
或<>
操作符
不等于操作符通常会让优化器放弃使用索引,转而进行全表扫描,因为其选择性较低。
总结
索引是一把双刃剑。合理地使用索引是数据库性能优化的核心技能之一。在创建索引之前,务必充分理解业务查询场景,并注意避免上述导致索引失效的“坑”,才能让索引发挥其最大价值。