注:本篇不考虑索引相关优化!只考虑limit分页优化。
传统分页:limit 、 offset
语句一:
select*from test_t limit11,10;
语句二:
MySql官方描述:
为了兼容PostgreSQL,MySQL 也支持 limit offset
如:
select*from test_t limit10 offset 11;
以上两种方式会随着表数据量的增加性能会急剧下降。
关于MySQL limit 优化相关的官方文档解读
一,查询总行数的另一种方法
查询分页的总行数,我们一般情况下是使用的, select count(*)
去实现的。文档中介绍了另外一种方式,通过 select FOUND_ROWS();
语句,不过这种方式的需要修改获取limit 数据的SQL 。例如:如果在SQL中同时执行这两条语句,就可以分别获取到分页的数据和分页总行数。
# 这个SQL 比平时的分页SQL 多了 SQL_CALC_FOUND_ROWS 修饰
SELECT SQL_CALC_FOUND_ROWS id,`name` FROM `d_common_all_select_info` ORDER BY `name` LIMIT 0,10;
# 这条SQL返回的为总行数,不过统计方式与select count(*) 有些不同,另外需要与上面的SQL 同时执行
SELECT FOUND_ROWS();
二,查询的order by 的顺序问题
如果order by 中包含相同的值,则order by出来的结果不一定每次一样,它返回的顺序与总体执行计划有关。例如,带limit 和不带limit的order by 语句,返回顺序不一样的。
所以如果order by 的列包含相同值的时候,保证每次都是相同的结果,最好在最后的加上id列(这里假设自增id列名为id
)。例如:平常要order by dt
的,然后dt中会包含相同值的最好修改为 order by dt,id
优化方案一:延迟关联
步骤:
- 先按照条件分页查询出主键ID(下面的字查询)。
- 然后根据主键ID去关联表,查询出所有需要列的记录数。(这样可以避免扫描太多的数据页)
SELECT SQL_NO_CACHE a.*FROM `table_test` a,(SELECT id FROM table_test ORDERBY id LIMIT600000,10) b WHERE a.id=b.idORDERBY a.id;
优化方案二:书签记录(id分页)
书签记录:指我们可以用一个临时变量来存储上一次取数记录的位置,然后在获取下一页的时候,可以根据这个值,来获取大于这个值的下一页记录(上一页类似),直接从该值以后开始扫描。
例如:假设我们上一次获取到了分页 limit 100000,10 的记录,最大的值的id为 188888
(这里的值作为了一个书签记录),则我们获取 limit 100001,10 的记录可以这样写:
注:一般接口会多定义几个字段,方便于sql的查询:
- 本页的最后一条记录ID
- 本页的第一天记录ID
- 是否是上一页操作
SELECT SQL_NO_CACHE *FROM `table_test` where id>188888ORDERBY id LIMIT10;
总结
性能:书签记录 > 延迟关联 > 传统limit
局限性:
- 书签记录:只适合只有上一页、下一页操作的功能场景,如app端上划、下划操作,但跳页操作不支持。
- 延迟关联:业务场景广泛。跳页、只有上下页场景都支持。推荐使用。
- 传统limit:数据量达百万级,性能下降明显,不推荐。