Mysql优化大分页查询

栏目: IT技术 · 发布时间: 5年前

内容简介:如题,年前做了一个需求,涉及到Mysql大分页查询,整理一下,希望对需要的小伙伴有帮助。系统结构如上图。经过排查是因为系统B拉取数据的方法是根据_tiemstamp(数据操作时间)分页查询系统A的接口,即:

如题,年前做了一个需求,涉及到 Mysql 大分页查询,整理一下,希望对需要的小伙伴有帮助。

背景

Mysql优化大分页查询

系统结构如上图。经过排查是因为 系统B拉取数据时间太长导致的推送超时。

系统B拉取数据的方法是根据_tiemstamp(数据操作时间)分页查询系统A的接口,即:

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

由于该数据是从其他数据源中导入的,所以_timestamp这个字段值几乎相同,这就导致了在我们的查询范围内存在大约 150万 的数据。一般遇到这种情况,首先想到的就是 是否需要给_timestamp添加索引 ,这张表上是存在_timestamp索引的。那么为什么还会出现这个问题呢?这就要从分页查询本身说起了。

分页查询的性能瓶颈

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.通过链表将叶子节点串联在一起,这样可以方便按区间查找。

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();

接着,我们看一下使用索引的情况下,分页查询语句的耗时情况。

Mysql优化大分页查询

可以看出,在使用索引的情况下,无论初始位置是0,还是145万,Mysql都会扫描所有符合条件的数据,然后找到初始位置的数据,向后查偏移量个数据,最后返回。

Mysql优化大分页查询 Mysql优化大分页查询

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

解决方法

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

但是回到问题本身,问题出现的原因就是分页语句随着初始位置的增加,会有性能问题,所以治本的办法,是对这个语句进行优化,有两个优化方法:

1 延迟关联法:

我们先查询出符合要求的主键(由于查询的字段有索引,该索引的叶子节点就是主键,通过索引覆盖我们可以省去一次回表操作。)

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

Mysql优化大分页查询

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

2 主键阈值法

如果你的主键是自增的,那么就可以通过条件推算出符合条件的主键最大值&最小值(这里也是通过索引覆盖省去了一次回表操作)

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

Mysql优化大分页查询

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

最后

最后对文章做一下补充说明:

1.文中优化效果是仅凭借调用一次 SQL 的耗时给出的,并不科学,仅仅是为了让大家有一个直观的概念。

2.无论是延迟关联法,还是主键阈值法。思想都是一样的,先把符合条件的主键找到,然后通过主键去定位符合条件的数据,这里优化了2个点: 1.通过索引覆盖避免了回表;2.通过主键直接定位数据的方法,省去了在数据集中查询初始位置的过程

3.优化的效果随数据量增加而增强。万级别的数据优化效果可能并不明显。

最后,期待您的订阅和点赞,专栏每周都会更新,希望可以和您一起进步,同时也期待您的批评与指正!

Mysql优化大分页查询

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Effective JavaScript

Effective JavaScript

赫尔曼 (David Herman) / 黄博文、喻杨 / 机械工业出版社 / 2014-1-1 / CNY 49.00

Effective 系列丛书经典著作,亚马逊五星级畅销书,Ecma 的JavaScript 标准化委员会著名专家撰写,JavaScript 语言之父、Mozilla CTO —— Brendan Eich 作序鼎力推荐!作者凭借多年标准化委员会工作和实践经验,深刻辨析JavaScript 的内部运作机制、特性、陷阱和编程最佳实践,将它们高度浓缩为极具实践指导意义的 68 条精华建议。 本书共......一起来看看 《Effective JavaScript》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换