前言
大家好,我是小东,今天和大家聊一聊水平维度分库分表数据查询方案
分表键的选择
我们对数据库表进行水平切分的时候必须要有一个维度,直白来说会选择表其中一个业务字段来作为水平切分的标准,我们把这个业务字段称为"水平分表键"。
这个分表键一般都是业务导向的。比如说,在电商领域对一个订单表进行水平拆分,如果是TO-C的场景比较多,一般采用的是按照买家ID的作为分表键。因为买家查询的场景是订单的主要场景。但是这个只是简单的方案,实际上的订单水平拆分涉及到的东西很多,文章后续会慢慢介绍。这里先抛砖引玉。
结论:分表键的确定是业务驱动的
分表后怎么应对不同维度的查询
我们还是拿订单表来举例,如果按照买家ID作为分表键来进行水平分表的话,那怎么应对其他维度的查询呢

通常我们对表进行水平拆分后,如果不做任何处理,其他维度的查询只能所有水平库水平表都进行扫描才能查询,这肯定是有问题的,接下来我们说下非分表键之外的查询的处理方案。
引入中间表

卖家在查询的过程中会多一个过程就是,先查询中间表来获取分表键买家ID,然后根据买家精准定位对应的订单库订单表。
一般我们中间表设计的过程中最好遵循以下几个原则
- 中间表尽可能字段少,为了保证查询效率
- 添加必须的查询条件,中间表直接过滤,避免过多数据的回主表查询
- 尽量不要添加主表中频繁修改的字段,避免主表修改后过多的同步中间表
中间表带来的问题
- 性能问题,虽然说不建议添加添加频繁修改的字段到中间表中,但是很多时候频繁修改字段作为我们查询的常用条件必须要进中间表,比如订单状态,卖家根据订单状态来筛选是一个很常见且合理的业务需求。所以在主表中订单状态发生改变后必须同步到中间表。
- 日益膨胀的问题,随着业务的发展,查询条件会日益增加导致中间表会日益膨胀,到最后中间表也会由着数据多和大引起的一系列性能问题
- 总结: 是难以适应灵活多变的查询场景。
多维度分库分表
多维度分库分表指的是按照不同的维度都进行分库分表,保存多份数据。还是上面的案例,我们可以对订单库按照卖家维度也进行一次分库分表

原本订单表是按照买家的 ID 来进行的,但是这种情况下,卖家查询订单就很困难。比如说卖家查询自己当日成交的订单量,就难以支持。而且本身卖家查询订单也不能算是一个低频行为,所以我尝试把数据复制了一份出去,然后按照卖家 ID 进行分库分表。这种方案的主要缺陷就是数据一致性问题,以及数据复制一份需要很多存储空间。
如果考虑到存储压力和同步压力,订单相关的表没必要全部都复制一遍。比如订单详情这一类占用空间大的表,需要用的时候直接查询买家库的详情表即可。这里和中间表不同的是,这里满足了卖家大部分查询需求,只有少部分查询是需要去查询买家库。
多维度分表应该遵循以下原则:
- 不同维度之间应该有主次之分。非主维度只支持查询,修改和数据同步由主维度发起,避免多维度修改后造成的数据不一致问题
- 其他维度库应该尽量满足本维度列表查询的所有条件和内容,包括统计。查询主维度库只支持单一单号详情的场景,这样可以有效的避免查多个主维度库和主维度表的情况
使用中间件
这里一般采用Elasticsearch作为其他维度搜索和聚合的中间件方案

因为Elasticsearch的支持海量数据和多维度搜索的特性,所以提供给其他维度查询。 这里同步还是部分数据同步到Elasticsearch中。具体单个精准详情的查询还是走主维度数据库的方式
这种方案的问题:
- Elasticsearch的数据实时性不够,对于高度要求实时的场景不是很满足
- 还是部分数据同步的原因,总有一些业务无法满足
数据同步的问题
不管是中间表,多维度分库分表,还是中间件的方式都存在一个重要的环节,数据同步的问题,下面简单介绍一下数据同步的方案有哪些
双写

一般由业务系统业务里面编码完成。需要注意的是
- 数据库相关的中间表和多维度要和主数据库保持一个事务,避免数据回滚导致的数据不一致
- Elasticsearch的同步需要在主数据库事务完成之后再进行同步
canal 数据同步

利用 Canal 之类的框架监听 binlog,然后异步地把数据库同步到其他地方。
总结
- 分库分表键的选择要注意根据候选项和业务需求来筛选。
- 分库分表后的非分表键的查询方案,包括引入中间表、多维度分库分表、使用其他中间件三个基础方案,以及分析了每个方案的简单实现方式和优点和缺点。
- 数据同步的方式包括双写和canal同步