Java猿社区—ShardingSphere之跨库分页带来的性能问题与常用解决办法
文章目录
参考:
ShardingSphere中文文档
假设页大小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作为下次查询条件
的分页方式等。
使用上次分页数据结尾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;
同理,使用上次分页数据结尾的时间戳作为下次查询条件也可以(按照时间进行排序)。
二次查询法
这种方法能够满足业务的精确需要,无需业务折衷,又高性能的方法,缺点是业务代码复杂且需要进行两次数据库查询。
请移步到:业界难题-“跨库分页”的四种方案
分页子查询(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
MySQL和PostgreSQL都支持LIMIT分页,无需子查询:
SELECT * FROM t_order o ORDER BY id LIMIT ? OFFSET ?