在MySQL中如何使用覆盖索引优化limit分页查询
背景
今年3月份时候,线上发生一次大事故。公司主要后端服务器发生宕机,所有接口超时。宕机半小时后,又自动恢复正常。但是过了2小时,又再次发生宕机。
通过接口日志,发现MySQL数据库无法响应服务器。在阿里云的技术支持的帮助下,发现了MySQL数据库中存在大量慢查询,导致CPU负载过高。最后,根据慢查询日志,定位到了出问题的SQL和业务接口。
业务接口是一个分页接口,莫名被刷到7000多页,偏移量(offset)高达20w多。每当这条SQL执行时,数据库CPU直接打满。查询时间超过1分钟才有响应。由于慢查询导致数据库CPU使用率爆满,其他业务的数据库请求无法得到及时响应,接口超时。最后,拖垮主服务器。
limit分页查询性能问题
MySQL Limit 语法格式:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
分页查询时,我们会在 LIMIT 后面传两个参数,一个是偏移量(offset),一个是获取的条数(limit)。当偏移量很小时,查询速度很快,但是当 offset 很大时,查询速度就会变慢。
下面我们以一个实例,讲解一下分页性能问题。假设有一张 300w 条数据的表,对其进行分页查询。
select * from tbl_works limit 1, 10 // 32.8ms
select * from tbl_works limit 10, 10 // 34.2ms
select * from tbl_works limit 100, 10 // 35.4ms
select * from tbl_works limit 1000, 10 // 39.6ms
select * from tbl_works limit 10000, 10 // 5660ms
select * from tbl_works limit 100000, 10 // 61.4 秒
select * from tbl_works limit 1000000, 10 // 273 秒
可以看到,随着偏移量(offset)的增加,查询时间变得越长。对于普通的业务而言,超过1秒的查询是绝对不可以忍受的。上例中,当偏移的起始位置超过10万时,分页查询的时间超过61秒。当偏移量超过100万时,查询时间竟然长达273秒。
从上例中,我们可以总结出:LIMIT分页查询的时间与偏移量值成正比。当偏移量越大时,查询时间越长。这种情况,会随着业务的增加,数据的增多,会越发的明显。那么,如何优化这种情况呢?答案是,覆盖索引。
优化方法
对于LIMIT分页查询的性能优化,主要思路是利用覆盖索引字段定位数据,然后再取出内容。
不使用覆盖索引,查询耗时情况:
SELECT * FROM `tbl_works`
WHERE `status`=1
LIMIT 100000, 10 // 78.3 秒
1)子查询分页方式
SELECT * FROM tbl_works
WHERE id >= (SELECT id FROM tbl_works limit 100000, 1)
LIMIT 20 // 54ms
子查询分页方式,首先通过子查询和覆盖索引定位到起始位置ID,然后再取所需条数的数据。
缺点是,不适用于结果集不以ID连续自增的分页场景。在复杂分页场景,往往需要通过过滤条件,筛选到符合条件的ID,此时的ID是离散且不连续的。如果使用上述的方式,并不能筛选出目标数据。
当然,我们也可以对此方法做一些改进,首先利用子查询获取目标分页的 ids,然后再根据 ids 获取内容。
根据直觉将SQL改造如下:
SELECT * FROM tbl_works
WHERE id IN (SELECT id FROM tbl_works limit 100000, 10)
// 错误信息:
// This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
然而,并不尽人意。我们得到一个错误提示。
错误信息的含义是,子查询不能有 limit操作。于是,我们对SQL进行了改造,对子查询包了一层:
SELECT t1.* FROM tbl_works t1
WHERE t1.id in (SELECT t2.id from (SELECT id FROM tbl_works limit 100000, 10) as t2) // 53.9ms
执行成功,且查询效率很高。但是,这种写法非常繁琐。我们可以使用下面的 join 分页方式,达到相同的优化效果。实际上,两者的原理是相同的。
2)join 分页方式
SELECT * FROM tbl_works t1
JOIN (SELECT id from tbl_works WHERE status=1
limit 100000, 10) t2
ON t1.id = t2.id // 53.6 ms
这条SQL的含义是,通过自连接与join定位到目标 ids,然后再将数据取出。在定位目标 ids时,由于 SELECT的元素只有主键 ID,且status 存在索引,因此MySQL只需在索引中,就能定位到目标 ids,不用在数据文件上进行查找。因而,查询效率非常高。
覆盖索引(Cover Index)
如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作。
简单的说,覆盖索引覆盖所有需要查询的字段(即,大于或等于所查询的字段)。MySQL可以通过索引获取查询数据,因而不需要读取数据行。
覆盖索引的好处:
- 索引大小远小于数据行大小。因而,如果只读取索引,则能极大减少对数据访问量。
- 索引按顺序储存。对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少。
- 避免对主键索引的二次查询。二级索引的叶子节点包含了主键的值,如果二级索引包含所要查询的值,则能避免二次查询主键索引(聚簇索引,聚簇索引既存储了索引,也储存了值)。
总结
通过利用覆盖索引,能极大的优化了Limit分页查询的效率。在真正的实践中,除了使用覆盖索引,优化查询速度外,我们还可以使用 Redis 缓存,将热点数据进行缓存储存。
背景描述的事故,我们考虑了时间成本和业务复杂度后,最后采取的是限制分页和增加缓存。所谓的限制分页,即在不影响阅读体验的前提下,只允许用户可以查看前几千条的数据。经测验,偏移量较小时的查询效率较令人满意,查询效率接近使用覆盖索引查询的速度。
参考资料
作者:youthcity
链接:https://www.jianshu.com/p/c6290e65d8b5
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
在MySQL中如何使用覆盖索引优化limit分页查询的更多相关文章
- mysql 通过使用联全索引优化Group by查询
/*SELECT count(*) FROM (*/ EXPLAIN SELECT st.id,st.Stu_name,tmpgt.time,tmpgt.goutong FROM jingjie_st ...
- MYSQL的全表扫描,主键索引(聚集索引、第一索引),非主键索引(非聚集索引、第二索引),覆盖索引四种不同查询的分析
文章出处:http://inter12.iteye.com/blog/1430144 MYSQL的全表扫描,主键索引(聚集索引.第一索引),非主键索引(非聚集索引.第二索引),覆盖索引四种不同查询的分 ...
- 【MySQL】MySQL的执行计划及索引优化
我们知道一般图书馆都会建书目索引,可以提高数据检索的效率,降低数据库的IO成本.MySQL在300万条记录左右性能开始逐渐下降,虽然官方文档说500~800w记录,所以大数据量建立索引是非常有必要的. ...
- mysql索引优化比普通查询速度快多少
mysql索引优化比普通查询速度快多少 一.总结 一句话总结:普通查询全表查询,速度较慢,索引优化的话拿空间换时间,一针见血,所以速度要快很多. 索引优化快很多 空间换时间 1.软件层面优化数据库查询 ...
- MySql数据表设计,索引优化,SQL优化,其他数据库
MySql数据表设计,索引优化,SQL优化,其他数据库 1.数据表设计 1.1数据类型 1.2避免空值 1.3text类型优化 2.索引优化 2.1索引分类 2.2索引优化 3.SQL优化 3.1分批 ...
- MySQL中的SQL的常见优化策略
MySQL中的SQL的常见优化策略 MySQL中的索引优化 MySQL中的索引简介 1 避免全表扫描对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索 ...
- Mysql中主键与索引
摘自: https://www.cnblogs.com/wicub/p/5898286.html 一.什么是索引?索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没 ...
- 第二百八十八节,MySQL数据库-索引、limit分页、执行计划、慢日志查询
MySQL数据库-索引.limit分页.执行计划.慢日志查询 索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获 ...
- SQL通用优化方案(where优化、索引优化、分页优化、事务优化、临时表优化)
SQL通用优化方案:1. 使用参数化查询:防止SQL注入,预编译SQL命令提高效率2. 去掉不必要的查询和搜索字段:其实在项目的实际应用中,很多查询条件是可有可无的,能从源头上避免的多余功能尽量砍掉, ...
随机推荐
- HyperLedger Fabric 1.4 区块链工作过程(2.3)
区块链的工作过程分交易产生.交易广播.节点计算.获取记账权.记账权广播.接收区块.验证区块和完成记账七个过程. 1) 交易产生:用户向区块链发了一笔交易信息,将产生交易:2) 交易广播:当一笔新交易产 ...
- 将python自动转换为.exe文件
使用py2exe包进行转换.py2exe怎么装的可以网上另查.时间久了,记不太清了...... 这个程序可以把自己进行转换.但是没法运行....其实只要是需要修改自身的程序打包后都没法运行. # -* ...
- asp.net页面刷新或者回发后DIV的滚动条位置不变!(转)
源文件:http://www.cnblogs.com/nyth/archive/2011/06/10/2077868.html 当把数据放在div里面,然后给div设置Scroll显示,在页面刷新后或 ...
- 树链剖分学习&BZOJ1036
题目传送门 树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组.SBT.SPLAY.线段树等)来维护每一条链. ...
- 【LG3234】[HNOI2014]抄卡组
题面 题解 分三种情况: 若所有串都没有通配符,直接哈希比较即可. 若所有串都有通配符, 把无通配符的前缀 和 无通配符的后缀哈希后比较即可. 中间部分由于通配符的存在,一定可以使所有串匹配. 若部分 ...
- 【CF833E】Caramel Clouds
[CF833E]Caramel Clouds 题面 洛谷 题目大意: 天上有\(n\)朵云,每朵云\(i\)会在时间\([li,ri]\)出现,你有\(C\)个糖果,你可以花费\(c_i\)个糖果让云 ...
- Redis主从复制(Master/Slave) 与哨兵模式
Redis主从复制是什么? 行话:也就是我们所说的主从复制,主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主 Redis主从复制 ...
- JavaScript 中函数的定义和调用
3种函数定义方式: 1.使用关键字 function 来声明并定义函数 function myFunction(a, b) { return a * b; } 调用函数: var x = myFunc ...
- APP端测试,常见功能测试点汇总
除去每个产品和版本不同的业务需求以及功能,针对于大多数的APP的共同点和移动设备的特性,本文总结了一些APP功能测试中经常遇见,需要考虑到的测试点以共参考 一.安装和卸载 应用的安装和卸载在任何一款A ...
- 接口自动化·分享·第二篇·你必须了解的HttpRequest和HttpResponse
完成一个接口调用其实就是完成了一次http请求,所以你必须要清楚一个http请求的组成. 一次完整的请求包含:请求+响应. 一.HttpRequest请求对象 要调用一个接口,首先要准备的是一个请求对 ...