MySQL索引的优化方式

索引是MySQL数据库中的一个重要概念,使用合理的话,可以显著提升数据库执行效率,这篇文章就介绍在创建与使用索引时的一些常见优化方式。

索引是什么?

索引是一种数据结构,通常为树或哈希表,用于数据库表中数据的快速查找。在数据库中,索引类似于书籍的目录,允许数据库引擎高效地定位到特定行的数据,而无需扫描整个表。
索引的使用就是为了提高数据查询的效率。

创建索引时要遵循的原则

使用唯一性索引

对于唯一性约束的列,使用唯一索引可以确保数据的唯一性,并提高查询性能。

CREATE UNIQUE INDEX idx_member_cdoe_unique ON t_member ( member_code );

为经常作为查询条件的字段建立索引

CREATE INDEX idx_member_name ON t_member ( member_name );

为经常需要排序、分组和联合操作的字段建立索引

对于表中常用于 JOIN、ORDER BY、GROUP BY 的列,为其创建索引。

使用复合索引

当查询条件涉及多个列时,使用复合索引可以提高查询效率。

CREATE INDEX idx_memeber_name_email ON t_member ( member_name, member_email );

限制索引的数目

索引数目不是越多越好,每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间越大,更新表时越复杂,建议单表索引控制在 5个以内。

尽量使用数据量少的索引

如果索引列的值很长,数据量很大,索引查询的速度会受影响。

尽量使用前缀来索引

如果索引字段的值很长,最好使用值的前缀来索引。较长的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。
示例:

CREATE INDEX idx_member_name_prefix ON t_member (member_name ( 10 ));

数据量小的表最好不要使用索引

由于数据量较小,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。

选择区分度高的列作为索引

区分度公式:

COUNT(DISTINCT 列名)/COUNT(*)

例如表里面有 100 条数据,表索引列有 96 个不同的值,这个列索引的区分度就是 96/100=0.96 。
索引的区分度越接近1,这个索引的效率很高,唯一键的区分度是1,效率就很高。

使用索引的注意事项与维护

避免冗余索引

创建重复或者冗余的索引会增加数据库写操作的开销。
如果已经有一个复合索引 (member_name, member_email),再单独为 member_name 创建索引就是冗余的。

避免在索引列上使用函数或表达式

索引列不能参与计算,保持“列干净”。对索引列应用函数或表达式时,数据库系统可能无法直接使用该索引来快速定位数据,从而导致全表扫描。

避免过度索引

索引虽然可以加快查询速度,也会增加写入操作(insert、update、delete)的开销,如果一个表有过多的索引,在每次插入新纪录的时候,MySQL需要更新全部的索引,会影响性能。

索引维护

定期检查,删除不再使用或很少使用的索引。

使用 EXPLAIN 分析查询

使用 EXPLAIN 命令分析查询执行计划,确保查询使用了索引。

使用索引时的常见问题与优化方式

有时搜索往往会出现数据库没有按照预期使用索引来加速查询,查询效率慢的问题,很大的概率就是 SQL语句执行索引失效导致转向全表扫描。常见导致索引失效的情况与优化方式如下:

不符合最左前缀匹配原则

最左前缀匹配原则:在使用复合索引时,该原则要求查询条件必须从索引的最左边开始匹配,并且不能跳过中间的列。
如果有一个表,对id列、name列、age列建立了一个联合索引 idx_id_name_age,实际上相当于建立了三个索引(id) (id,name) (id,name,age)
如果搜索为: SELECT * FROM user WHERE id=1; 使用 id 作为第一个查询条件。该搜索是遵循最左匹配原则的,在搜索过程中使用到了联合索引。
如果搜索为: SELECT * FROM user WHERE name = 'abc'; 或者 SELECT * FROM user WHERE age = 20; 使用 name 或者 age 作为第一个查询条件。那么没有遵守最左匹配原则,就不会使用到该复合索引了。

减少 SELECT * 的使用

具体需要展示那些列,就查询哪些列。

LIKE模糊查询不能使用索引

