作为一个前端专业的人来说,对于事务的理解,一直停留在“要么都成功,要么都不成功”的小白阶段。既然自己将2018年定义为”深入理解“的一年,那么就从深入理解事务开始吧。
正如文章开头所说的:事务是一系列的动作,这些动作必须全部完成,如果有一个失败,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。在企业级应用的开发过程中,事务管理是必不可少的技术,用来确保数据的完整性和一致性。
事务有四个特性,也就是经常被提到的ACID:
上面我们说到的事务,也可以称为是”本地事务“。目前许多框架,都能够很方便的支持本地事务。比如Spring Boot,只需要在方法前加上”@Transaction“的注解,就可以愉快的使用事务了。
但是,事务到此未知就结束了吗?不是的,随着企业应用越来越复杂,应用的架构也从单体架构演变到了SOA,还有现在炙手可热的微服务。这时候,又出现了分布式事务的概念。
分布式事务,简单来说就是指对数据库的处理操作分布在不同的节点之上,而且操作的数据,分布于不同的数据库。分布式事务,需要保证不同数据库的数据一致性。如下图:
处于数据量或者数据隔离的考虑,实际开发中需要进行分库分表。原来一个库现在变成了多个库,这时候要保证数据一致性,就要用到分布式事务。
所谓的SOA话,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务。
说到分布式事务,就离不开CPA原则与BASE方案。
CPA指的是,在一个分布式系统中,一致性(C)、可用性(A)、分区容错性(P),三者不可兼得。CPA是NoSQL数据库的基石。
CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。
BASE就是为了解决关系数据库强一致性引起的问题而引起的可用性降低而提出的解决方案。
BASE是下面三个术语的缩写:
两阶段提交(Two Phase Commit, 2PC), 具有强一致性, 是CP系统的一种典型实现,常见的标准是XA,JTA等。例如Oracle的数据库支持XA。
下面是两阶段提交的示意图:
图的上半是两阶段提交成功的演示, 下半是两阶段提交失败的演示。
两阶段提交目前并不是主流的解决方案,其主要原因是:协调者需要等待所有参与者发出yes请求,或者一个参与者发出no请求后,才能执行提交或者终端操作。这会造成长时间锁住多个资源,造成性能瓶颈。如果参与者有一个耗时长的操作, 性能损耗会更明显;还有一个缺点,就是实现复杂,不利于系统的扩展。
TCC, 是基于补偿型事务的AP系统的一种实现, 具有最终一致性。所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。
通过将一系列同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步阻塞操作的影响。基于消息执行就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:
执行步骤如下:
需要额外说明的一点, 就是事务消息投递到MQ订阅方后, 并不一定能够成功执行. 需要MQ订阅方主动给予消费反馈(ack)
此方案适用于执行周期较长,实时性要求不高的场景。
这是分布式事务中要求最低的一种, 也可以通过消息中间件实现, 与前面异步确保型操作不同的一点是, 在消息由MQ Server投递到消费者之后, 允许在达到最大重试次数之后正常结束事务.这种方案适用于交易结果消息的通知等
最近两年,微服务的呼声越来越高,不可避免的,微服务也会面临事务的困扰。
事务,尤其是分布式事务,是一个很大的话题,除了上述列出的几种解决方案,根据不同的业务要求,还有许多其他的解决方案。按照控制力度,分布式事务分为部分控制和完全控制两种: