社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

社区精选|MySQL 百万数据深度分页优化思路分析

SegmentFault思否 • 2 年前 • 129 次点击  

今天小编为大家带来的是社区作者 一个程序员的成长 的文章,让我们一起来学习 MySQL。




业务场景


一般在项目开发中会有很多的统计数据需要进行上报分析,一般在分析过后会在后台展示出来给运营和产品进行分页查看,最常见的一种就是根据日期进行筛选。这种统计数据随着时间的推移数据量会慢慢的变大,达到百万、千万条数据只是时间问题。


瓶颈再现


创建了一张 user 表,给 create_time 字段添加了索引。并在该表中添加了 100w 条数据。



我们这里使用 limit 分页的方式查询下前 5 条数据和后 5 条数据在查询时间上有什么区别。


查询前 10 条基本上不消耗什么时间



我们从第 50w+开始取数据的时候,查询耗时 1 秒。



SQL_NO_CACHE

这个关键词是为了不让 SQL 查询走缓存。


同样的 SQL 语句,不同的分页条件,两者的性能差距如此之大,那么随着数据量的增长,往后页的查询所耗时间按理会越来越大。


问题分析


回表


我们一般对于查询频率比较高的字段会建立索引。索引会提高我们的查询效率。我们上面的语句使用了SELECT * FROM user,但是我们并不是所有的字段都建立了索引。当从索引文件中查询到符合条件的数据后,还需要从数据文件中查询到没有建立索引的字段。那么这个过程称之为回表。


覆盖索引


如果查询的字段正好创建了索引了,比如 SELECT create_time FROM user,我们查询的字段是我们创建的索引,那么这个时候就不需要再去数据文件里面查询,也就不需要回表。这种情况我们称之为覆盖索引


IO


回表操作通常是IO 操作,因为需要根据索引查找到数据行后,再根据数据行的主键或唯一索引去聚簇索引中查找具体的数据行。聚簇索引一般是存储在磁盘上的数据文件,因此在执行回表操作时需要从磁盘读取数据,而磁盘 IO 是相对较慢的操作。


LIMTI 2000,10 ?


你有木有想过 LIMIT 2000,10 会不会扫描 1-2000 行,你之前有没有跟我一样,觉得数据是直接从 2000 行开始取的,前面的根本没扫描或者不回表。其实这样的写法,一个完整的流程是查询数据,如果不能覆盖索引,那么也是要回表查询数据的。


现在你知道为什么越到后面查询越慢了吧!


问题总结


我们现在知道了 LIMIT 遇到后面查询的性能越差,性能差的原因是因为要回表,既然已经找到了问题那么我们只需要减少回表的次数就可以提升查询性能了。


解决方案


既然覆盖索引可以防止数据回表,那么我们可以先查出来主键 id(主键索引),然后将查出来的数据作为临时表然后 JOIN 原表就可以了,这样只需要对查询出来的 5 条结果进行数据回表,大幅减少了 IO 操作。


优化前后性能对比


我们看下执行效果:


  • 优化前:1.4s



  • 优化后:0.2s



查询耗时性能大幅提升。这样如果分页数据很大的话,也不会像普通的 limit 查询那样慢。



点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,“公众号后台回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~

- END -


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/154960
 
129 次点击