使用 LIKE 时注意通配符位置,不要以 % 开头的。

SELECT member_name FROM t_member WHERE member_name LIKE 'abc%';  可以使用索引。
SELECT member_name FROM t_member WHERE member_name LIKE '%abc';  索引失效,使用全表扫描,可以使用函数索引,反向索引。
SELECT member_name FROM t_member WHERE member_name LIKE '%abc%'; 索引失效,无法使用反向索引

范围查询右边的列不能使用索引

MySQL 会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。
不能使用索引中范围条件右边的列(范围列可以用到索引),范围列之后列的索引全失效,范围条件有:<、<=、>、>=、between等。

在WHERE子句中对字段进行函数操作

不要在索引列上面做任何操作(计算、函数),否则会导致索引失效而转向全表扫描。
例如:

SELECT id FROM t_member WHERE substring(member_name,1,3) = 'abc';

WHERE 子句中使用 OR 连接条件

必须保证 OR 前后的字段都有索引,索引才会生效,否则会导致索引失效而转向全表扫描。

SELECT id FROM t_member WHERE num =10 OR member_name = 'abc';

可以使用 UNION ALL 利用索引。

SELECT id FROM t_member WHERE num = 10
UNION ALL
SELECT id FROM t_member WHERE member_name = 'abc';

UNION、OR、IN 都能够命中索引,建议使用 IN

使用 UNION、OR 和 IN 都能够在一定条件下命中索引,但是从使用上说,推荐 IN 。

对于连续的数值,能用 BETWEEN 就不要用 IN

在处理连续的数值范围时, BETWEEN 可以更高效的利用索引。

负向条件查询不能使用索引

负向条件有:!=、<>、not in、not exists、not like 等。
可以使用正向条件查询,如:将 SELECT * FROM table WHERE column != 1; 转换为 SELECT * FROM table WHERE column IN (2,3);。
使用复合索引,在一些情况下,创建复合索引(多列索引)可以帮助优化查询。

IS NULL 和 IS NOT NULL 不能使用索引

尽量避免在 WHERE子句中对字段进行 NULL 值判断,否则将导致引擎放弃使用索引而进行全表扫描。实际上,IS NULL 和 IS NOT NULL 条件在某些情况下是可以使用索引的,可以使用 EXPLAIN命令查看执行计划,看看能否支持使用索引。

大量数据分页效率低问题

在具有大量数据的场景下,使用传统的LIMIT offset, N分页查询可能会导致性能问题。MySQL不会跳过前 offset 行,而是获取 offset + N 行数据,然后抛弃前 offset行,返回 N 行。这种方法在 offset特别大的时候效率非常低下。例如:

SELECT * FROM t_member LIMIT 10000,10;

该 SQL 表示查询从第 10001  开始的 10 行数据,但是实际上会先读取 10010 条记录,抛弃前 10000 条记录,然后读后面 10 条想要的数据。
可以使用延迟关联(Deferred Join)或者子查询优化超多分页场景。

子查询优化

可以先通过子查询获取需要的数据ID,再根据这个ID进行范围搜索。

SELECT a.* 
FROM t_member a 
WHERE a.id <= ( SELECT id FROM t_member ORDER BY id DESC LIMIT 10000, 1)
ORDER BY a.id DESC 
LIMIT 10;

注意:这种范围搜索要注意排序问题。

延迟关联优化

延迟关联的优化思路跟子查询的类似,先把条件转移到主键索引,减少回表。不同点就是使用关联查询代替子查询。

SELECT
  a.* 
FROM
  t_member a
  INNER JOIN ( SELECT id FROM t_member ORDER BY a.id LIMIT 10000, 10 ) AS b ON a.id = b.id;

不必要的数据转换

查询时注意字段类型与数据库表中的字段类型相匹配,避免隐式的数据类型转换。例如,字符串类型不加单引号会导致索引失效。

复杂的嵌套查询

嵌套查询(子查询)虽然功能强大,但在某些情况下会导致性能瓶颈。可以使用 JOIN 代替子查询,提升查询效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值