select * from t order by 普通的索引 limit 起始位置,分页长度
先找一条 sql 如下,当它使用了 key1 普通索引在执行的时候过程如下:
select * from limit_test where key1 > 'a' and key1 < 'b' and common_field='a';
执行过程 uml 图表示:
limit 是 server 层准备向客户端发生记录的时候才去处理 limit 的内容;先找一条 sql:
select * from test_limt order by key1 limit 5000,1;
limit 5000,1
,意味着第 5001 条才是真正要发送给客户端的,所以这里先做一个统计,假设 server 层维护了一个称作 limit_count 变量用于统计已经跳过了多少条记录,此时 limit_count = 1;放到 uml 图:
上述过程有一个问题,server 层在发送给客户端之前才去判断 limit 条件,意味着要进行 5001 次回表。server 层在进行执行计划分析的时候,觉得执行这么多次回表的成本太大了,还不如直接执行全表扫描 + filesort 块,所以这么慢;
进行了 5001 次回表操作成本太高了,如果能只回表一次就好了;
select * from limit_test,(select id from limit_test limit 5000,1) as d where limit_test.id = d.id
这样写的话,select id from limit_test limit 5000,1
首先通过 key1 普通索引获取到 id ,然后通过 id 去 limit_test 中去查询。这样省去了 5000 次回表,效率大大提高;
执行时间比较:
select * from limit_test,(select id from limit_test limit 5000,1) as d where limit_test.id = d.id
虽然子查询只查询了主键 id,limit 实现 server 层去存储引擎还是一条一条去拿,再进行判断是不是 5001,只是减少了回表成本。
以上参考Mysql 是怎么运行的作者的公众号文章——MySQL的LIMIT这么差劲的吗
在分页查询情况下,可以把用户翻页的把上一页开始 key1(first_key1) 和 结束的 key1(last_key1) 数据以请求参数的形式带到这次的分页查询中;