问题描述
最近线上出现了一个问题,在某个 SELECT … FROM table1 ORDER BY ××× LIMIT N 时,第一页和第二页出现了重复的数据。
结论
MySQL在5.6版本的时候对 ORDER BY LIMIT 做了个优化(排序字段无索引,且值不唯一)时,优化器遇到 ORDER BY LIMIT 语句的时候,使用了priority queue(优先队列),优先队列使用的堆排序,因为这种场景是用于求TOP N 问题,堆排快,但是堆排不稳定。
分析
在官方文档当中,有提到
在官方文档当中
while (get_next_sortkey())
{
if (using priority queue)
push sort key into queue
else
{
if (no free space in sort_keys buffers)
{
sort sort_keys buffer;
dump sorted sequence to 'tempfile';
dump BUFFPEK describing sequence location into 'buffpek_pointers';
}
put sort key into 'sort_keys';
}
}
if (sort_keys has some elements && dumped at least once)
sort-dump-dump as above;
else
don't sort, leave sort_keys array to be sorted by caller.
描述了伪代码的优化。原因是啥呢,就是因为快。
PQ
priority queue 是一个堆,order by limit 本质上就是在求TOP N ,那只要求出最大N条数据/最小的N条数据就行。剩下的数据不需要再进行排序,这样就极大的加快了速度。而堆排序总所州市
解决办法
1.索引排序字段
2.在排序中加上唯一值,比如主键 id,这样由于 id 是唯一的,就能确保参与排序的 key 值不相同。
参考文章:
https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html