抽象版本:
假设分库分表共有n个表,查询是LIMIT X OFFSET Y,那么:
- 首先发送查询语句 LIMIT X OFFSET
Y/N
到所有的表 - 找到返回结果中的最小值(升序),记作min
- 执行第二次查询,关键是BETWEEN min AND max,其中max是第一次查询的数据中每个表各自的最大值
- 根据min、第一次查询和第二次查询的值来确定min的全局偏移量。总的来说,min在某个表里的偏移量这样计算:如果第二次查询比第一次查询多了K条数据,偏移量就是Y/N-K。然后把所有表的偏移量加在一起,就是min的全局偏移量
- 根据min的全局偏移量,在第二次查询的结果里面向后补足到Y,得到第一条数据的位置,再取X条。
引入中间表
引入中间表的意思是额外存储一份数据,只用来排序。这个方案里面就是在中间表里加上排序相关的列。
排序是一个非常常见的需求,那么就可以考虑引入一个中间表来辅助排序。比如说用更新时间来排序的时候,在中间表里加上更新时间。查询的时候先在中间表里查到目标数据,再去目标表里把全部数据都查询出来。
有两个明显的缺陷:一是WHERE只能使用中间表上的列;二是维护中间表也会引起数据一致性问题。
那么如何解决数据一致性问题呢?
比较简单的做法就是业务保持双写,也就是写入目标表也写入中间表。不过这里我更加建议使用 Canal 之类的框架来监听 binlog,异步更新中间表。这样做的好处是业务完全没有感知,没有什么改造成本。更新的时候可以考虑引入重试机制,进一步降低失败的几率。
面试官可能进一步问你,如果更新中间表经过重试之后也失败了,怎么办?
这时候并没有更好的办法,无非就是引入告警,然后人工介入处理。最后你可以再总结一下这个方案。
这个方案是一个依赖最终一致性的方案,在强调强一致性的场景下并不是很合适。