数据库高可用架构-分表分库
什么是分表分库?
分表是将一份大的表数据进行拆分后存放至多个结构一样的拆分表中;
分库就是将一个大的数据库拆分成类似于多个结构的小数据库。
分表分库需要考虑的因素?
如果使用分表分库,有3个通用技术需求需要实现。
1)SQL组合:因为关联的表名是动态的,所以需要根据逻辑组装动态的SQL。比如,要根据一个订单的ID获取订单的相关数据,Select语句应该针对(From)哪一张表?
2)数据库路由:因为数据库名也是动态的,所以需要通过不同的逻辑使用不同的数据库。比如,如果要根据订单ID获取数据,怎么知道要连接哪一个数据库?
3)执行结果合并:有些需求需要通过多个分库执行后再合并归集起来。假设需要查询的数据分布在多个数据库的多个表中(比如在order1里面的t_order_1,order2里面的t_order_9中),那么需要将针对这些表的查询结果合并成一个数据集。
分表分库的实现模式?
而目前能解决以上问题的中间件分为两类:Proxy模式、Client模式。
1)Proxy模式
下图为ShardingSphere官方文档中的Proxy模式图,重点看中间的Sharding-Proxy层。
这种设计模式将SQL组合、数据库路由、执行结果合并等功能全部放在了一个代理服务中,而与分表分库相关的处理逻辑全部放在了其他服务中,其优点是对业务代码无侵入,业务只需要关注自身业务逻辑即可。

2)Client模式
ShardingSphere官方文档中的Client模式如下图所示。这种设计模式将分表分库相关逻辑放在客户端,一般客户端的应用会引用一个jar,然后在jar中处理SQL组合、数据库路由、执行结果合并等相关功能。

两种模式的优缺点

两种模式对应的中间件

分表分库实现思路
技术选型这一难题解决后,具体如何落实分表分库方案呢?需要考虑5个要点。
1)使用什么字段作为分片主键?
选择字段作为分片主键时,一般需要考虑3个要求:数据尽量均匀分布在不同的表或库、跨库查询操作尽可能少、所选字段的值不会变(这点尤为重要)。
2)分片的策略是什么?
目前通用的分片策略分为根据范围分片、根据Hash值分片、根据Hash值及范围混合分片这3种。
a.根据范围分片:比如user_ID是自增型数字,把user_ID按照每100万份分为一个库,每10万份分为一个表的形式进行分片,见下图。
说明:这里说的是分表,分库就是把分表分组存放在一个库即可。

b.根据Hash值分片:指的是根据user_ID的Hash值mod(取模)一个特定的数进行分片(为了方便后续扩展,一般是2n)。
c.根据Hash值及范围混合分片:先按照范围分片,再根据Hash值取模分片。比如,表名=order_#user_ID% 10#_#hash(user_ID)%8,即分成了10×8=80个表,如图3-4所示。以上3种分片策略到底应该选择哪个?只需要考虑一点:假设之后数据量变大了,需要把表分得更细,此
3)业务代码如何修改?
业务代码修改需要注意几个要点:
a.如果使用微服务,对于特定表的分表分库,其影响面只为该表所在的服务,而如果是一个单体架构的应用做分表分库,那会很麻烦。因为单体架构里面会有很多的跨表关联查询,也就是说,很多地方会直接与订单表一起进行Join查询,这种情况下,要想将订单数据拆分到多个库、多个表中,修改的代码就会非常多。
b.在互联网架构中,基本不使用外键约束。
c.分库分表以后,与订单有关的一些读操作都要考虑对应的数据是在哪个库哪个表。可以的话,尽量避免跨库或跨表查询。
4)历史数据如何迁移?
常用的方案是监控数据库变更日志,将数据库变更的事件变成消息,存到消息系统,然后有个消费者订阅消息,再将变动的数据同步到查询数据库,如下图。

此数据迁移方案的基本思路为:旧架构继续运行,存量数据直接迁移,增量数据监听binlog,然后通过canal通知迁移程序迁移数据,等到新的数据库拥有全量数据且校验通过后再逐步切换流量到新架构。
数据迁移解决方案的详细步骤如下。
a.上线canal,通过canal触发增量数据的迁移。
b.迁移数据脚本测试通过后,将老数据迁移到新的分表分库中。
c.注意迁移增量数据与迁移老数据的时间差,确保全部数据都被迁移过去,无任何遗漏。
d.此时新的分表分库中已经拥有全量数据了,可以运行数据验证程序,确保所有数据都存放在新数据库中。
5)未来的扩容方案是什么?
随着业务的发展,如果原来的分片设计已经无法满足日益增长的数据量的需求,就需要考虑扩容了。扩容方案主要依赖以下两点。
a.分片策略是否可以让新表数据的迁移源只有一个旧表,而不是多个旧表?这就是前面建议使用2n分表的原因——以后每次扩容都能扩为2倍,都是把原来一张表的数据拆分到两张表中。
b.数据迁移。需要把旧分片的数据迁移到新的分片上,这个方案与上面提及的历史数据迁移一样。
