首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

900w条数据的SQL,请优化一下

有一张财务流水表,未分库分表,目前的数据量为 9555695,分页查询使用到了 limit,优化之前的查询耗时 16 s 938 ms (execution: 16 s 831 ms, fetching: 107 ms),按照下文的方式调整 SQL 后,耗时 347 ms (execution: 163 ms, fetching: 184 ms);

操作:查询条件放到子查询中,子查询只查主键 ID,然后使用子查询中确定的主键关联查询其他的属性字段;

原理:减少回表操作;

一,前言

首先说明一下 MySQL 的版本:

表结构:

id 为自增主键,val 为非唯一索引。

灌入大量数据,共 500 万:

我们知道,当 limit offset rows 中的 offset 很大时,会出现效率问题:

时间相差很明显。

为什么会出现上面的结果?我们看一下

的查询过程:

查询到索引叶子节点数据。

根据叶子节点上的主键值去聚簇索引上查询需要的全部字段值。

类似于下面这张图:

像上面这样,需要查询 300005 次索引节点,查询 300005 次聚簇索引的数据,最后再将结果过滤掉前 300000 条,取出最后 5 条。MySQL 耗费了大量随机 I/O 在查询聚簇索引的数据上,而有 300000 次随机 I/O 查询到的数据是不会出现在结果集当中的。

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

其实我也想问这个问题。

证实

下面我们实际操作一下来证实上述的推论:

为了证实

是扫描 300005 个索引节点和 300005 个聚簇索引上的数据节点,我们需要知道 MySQL 有没有办法统计在一个 SQL 中通过索引节点查询数据节点的次数。我先试了 Handler_read_*系列,很遗憾没有一个变量能满足条件。

我只能通过间接的方式来证实:

InnoDB 中有 buffer pool。里面存有最近访问过的数据页,包括数据页和索引页。所以我们需要运行两个 SQL,来比较 buffer pool 中的数据页的数量。预测结果是运行

之后,buffer pool 中的数据页的数量远远少于

对应的数量,因为前一个 SQL 只访问 5 次数据页,而后一个 SQL 访问 300005 次数据页。

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

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

为了防止上次试验的影响,我们需要清空 buffer pool,重启 MySQL。

运行 SQL:

我们可以看明显的看出两者的差别:第一个 SQL 加载了 4098 个数据页到 buffer pool,而第二个 SQL 只加载了 5 个数据页到 buffer pool。符合我们的预测。也证实了为什么第一个 SQL 会慢:读取大量的无用数据行(300000),最后却抛弃掉。

而且这会造成一个问题:加载了很多热点不是很高的数据页到 buffer pool,会造成 buffer pool 的污染,占用 buffer pool 的空间。遇到的问题

为了在每次重启时确保清空 buffer pool,我们需要关闭innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,这两个选项能够控制数据库关闭时 dump 出 buffer pool 中的数据和在数据库开启时载入在磁盘上备份 buffer pool 的数据。

hello,我是御风,很高兴在这里与你相遇。我是一名程序员,也是一名B站UP主。我乐于分享,勤于学习。希望我们共同成长。永远相信梦想的力量

往期经典

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200202A0GPBB00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券