mysql延时关联

描述

mysql有种sql优化方式,叫延时关联,即通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据,尤其在大分页查询的场景下,可以提高查询效率。例如下面两行SQL查询语句,在使用覆盖索引的大分页查询场景下,第二条的执行速度要远远快于第一条SQL。

1
2
3
select * from table where xxx limit a,b;

select * from table where id in (select id from table where xxx limit a,b);

在覆盖索引的场景下,第一条的执行逻辑是

  1. 通过索引找到(a+b)条符合查询条件的记录id
  2. 再通过(a+b)个id回表查询这(a+b)条记录
  3. 最后按分页条件给用户返回b条记录

而第二条SQL的执行逻辑则是

  1. 通过索引找到(a+b)条符合查询条件的记录id
  2. 按分页条件取b个记录id,然后回表查询这b条记录
  3. 最后给用户返回b条记录

不难看出,第二条SQL在覆盖索引的场景下,减少了大量的回表执行次数,从而提高了执行效率。而在非索引覆盖的场景下,延时关联失效,两种SQL的执行速度没有多少区别。

测试

创建一个测试表,并插入近200万条测试数据。

1
2
3
4
5
6
7
8
9
CREATE TABLE `salary_static` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`school_id` int(11) NOT NULL COMMENT '学校id',
`student_id` int(11) NOT NULL COMMENT '毕业生id',
`salary` int(11) NOT NULL DEFAULT '0' COMMENT '毕业薪水',
`year` int(11) NOT NULL COMMENT '毕业年份',
PRIMARY KEY (`id`),
KEY `year_key` (`year`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='毕业生薪水数据统计';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
delimiter  //
CREATE PROCEDURE init_salary_static()
BEGIN
DECLARE year INT;
DECLARE schid INT;
DECLARE stuid INT;
SET year = 2000;
WHILE year < 2020 DO
START TRANSACTION;
SET schid = 1;
WHILE schid < 100 DO
SET stuid = 1;
WHILE stuid < 1000 DO
insert into salary_static(school_id,student_id,salary,year) values (schid,stuid,floor(rand()*10000),year);
SET stuid = stuid + 1;
END WHILE;
SET schid = schid + 1;
END WHILE;
SET year = year + 1;
COMMIT;
END WHILE;
END //
delimiter ;

call init_salary_static();

覆盖索引的场景

1
2
3
4
5
select * from salary_static where year < 2010 limit 500000,10;

select s.* from salary_static s join
(select id from salary_static where year < 2010 limit 500000,10)
as tmp on (tmp.id = s.id);

image

由图可见,优化前sql查询平均需要大约0.8秒的时间,而通过延时关联优化后,则仅需要大约0.1秒的时间。即在覆盖索引的场景下,延时关联对于大分页查询可以有效提高查询效率。

回表的场景

1
2
3
4
5
select * from salary_static where year < 2010 and salary < 8000 limit 500000,10;

select s.* from salary_static s join
(select id from salary_static where year < 2010 and salary < 8000 limit 500000,10)
as tmp on (tmp.id = s.id);

image

由图可见,优化前sql查询平均需要大约1.2秒的时间,而通过延时关联优化后,依然需要大约1.2秒的时间。即在非覆盖索引的场景下,延时关联不能提高查询效率。