我说分布式之分库分表
分布式应用架构下,数据量及接口并发量大幅上升后,单数据库由于无法抗住大流量、高并发的请求,从而造成数据库查询缓慢,频繁锁记录等问题。
为了解决上述的问题,在分布式环境下,常用的应对高并发、大流量场景的方案之一就是分库分表。
垂直拆分
首先说一下垂直拆分。核心思路是:通过 降低单库(表)的大小来提高性能 。
如,交易场景下,将用户数据、订单数据、流水数据单独建库,
应用层拆分为用户、订单、流水等微服务,每类型服务只对一个业务库做操作,从而在物理上将数据拆分开来,保证其中某一个业务挂掉对别的服务没有影响。也能够在一定程度上增加系统抗并发的能力。
上面这种主要提到的是分库的情况,至于分表,道理也相似。
核心思路就是,将一个大表根据功能,拆分成一个个的子表,如:用户表可以根据业务情况拆分成用户基础信息表,用户详细信息表。订单表可以根据具体场景拆分为:订单主表,订单流水表等。
垂直拆分分析
这里我们对垂直拆分的优缺点进行分析
优点
- 专库专用,业务更加简洁清晰;
- 有利于维护,某个业务的变更只和本业务库有关;
- 有利于实现读写分离,冷热数据的分离等。
缺点
- 垂直拆分只是将对单库的访问拆分为对多库的访问,但随着访问量激增,单库的压力依旧很大;
- 由于拆分了数据库,各个业务之间无法通过sql进行关联查询,只能在应用层进行归并;
- 分布式事务下的数据一致性需要依赖最终一致性方案解决。
PS: 关于分布式事务相关的知识和技术点,我在之前的系列中已经系统的描述过,详情可以看
水平拆分
接着分析一下水平拆分。
水平划分是根据一定规则,如时间或id序列值等进行数据的拆分。比如根据时间拆分不同的数据库。
每个数据库中的表结构一致,但是数据是拆分的,所有的分库数据合在一起才是所有的业务数据。这样将访问压力分摊至各个水平表中,从而提升性能。
又比如根据业务id的值,根据规则(如取模)分成若干个表。每个表结构一致,(这点与垂直拆分相反)。
水平拆分分析
我们对水平拆分进行优缺点的分析。
优点
- 由于进行了拆分,单库/表中的数据量较之未拆分前,大幅度减少,查询性能明显提高
- 系统的整体的稳定性和负载能力得到提高,某个持久化节点down掉不影响所有的业务
- 由于是水平拆分,不需要改动业务代码
缺点
- 水平拆分需要定义拆分规则,该规则对业务层有侵入
- 数据量上升以后,系统扩容需要对分片数据进行迁移
- 由于水平拆分,之前对单表的维护变更为维护多表,对DBA的工作压力增加
- 与垂直拆分相同,由于数据分布到多个片上,依旧存在无法跨库join等的问题,同时仍旧存在分布式事务、数据一致性的问题。
解决方案
由于单纯使用垂直拆分或者水平拆分均有利有弊,因此我们可以针对业务的特性考虑同时采用垂直和水平两种拆分方式。
在业务层,通过垂直拆分将应用微服务化,对访问量大的数据库表进行单独拆分。对于业务属性强且数据量大的数据表进行水平拆分,如:订单表,订单流水表。让具有相关性的数据分表分布在同一个片上,从而实现既做了分库分表减轻了系统访问压力,还可以保证同一个用户的数据能够分布在同一个片上,尽量避免分布式事务。
举个例子:
- 用户表的数据访问量大,但基本上不需要和其他的业务表做关联查询(因为互联网业务场景下,我们在表结构设计时一般都会进行数据字段的冗余,设计时最多进行到第二范式)。对于用户表、配置信息表这类的数据,我们一般可以采用取模的方式进行水平的拆分。
- 对于订单表、流水表这种需要业务上进行关联查询的数据表,我们尽量通过同一个分片键进行订单、流水的id生成及入库操作。在入库时,能够保证同一个用户的订单、流水等业务信息分布到一个片上,这样我们就可以继续使用单库事务。
当前技术领域对分库分表已经有很多的成熟方案,能够让我们稍作规则定制即可开箱即用,不需要做针对性的开发,常见的有两种模式,代理模式,客户端模式。
框架名 | 模式 | 简介 |
---|---|---|
mycat | 代理模式 | 基于阿里开源的Cobar产品而研发,代理模式,更新较慢。 |
ShardingProxy | 代理模式 | 开源套件ShardingSphere生态中的组件,社区活跃 |
Shardingjdbc | 客户端模式 | 开源套件ShardingSphere生态中的组件,社区活跃,使用较多 |
Zdal | 客户端模式 | 蚂蚁金服闭源分库分表套件,性能较好,github有较老的版本 |
我们在业务开发中,大量使用成熟的客户端模式进行数据库层的操作。近年大火的数据库中间件ShardingSphere已经在业界有了很多的落地案例和最佳实践,因此在后续的文章中,我将展开讲解如何利用开源组件ShardingSphere中的Shardingjdbc进行分库分表,通过demo讲解和实战模拟的方式,较为系统的展示分布式场景下的数据访问层的开发,我们拭目以待。