摘要:github:https://github.com/bjlhx15/framework-middleware.git
分库分表方式
垂直拆分与水平拆分
表结构与数据,user
1 | id name age gender address |
垂直拆分【纵向拆分】
表结构拆分,如上分为:
- 示例
表1,user_base1
2
3id name age gender
1 张三1 15 男
2 李四2 17 男
表2,user_ext
1 | id address |
- 优点:
- 拆分后业务清晰,查分规则明确
- 系统之间整合扩展容易
- 按照成本、应用等级、应用类型等将表放置在不同的机器上,便于管理
- 便于实现动静分离、冷热分离的数据库表的设计模式
- 数据维护简单
- 缺点
- 部分业务表无法关联【join】、只能通过接口方式解决。提高了复杂度
- 存在单库、单表性能瓶颈。不易进行数据扩展和性能提升
- 事务处理复杂。
水平拆分
数据拆分,如上表可分为:
示例
表1,user11
2
3id name age gender address
1 张三1 15 男 北京市大兴区
3 李四3 17 男 北京市海淀区表2,user2
1
2
3id name age gender address
2 张三2 15 男 北京市大兴区
4 李四4 17 男 北京市海淀区优点:
- 单库单表数量级一定,有助于性能的提升
- 应用改造少,只需增加路由规则
- 提高了系统稳定性和负载能力
- 缺点:
- 数据分散,使用join较难,性能差
- 拆分规则难以抽象
- 分片事务的一致性难以解决
- 数据扩容的难度和维护量极大
小结
共同问题:
- 分布式事务问题
- 跨节点join问题
- 存在跨节点合并排序、分页问题
- 多数据源管理问题
如上,垂直拆分,倾向于业务拆分,技术上比较关注水平拆分。。
使用数据库的三个阶段
单库单表→单库多表→多库多表
分库分表适用情况
- 数据表瓶颈【mysql 单表5000w数据达到极限】
- 数据库吞吐瓶颈
- 未来预留设计
解决实施方案
主要有三类:客户端分片、代理分片、支持事务的分布式数据库
客户端分片
应用端实现,每个应用层直接嵌入分片逻辑,一般一个jar。主要有三种方式:应用层直接实现、通过定制JDBC协议实现、通过定义ORM框架实现。
应用层直接实现
直接在应用层,根据分片字段读取分片规则,然后解析分片规则。根据分片规则实现切分的路由逻辑,从应用层直接决定每次操作应该使用哪个数据库实例、数据库以及数据库表。通常实现方式是封装成一个jar。
- 优点:简单、快速、方便定位问题
- 缺点:业务侵入、产生较多的数据库连接
通过定制JDBC协议实现
在jdbc层进行处理,参看sharding jdbc
- 优点:业务透明,不侵入
- 缺点:需要了解jdbc协议
通过定义ORM框架实现
把分片规则实现到orm框架或者通过ORM框架的扩展机制来完成分库分表逻辑。如mybatis,
1 | <select id="getUser" parameterType="java.util.Map" resultType="User"> |
代理分片
在应用层与数据库层中增加一个代理层,把分片的路由规则配置在代理层,代理层对外提供与JDBC兼容的接口给应用层,应用层开发不必关心分片规则。
- 优点:应用开发专注业务逻辑
- 缺点:增加了代理层,而且数据库部署一般与应用分离。
框架:Cobar和Mycat
支持事务的分布式数据库
如:OceanBase、TiDB,对外提供JDBC接口。
分库分表的架构设计
如上,垂直拆分,倾向于业务拆分,技术上比较关注水平拆分。故后续研究是水平拆分
水平切分方式的路由过程和分片维度
路由过程
在表设计时,需要确定对表按照什么样的规则进行分库分表。
针对输入的请求,通过分库分表规则查找到对应的表和库的过程叫做路由。
水平切分的分片维度
按照哈希切片
对数据分某个字段求哈希,在除以分片总数后取模,取模后相同的数据为一个分片,这种将数据分成多个分片的方法叫哈希分片。
设计系统时,要充分考虑如何设计数据库的分库分表的路由规则。
- 优点:数据分散均匀,数据压力分散的效果比较好
- 缺点:查询需求需要聚合处理。
按照时间切片
按照指定时间周期进行切片【如按年,按月】,主要取决于数据交易量。
针对具有明确时间特点的数据进行切分。
分片后的事务处理机制
分布式事务
由于将单表的数据切片后存储在多个数据库甚至多个数据库实例中,所以依靠数据库本身的事务机制不能满足所有场景的需要。
主流分布式事务解决方案:两阶段提交协议、最大努力保证模式、事务补偿机制
两阶段提交协议
将分布式事务分为两个阶段,一个是准备阶段,一个是提交阶段,两个阶段都是由事务管理器发起。
基于两阶段提交协议,事务管理器能够最大限度地保证跨数据库操作的事务的原子性,是分布式系统环境下最严格的事务实现方法。
符合J2EE规范的AppServer(如:webSphere、Jboss等)对关系型数据库数据源和消息队列都实现了两阶段提交协议。。只需在使用时配置即可。
- 缺点
两阶段提交协议,会有性能问题,难以进行水平伸缩,因为在提交事务的过程中,事务管理器需要和每个参与者进行准备和提交的操作的协调,在准备阶段锁定资源,在提交阶段消费资源,但是由于参与者较多,锁定资源和消费资源之间的时间差被拉长,导致响应速度较慢,在此期间产生死锁或者不确定结果的可能性比较大。因此在互联网行业里,为了追求性能的提升,很少时候两阶段提交协议。
另外,由于两阶段提交协议是阻塞协议,在极端情况下不能快速响应请求方,因此目前有提出过三阶段提交协议,解决了两阶段提交协议的阻塞问题,但仍然需要事务管理器在参与者之间协调,才能完成一个分布式事务。
最大努力保证模式
一种常用的保证分布式一致性的模式,最大努力保证模式适用于对一致性要求并不十分严格但是对性能要求较高的场景。
具体实现,在更新多个资源时,将多个资源的提交尽量延后到最后一刻处理,这样的话,如果业务流程出现问题,则所有的资源更新都可以回滚,事务仍然保持一致性。
唯一可能出现问题,在提交多个资源时发生了系统异常,如网络问题,一旦出现这种情况,就需要进行实时补偿,将已提交的事务进行回滚,同TCC模式类似。
示例:如 涉及两个操作,一个是消息消费,一个是更新数据库,需要保证分布式的一致性。
1 | 1、开始消息事务 |
1-4不是很重要,5、6 发生异常都会回滚,5-6之间异常,导致消息被重发,消息重复消费,需要通过幂等进行处理。
对于提交顺序,需要充分考虑。如数据库事务中嵌套远程调用事务,且远程调用事务是耗时任务,导致数据库事务拉长,拖垮数据库。
事务补偿机制
性能要求比较高场景中,两阶段提交协议不适用,最大努力保证模式会使多个分布式操作互相嵌套,也可能不适合。
事务补偿机制,提高性能,并且能够尽最大可能地保证事务的最终一致性。
在数据库分库分表后,如果涉及的多个更新操作在某一个数据库范围内完成,则可以使用数据库内的本地事务保证一致性;对于跨库的多个操作,可通过补偿和重试,使其在一定时间窗口内完成操作。