Mysql骚操做:优化大分页查询

背景

Mysql骚操做:优化大分页查询

系统结构如上图。通过排查是由于系统B拉取数据时间太长致使的推送超时。
系统B拉取数据的方法是根据_tiemstamp(数据操做时间)分页查询系统A的接口,即:前端

1SELECT 字段名2FROM 表名3WHERE _timestamp >= beginTime AND _timestamp <= endTime 4LIMIT n, m;

因为该数据是从其余数据源中导入的,因此_timestamp这个字段值几乎相同,这就致使了在咱们的查询范围内存在大约150万的数据。通常遇到这种状况,首先想到的就是是否须要给_timestamp添加索引,这张表上是存在_timestamp索引的。那么为何还会出现这个问题呢?这就要从分页查询自己提及了。sql

分页查询的性能瓶颈

B+树简述

首先咱们要了解InnoDB存储引擎中的B+数索引。这里我简单总结一下:缓存

Mysql骚操做:优化大分页查询

上图是一颗B+树,经过观察咱们能够发现它的一些特色:
  1.每一个节点中子节点个个数不能少于m/2个,不能大于m个(B+树是一颗m叉树,图中m=3)
  2.根节点的节点个数能够超过m/2个,这是一个例外
上述两点特性是为了保证B+树的查询效率。
节点数超过m越多,在总节点数相同的状况下,树的高度h就越小,此时m叉数就会向链表退化(O(logn)->O(n))。   节点数小于m/2越多,在总节点数相同的状况下,树的高度h就越高,此时查询数据,就须要经历更屡次的IO
  3.m叉树非叶子节点只存储索引,不存储数据
  4.经过链表将叶子节点串联在一块儿,这样能够方便按区间查找。ide

B+比起二叉查找树,有什么优点?

更矮,这就减小了IO次数。
因为非叶子节点不存储数据,上图查询任何数据,都须要3次IO,查询性能更稳定
因为叶子节点使用了链表链接,范围查询更简便。性能

分页查询过程

1.首先经过非主键索引查询出全部条件的主键
2.经过主键索引,定位到数据
3.不断重复上述操做
4.根据分页条件,肯定返回数据的启始位置以及数据量
5.返回数据
能够看出,初始位置值越大,定位时须要查询的数据就越多,查询效率也会越低测试

测试集

为了测试优化效果,我准备了150万测试数据(须要跑几分钟)。优化

1# 建表语句
 2CREATE TABLE `test`(
 3  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
 4  `name` varchar(512) NOT NULL DEFAULT '无' COMMENT '建立人',
 5  `_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 6  PRIMARY KEY (`id`),
 7  KEY `ix_timestamp` (`_timestamp`)
 8) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='测试表';
 9
10
11# 经过存储过程导入数据
12drop procedure idata;
13delimiter ;;
14create procedure idata()
15begin
16  declare i int;
17  set i=1;
18  while(i<=1500000)do
19    insert into test values(i, i, now());
20    set i=i+1;
21  end while;
22end;;
23delimiter ;
24
25call idata();

  接着,咱们看一下使用索引的状况下,分页查询语句的耗时状况。3d

Mysql骚操做:优化大分页查询

  能够看出,在使用索引的状况下,不管初始位置是0,仍是145万,Mysql都会扫描全部符合条件的数据,而后找到初始位置的数据,向后查偏移量个数据,最后返回。code

Mysql骚操做:优化大分页查询

Mysql骚操做:优化大分页查询

这两条语句的执行速度差距很是大,大约3个数量级(0.00sec,10 sec)blog

解决方法

针对于limit,有不少优化的方法,好比前端加缓存、或者使用分页加载的方式展现数据。(大部分用户请求数据的初始开始都不会很大)。在咱们的使用场景中,调大超时时间的阈值也是能够的。

可是回到问题自己,问题出现的缘由就是分页语句随着初始位置的增长,会有性能问题,因此治本的办法,是对这个语句进行优化,有两个优化方法:

1 延迟关联法:

咱们先查询出符合要求的主键(因为查询的字段有索引,该索引的叶子节点就是主键,经过索引覆盖咱们能够省去一次回表操做。)

而后再经过主键索引查询数据,这就省去了遍历数据找初始位置数据的过程

Mysql骚操做:优化大分页查询

  经过延迟关联的方法,咱们将10sec的耗时下降到了1.58sec,优化了将近1个数量级。

2 主键阈值法

若是你的主键是自增的,那么就能够经过条件推算出符合条件的主键最大值&最小值(这里也是经过索引覆盖省去了一次回表操做)

而后再根据阈值,取数据便可,一样省去了遍历数据找初始位置数据的过程

Mysql骚操做:优化大分页查询

经过主键阈值法的方法,咱们将10sec的耗时下降到了1.12sec,优化了1个数量级

最后

最后对文章作一下补充说明:
  1.文中优化效果是仅凭借调用一次SQL的耗时给出的,并不科学,仅仅是为了让你们有一个直观的概念。
  2.不管是延迟关联法,仍是主键阈值法。思想都是同样的,先把符合条件的主键找到,而后经过主键去定位符合条件的数据,这里优化了2个点:1.经过索引覆盖避免了回表;2.经过主键直接定位数据的方法,省去了在数据集中查询初始位置的过程  3.优化的效果随数据量增长而加强。万级别的数据优化效果可能并不明显。

相关文章
相关标签/搜索