随着公司的业务发展不断的壮大,像一些核心的业务(如订单)数据量会越来越大,此时就需要考虑分库分表方案来应对业务的发展。今天就来聊聊分库分表的一些设计方案。
1、冷热数据分离方案
在我们业务中有些数据只是最近一段时间使用比较频繁,过着这段时间就基本上不用了,如龙虾之前负责的物流系统中的物流轨迹数据,一条物流单号对应着若干条物流轨迹数据,如下所示的物流轨迹:
一个物流单号(如YT20241234569)对应的轨迹有6条数据数据了,假设一天的订单量有2万单,此时至少有12万条物流轨迹产生, 日复一日的数据量积累,那么物流轨迹表的数据也是非常的庞大的。
从业务角度分析,按照用户的习惯来讲,某个订单待收货与交易成功之间的这段时间中我们是比较关心物流的轨迹的,一旦收到货之后基本很少再去看这单的物流轨迹信息,所以针对这种数据量大(物流轨迹数据)、只在某段时间内频繁关心的数据,我们可以使用冷热数据隔离的方案来解决数据量大的问题。下图使用物流轨迹数据冷热分离方案为案例分析:
(1)物流单号订阅物流系统,物流系统将物流单号订阅三方快递,一旦订阅成功之后,三方快递收到物流轨迹变动就会推送给物流系统,然后物流系统将数据存放到热表中;
(2)用户查询的时候优先从热表先查询数据,如果热表有物流轨迹的数据就直接返回数据给用户;如果热表中不存在物流数据,那么再去冷表中查询数据,将冷表的查询结果给用户;
(3)每天夜里(如凌晨两点)采用定时任务将一个月之前的数据都迁移到冷表中,这样可以保持热表中都是最近的数据。
至此就完成了一套使用通过冷热分离的方案实现对日增几十万条业务数据的处理。
2、分库分表方案
公司现有的业务体量非常大的,在读写分离、主从架构都无法满足现有的业务的时候,我们就可以考虑分库分表,为什么不优先考虑分库分表方案呢?因为业务数据越分散,开发和维护成本就越高,并且系统的不稳定性又多一些威胁因素。
分库分表是应对业务数据量大、高并发的重要手段之一,我们要搞清楚何时分库,何时分表,何时既分库也分表呢?
(a)分库的场景:在高并发下,数据库的连接不够用的时候,此时可以通过增加数据库的实例来增加数据库的连接数。如下所示的分库方式:
(b)分表的场景:如果单表的数据量很庞大,此时数据库的连接是够用的,但是存储和查询的性能已经成为业务瓶颈,那么就考虑分表。如下图所示的分片:
(c)既分库也分表的场景:数据库的连接不够,并且表数据量很庞大此时一般需要考虑既要分库也要分表。但是具体分多少库分多少表实际的业务预估数据量来做决定,如下图所示的既分库也分表的图:
在确定了需要分库分表后就需要考虑将数据分到哪个库或者哪张表中,下面介绍4种主流的切分:
(1)Range法
此算法是按照某个字段(如订单id、用户id)的数据区间来进行切分的,可以将数据切分到同一个数据库的不同表中,如下所示:
也可以将数据切分到不同库的不同表中,如下所示:
Range算法对于需要扩容来说是非常的友好的,因为只需要添加一张数据表,通过算法就可以自动实现扩容机制。同时Range算法也存在写偏移和热点数据问题。
(2)hash分片算法
该方案是通过对分表键key进行某种运算(如取模运算),然后通过运算结果来决定路由的库和表,如下图所示:
hash分片方案可以使得数据分片比较均匀,大大降低数据倾斜和热点数据的问题; hash分片方案的缺点也很明显,如后期扩容存在一定的难度,需要迁移数据;数据被切分到不同的库和表中,存在查询和分页等问题;
(3)查表映射法
此方案的实现原理是将决定某个sharding key落在哪个分片上靠人为的预先制定的策略(策略记录在数据表中)来分配,如下所示的分配流程:
查表映射法可以灵活设置路由规则,但是要求映射表本身的数据不能太多,否则映射表反而成为性能瓶颈了。
查表映射法相对其他两种分片算法来说,它需要二次查询、实现上也更加的复杂一些。
(4)一致性hash
一致性hash可以按照普通的hash算法对key哈希到一个圆环空间上,形成一个顺时针的首位闭合的环形,如下图所示:
此时key1顺时针放在节点A上,同理key放在B节点上,如果A、B、C节点假设分布不均匀,我们可以使用虚拟节点的方案来处理,之前龙虾也介绍过一致性hash,有兴趣的朋友可以在看下:
深入理解一致性Hash和虚拟节点
3、分库分表带来的问题
(1)分布式id
单库单表的时代,我们可以直接使用表自增主键保证全局唯一,分库分表后,需要自己维护全局唯一的ID。生成全局唯一id的方案如下整理:
整理10种分布式id生成方案
(2)分布式事务
分库分表之后可能就需要引入分布式事务的问题,解决方案如下整理:
罗列分布式事务解决方案
(3)分页查询问题
单库单表的时候我们可以使用数据的limit来进行分页查询,但是分库分表后就出现分页查询的问题了,常见的处理方案如下:
(a)使用Elasticsearch;
(b)特定的条件先分页查询;按照某个字段先分页查询数据,查询好之后再去查询组装其他的数据。