分享

UC头条:一次 SQL 查询优化原理分析(900W 数据,从 17s 到 300ms)下

 gaolidabangzi 2022-02-25

肯定会有人问:既然一开始是利用索引的,为什么不先沿着索引叶子节点查询到最后需要的5个节点,然后再去聚簇索引中查询实际数据。这样只需要5次随机I/O,类似于下面图片的过程:

点击加载图片

其实我也想问这个问题。

证实

下面我们实际操作一下来证实上述的推论:为了证实select*fromtestwhereval=4limit300000,5是扫描300005个索引节点和300005个聚簇索引上的数据节点,我们需要知道MySQL有没有办法统计在一个sql中通过索引节点查询数据节点的次数。我先试了Handler_read_*系列,很遗憾没有一个变量能满足条件。我只能通过间接的方式来证实:InnoDB中有bufferpool。里面存有最近访问过的数据页,包括数据页和索引页。所以我们需要运行两个sql,来比较bufferpool中的数据页的数量。预测结果是运行select*fromtestainnerjoin(selectidfromtestwhereval=4limit300000,5);之后,bufferpool中的数据页的数量远远少于select*fromtestwhereval=4limit300000,5;对应的数量,因为前一个sql只访问5次数据页,而后一个sql访问300005次数据页。

select*fromtestwhereval=4limit300000,5

mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;Emptyset(0.04sec)

可以看出,目前bufferpool中没有关于test表的数据页。

可以看出,此时bufferpool中关于test表有4098个数据页,208个索引页。

select*fromtestainnerjoin(selectidfromtestwhereval=4limit300000,5);为了防止上次试验的影响,我们需要清空bufferpool,重启mysql。

mysqladminshutdown/usr/local/bin/mysqld_safe&

mysql>selectindex_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhereINDEX_NAMEin('val','primary')andTABLE_NAMElike'%test%'groupbyindex_name;Emptyset(0.03sec)

运行sql:

我们可以看明显的看出两者的差别:第一个sql加载了4098个数据页到bufferpool,而第二个sql只加载了5个数据页到bufferpool。符合我们的预测。也证实了为什么第一个sql会慢:读取大量的无用数据行(300000),最后却抛弃掉。而且这会造成一个问题:加载了很多热点不是很高的数据页到bufferpool,会造成bufferpool的污染,占用bufferpool的空间。遇到的问题为了在每次重启时确保清空bufferpool,我们需要关闭innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,这两个选项能够控制数据库关闭时dump出bufferpool中的数据和在数据库开启时载入在磁盘上备份bufferpool的数据。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多