一、问题
假设页大小20,如从从库im_team_messsage0,im_team_messsage1查询,使用者编写的sql如下(只查询第1000行到第1010行)。
SELECT * FROM im_team_messsage ORDER BY send_at DESC LIMIT 1000, 10;
shardingsphere的改写引擎会对此sql进行改写,可以看到ss将limit后解析成从第一行开始查询到第1010行。
SELECT * FROM im_team_messsage0 ORDER BY send_at DESC LIMIT 0, 1010;
SELECT * FROM im_team_messsage1 ORDER BY send_at DESC LIMIT 0, 1010;
数据库端的查询压力都是差不多的, 因为都是要差不多要扫描1010行才能取得到数据。不同的是改写sql后, 客户端的内存消耗和网络消耗变大了。
shardingsphere巧妙地利用流式处理和优先级队列结合的方式(“流式归并”),消除了客户端内存消耗的压力, 但是网络消耗的影响依然是无法消除。
二、如何避免使用limit带来的效率问题
有很多方法可以避免使用LIMIT进行分页。比如构建行记录数量与行偏移量的二级索引,或使用上次分页数据结尾ID作为下次查询条件的分页方式等。
2.1、使用上次分页数据结尾ID作为下次查询条件
前提:使用shardingsphere分布式id递增雪花算法保证id呈现连续性
使用上次分页数据结尾ID作为下次查询条件:
但同时需要注意的是,由于排序的需要,大量的数据仍然需要传输到ShardingSphere的内存空间。 因此,采用LIMIT这种方式分页,并非最佳实践。 由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案,例如:
SELECT * FROM im_team_message WHERE id > 100000 AND id <= 100010 ORDER BY id;
或通过记录上次查询结果的最后一条记录的ID进行下一页的查询,例如:
SELECT * FROM im_team_message WHERE id > 10000000 LIMIT 10;
同理,使用上次分页数据结尾的时间戳作为下次查询条件也可以(按照时间进行排序)。
2.2、二次查询法
这种方法能够满足业务的精确需要,无需业务折衷,又高性能的方法,缺点是业务代码复杂且需要进行两次数据库查询。
三、分页子查询(Mysql、Oracle)
Oracle和SQLServer的分页都需要通过子查询来处理,ShardingSphere支持分页相关的子查询。
Oracle 支持使用rownum进行分页:
SELECT * FROM (SELECT row_.*, rownum rownum_ FROM (SELECT o.order_id as order_id FROM t_order o JOIN t_order_item i ON o.order_id = i.order_id) row_ WHERE rownum <= ?) WHERE rownum > ?
目前不支持rownum + BETWEEN的分页方式。
MySQL和PostgreSQL都支持LIMIT分页,无需子查询:
SELECT * FROM t_order o ORDER BY id LIMIT ? OFFSET ?
评论