mysql offset 执行缓慢

Reading time ~1 minute

这段时间,由于要提取一些关键词,所以要遍历 mysql 中整个表一条条的操作,于是毫不犹豫的写出了下面的语句:

1
SELECT * FROM documents ORDER BY id LIMIT ?, 1

这语句已经是简单的令人发指了,所以也没想太多,直接就跑了起来。

然而,跑着跑着,某次回来看的时候,发现执行很慢,经常更新一个就要卡好久, 刚开始以为是网络不太好,没太在意,后来感觉实在是有点不对劲,就加日志仔细查了下是哪里慢了,结果竟然发现那句 SELECT 异常的慢。 抱着不敢相信的态度,去命令行下执行了一下,发现真的用了快一分钟才执行一句,当时就惊呆了。 我们这可是主键聚簇索引,并且还只取一条数据而已啊!!!

于是仔细又测试了一下,发现在 offset 小的时候没什么问题,之后 offset 大了之后才会有问题, 看查询分析也会发现,里面显示的行数也跟 offset 有关,看来真的就是 offset 不会快速跳过,还是会一个个的扫过来,想想真是恐怖。 虽然不知道为什么会设计成这样,讲道理 B tree 索引应该有能力直接找到第 N 大的元素啊。

不过既然 offset 慢是既定事实,那么我们还是想想应该怎么优化吧。如果纯粹是针对这句语句的话,可以改成:

1
SELECT * FROM documents WHERE id = (SELECT id FROM documents ORDER BY id LIMIT ?, 1)

这样的话,扫描的时候,只需要对索引进行扫描了,速度会快一些,但个人感觉不治本,毕竟只是常量级别的优化。 所以,其实最好的办法应该还是记录上一次的的最后一条记录的 id 值,然后后面执行的时候用 WHERE 语句做过滤,享受飞一般的速度。

1
SELECT * FROM documents WHERE id > ? ORDER BY id LIMIT 1

当然这种条件不是每次都能满足,当我们不是顺序访问的时候,我们是拿不到上次的 id 的,不过我们这里只是遍历,所以完全是可以满足这个要求的。

这次的事情算是给自己提了个醒,永远不要太轻视一句简单的 SQL,有时候写法上稍微有点不同,效果会是天壤之别。 说起来好像原来也遇到过这个问题,不过当时简单带过就给忘了,导致这次又掉这个坑里了,说到底还是 SQL 没学扎实。

不过感觉好想吐槽下阿里云 RDS,本来以为速度应该比一般自己搭 mysql 快的,结果哪想到,这次才 offset 几万就慢成这样, 专门把数据 dump 下来在自己机器上跑了下,妥妥 1s 之内能成毫无压力,然后我本机隔离级别还是 RR……

挂载网络文件夹后网络故障时文件操作命令卡死

挂载 NFS 或者 Samba 的时候,经常会由于网络故障导致挂载好的链接断掉。此时如果尝试进行 ls、cd、df 等各种命令,只要与此目录沾上边,就会卡住。如果使用了类似 oh-my-zsh 这种配置的,只要在网络目录中,弹出命令提示符前就会直接卡住。这个时候第一反应就是...… Continue reading

路由折腾记 第四弹

Published on September 02, 2017