分库后,分页查询按照时间time来排序order by。
1.全局视野法
若查询第x页的数据,每页y条。一共n个库。
步骤:
- 将order by time offset (x*y+1) limit y,改写成order by time offset 0 limit (x*y+1) +y
- 服务层将改写后的SQL语句发往各个分库:即每库各取x页的数据
- 服务层将得到 n*(x*y+1+y) 条数据
- 服务层对得到的数据进行内存排序,内存排序后再取偏移量x*y+1后的y条记录,就是全局视野所需的一页数据
2.业务折衷法
2.1.禁止跳页查询
只提供“下一页”的功能
- 查询第1页数据
每个库执行order by time where time>0 limit y
得到 n*y条数据,内存排序,得到前y条,即为第1页。
$time_max=第1页最大的time - 获取第2页数据
每个库依次查询
order by time where time>$time_max limit y
一共得到n*y条数据,取time最小的y条为第2页数据。
$time_max=第2页最大的time
数据的传输量和排序的数据量不会随着不断翻页而导致性能下降。
3. 二次查询法
为了方便举例,假设2个库,一页5条数据,查询第200页的SQL语句为select * from T order by time offset 1000 limit 5;
- 查询改写
改写为select * from T order by time offset 500 limit 5,并投递给所有的分库,注意,这个offset的500,来自于全局offset的总偏移量1000,除以水平切分数据库个数2。
如果是3个分库,则可以改写为select * from T order by time offset 333 limit 5
假设这三个分库返回的数据(time, uid)如下: - 每个分库都返回的按照time排序的一页数据。
- 找到所返回3页全部数据的最小值
- 第一个库,5条数据的time最小值是1487501123
第二个库,5条数据的time最小值是1487501133
第三个库,5条数据的time最小值是1487501143
故,三页数据中,time最小值来自第一个库,time_min=1487501123,这个过程只需要比较各个分库第一条数据。 - 查询二次改写
第二次要改写成一个between语句,between的起点是time_min,between的终点是原来每个分库各自返回数据的最大值:
第一个分库,第一次返回数据的最大值是1487501523
所以查询改写为select * from T order by time where time between time_min and 1487501523
第二个分库,第一次返回数据的最大值是1487501323
所以查询改写为select\ * from T order by time where time between time_min and 1487501323
第三个分库,第一次返回数据的最大值是1487501553
所以查询改写为select * from T order by time where time between time_min and 1487501553
第二次查询会返回比第一次查询结果集更多的数据,假设这三个分库返回的数据(time, uid)如下: - 在每个结果集中虚拟一个time_min记录,找到time_min在全局的offset
- 故:
time_min在第一个库的offset是333;
time_min在第二个库的offset是331;
time_min在第三个库的offset是330;
综上,time_min在全局的offset是333+331+330=994; - 得到time_min在全局的offset,就相当于有了全局视野,根据第二次的结果集,就能够得到全局offset 1000 limit 5的记录
- 第二次查询在各个分库返回的结果集是有序的,又知道了time_min在全局的offset是994,一路排下来,容易知道全局offset 1000 limit 5的一页记录(上图中黄色记录)。