从存储到数据库,从搜索到大数据, 数据管理技术总是充满挑战

分布式事务

分布式事务是个历久弥新的话题,对分库分表,分布式事务的目的是保障分库数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点永久性宕机,如此像单机事务一样的ACID是无法奢望的。另外,业界著名的CAP理论也告诉我们,对分布式系统,需要将数据一致性和系统可用性,分区容忍性放在天平上一起考虑。

两阶段提交协议(简称2PC)是实现分布式事务较为经典的方案,适用于中间件这种数据节点无耦合的场景。2PC的核心原理是通过提交分阶段和记日志的方式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个状态节点重试,如coordinator重启后,通过日志可以确定提交处于prepare还是prepareAll状态,若是前者,说明有节点可能没有prepare成功,或所有节点prepare成功但是还没有下发commit,状态恢复后给所有节点下发rollback;若是prepareAll状态,需要给所有节点下发commit,数据库节点需要保证commit幂等。与很多其他一致性协议相同,2PC保障的是最终一致性。

2PC整个过程如下图所示:

%e6%97%a0%e6%a0%87%e9%a2%987

2PC过程

在DDB中,DBI和Proxy组件都作为coordinator存在,2PC实现时,记录prepare和prepareAll的日志必须sync,以保障重启后恢复的状态是正确的,而coordinator最后的commit日志主要作用是回收之前日志,可异步执行。

由于2PC要求coordinator记日志,事务吞吐率受到磁盘IO性能的约束,为此DDB实现了group io优化,可极大程度提升2P C的吞吐率。2PC本质上说是一种阻塞式协议,两阶段提交过程需要大量线程资源,因此CPU和磁盘都有额外消耗,与单机事务相比,2PC在响应时间和吞吐率上会相差很多,从CAP的角度出发,可以认为2PC在一定程度上成全了C,牺牲了A。

另外,目前MySQL最流行的5.5和5.6版本中,XA事务日志无法replicate到从节点,这意外着主库一旦宕机,切换到从库后,XA的状态会丢失,可能造成数据不一致,这方面MySQL 5.7已经有所改善。

虽然2PC有诸多不足,我们依然认为在DDB中有实现价值,DDB作为中间件,其迭代周期要比数据库这种底层服务频繁很多,若没有2PC,一次更新或重启就可能造成应用数据不一致。从应用角度看,分布式事务的现实场景常常是无法规避的,在有能力给出其他解决方案前,2PC也是一个不错的选择。

对购物转账等电商和金融业务,中间件层的2PC最大的问题在于业务不可见,一旦出现不可抗力或意想不到的一致性破坏,如数据节点永久性宕机,业务难以根据2PC的日志进行补偿。金融场景下,数据一致性是命根,业务需要对数据有百分之百的掌控力,建议使用TCC这类分布式事务模型,或基于消息队列的柔性事务框架,这两种方案都实现在业务层,业务开发者具有足够掌控力,可以结合SOA框架来架构。原理上说,这两种方案都是大事务拆小事务,小事务变本地事务,最后通过幂等的retry来保障最终一致性。

弹性扩缩容

分库分表数据库中,在线数据迁移也是核心需求,会用在以下两种场景:

  1. 数据节点弹性扩容

随着应用规模不断增长,DDB现有的分库可能有一天不足以支撑更多数据,要求DDB的数据节点具有在线弹性扩容的能力,而新节点加入集群后,按照不同的sharding策略,可能需要将原有一些数据迁入新节点,如hash分区,也有可能不需要在线数据迁移,如一些场景下的range分区。无论如何,具备在线数据迁移是DDB支持弹性扩容的前提。

  1. 数据重分布

开发者在使用DDB过程中,有时会陷入困局,比如一些表的分区字段一开始没考虑清楚,在业务已经初具规模后才明确应该选择其他字段。又如一些表一开始认为数据量很小,单节点分布足以,而随着业务变化,需要转变为多节点sharding。这两种场景都体现了开发者对DDB在线数据迁移功能的潜在需求。

无论是弹性扩容,还是表重分布,都可当做DDB以表或库为单位的一次完整在线数据迁移。可分为两个阶段:全量迁移和增量迁移,全量迁移是将原库或原表中需要迁移的数据dump出来,并使用工具按照分区策略 apply到新库新表中。增量迁移是要将全量迁移过程中产生的增量数据更新按照分区策略apply到新库新表。