MySQL查询优化-使用索引的正确姿势

1.左向原则

所谓左向原则即where条件从左往右,应将索引字段的条件写在最左侧。

例如:某表字段actor_id创建有索引,age字段没有创建索引

where actor_id=25 and age=25 (此写法可以使用索引)

where age=25  and actor_id=25  (此写法不会使用索引)

如果是联合索引(多字段索引),应按照创建索引时的顺序,从左往右使用索引字段。不可以不使用排序在前的索引字段而直接使用排序灾后的字段。

例如:某表联合索引包含字段a1、a2、a3

可使用索引的条件写法如下:

where a1=1 (a1字段使用索引)

where a1=1 and a2=1 (a1字段和a2字段使用索引)

where a1=1 and a2=1 and a3=1(三个字段都是用索引)

where a1=1 and a3=1(注意,这个写法只有a1字段使用了索引,a3字段无法使用索引,因为a3前面还有a2字段)

其他的写法不使用索引,比如

where a2=1

where a3=1

2.避免使用多个范围查询

所谓范围查询包括:>,<,>=,<=,between等。

当使用2个或更多范围查询时,MySQL从第二个范围查询条件开始无法使用索引。

例如:where  actor_id >23 and actor_id<27 and  age >20 (actor_id字段上创建了索引,age字段没有创建索引)

这里有3个范围条件,只有第一个条件“actor_id >23”可以使用包含actor_id的索引。

还是刚才的例子如果条件改写为如下,则不能使用索引

where  age >20 and actor_id >23 and actor_id<27

第一小节提到了左向原则,这个写法违背了该原则,重要的事情要多说几遍。

这里给出一个实用小技巧,可以将范围查询当做精确查询来用,即使用in来替代范围查询(如大于,小于等),并将它放在范围查询条件的左侧。
举例:
where  actor_id in(24,25,26) and  age >20

本例actor_id取值范围是较少的几个值,可以用in进行枚举来替代一个范围。这个方法适用于取值范围较少的情况。

3.排序的优化

order by和limit偏移量的组合经常用在翻页。在数据量较大时,偏移量大会导致效率极低。
这因为MySQL需要花费大量的时间来扫描需要丢弃的数据。
这方面提供2个策略:
第一个策略是反范式化、预先先计算和缓存
第二个策略是使用延迟关联,通过使用覆盖索引查询返回需要的逐渐,再根据这些逐渐管理原表获得需要的行。
下面是一个使用延迟关联的例子:

优化前:
SELECT * FROM `warehouses` ORDER BY `uid` DESC LIMIT 20 OFFSET 1691580
优化后:
SELECT a.* FROM `warehouses` AS a INNER JOIN

(SELECT uid,gid FROM `warehouses` ORDER BY `uid` DESC LIMIT 20 OFFSET 1691580) AS b

WHEREa.uid=b.uid AND a.gid=b.gid

按一般理解,关联表比使用单表效率要低。然而在这种情况下却能获得效率提升。

优化后的语句执行先执行关联表部分,因为使用索引,且读取数据的字段都是索引包含字段,所以能快速获得。

之后关联获得最终数据,此时通过索引字段关联主表,这时候需要从数据页读取全部字段数据,但是只有20条,还是比较少的,速度比较快。

反观未优化的SQL,直接读取数据然后抛弃不需要的数据。如果偏移量比较大,就会导致读取大量无用数据,速度自然慢。


4.减少索引和数据碎片

B-Tree索引可能会碎片化,这会降低查询的效率。
B-Tree需要随机磁盘访问才能定位到叶子页。如果叶子页在屋里犯不上是顺序且紧密的,那么查询的性能就会更好。
可以通过optimize table或者导出再导入的方式重新整理数据。
这对多数存储引擎都是有效的。
对于不支持optimize table的引擎,可以通过一个不做任何操作的alter table操作来重建表。
例如:
alter table 表名  engine=引擎名;
这里本人有一个案例分享给大家。

我司有些表数据增长过快(初期没有采用分表策略),经考虑决定将部分数据导出,原表中保留1个月的数据。

备份原表数据后,下一步就是要移除生产环境表数据了。

最简单的方法就是delete,测试执行时间大约3个小时。delete后,还有一个现象就是底盘空间没有释放,需要再做优化。

另一个方案就是后来我实施的方案,将要保留的数据执行一次备份,然后将备份去覆盖线上的表。执行时间大约是1.5小时。

第二个方案过程其实是先drop表,然后做建表和insert。insert的速度本就比delete要快,同时整个过程避免了索引和数据的碎片化。
 

5.聚集索引

主键就是典型的聚集索引。聚集索引除了索引值外,还存储了字段值。所以如果利用好聚集索引,就可以直接从索引中取值,效率较高。

例如:某表x主键id

select id,a,b from x where id=100000

6.利用覆盖查询

select避免使用*,需要什么字段就写那个字段。这样做的目的是有机会来利用覆盖查询提高效率。

利用覆盖查询注意满足下面的要求:

a.如果返回的列,在索引中都保存了值,那么就可以达到高效查询。

例如:某表x主键id,辅助索引包含a字段,b字段

select a,b from x where a=1

select a,b from x where a=1 and b=1
b.如果返回的列除了索引条件,还包括主键,也可以达成覆盖查询的效果。因为每个二级索引都保存了主键值。

例如:

select id, a,b from x where a=1 and b=1

发布了95 篇原创文章 · 获赞 3 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/bigwood99/article/details/105305905