在平常讨论技术方向的时候,常常听伙伴抛出一些概念高可用性,一致性关键词。可是未必每一个
人都能把这些概念说清楚。今天咱们就来聊聊分布式事物那些事,分享一下咱们在项目中是如何使用的。
1、CAP理论web
1.什么是CAP理论数据库
2000年,Eric Brewer教授在PODC的研讨会上提出了一个猜测。一致性、可用性和分区容错性三者没法在分布式系统中被同时知足,而且最多只能知足其中两个!Brewer教授当时想象的分布式场景是webservice,一组websevrice后台运行着众多的server,对service的读写会反应到后台的server集群。网络
强一致性 (Consistency):系统在执行过某项操做后仍然处于一致的状态。在分布式系统中,更新操做执行成功后全部的用户都应该读取到最新的值,这样的系统被认为具备强一致性。
可用性 (Availability):每个操做老是可以在必定的时间内返回结果,超时和不能返回结果均不符合条件。
分区容错性(Partition Tolerance):分区容错性能够理解为系统在存在网络分区的状况下仍然能够接受请求。这里网络分区是指因为某种缘由网络被分红若干个孤立的区域,而区域之间互不相通。还有一些人将分区容错性理解为系统对节点动态加入和离开的处理能力,由于节点的加入和离开能够认为是集群内部的网络分区。架构
2.系统为何不能同时知足CAP特性app
以下图所示,在网络中有两个节点分别为G1和G2,这两个节点上存储着同一数据的不一样副本,如今数据是一致的,两个副本的值都为V0,A、B分别是运行在G一、G2上与数据交互的应用程序。异步
在正常状况下,操做过程以下(以下图所示):
(1) A将V0更新,数据值为V1;
(2) G1发送消息m给G2,数据V0更新为V1;
(3) B读取到G2中的数据V1。分布式
若是发生网络分区故障,那么在操做的步骤(2)将发生错误:G1发送的消息不能传送到G2上。这样数据就处于不一致的状态,B读取到的就不是最新的数据,以下图所示。若是咱们采用一些技术如阻塞、加锁、集中控制等来保证数据的一致,那么必然会影响到系统的可用性和分区容错性。因此同时知足三点,老是须要放弃一部分。spa
系统知足三个条件中不一样的两个条件会具备不一样的特色
放弃P,保证AC:若是想避免分区容错性问题的发生,一种作法是将全部的数据(与事务相关的)都放到一台机器上。
放弃A,保证CP:一旦遇到分区容错故障,那么受到影响的服务须要等待数据最终一致性
放弃C,保证AP:这里所说的放弃一致性,而保留数据的最终一致性。该方式也是解决分布式事物的主流方式。日志
2、分布式事物解决方案code
eBay的架构师Dan Pritchett 在2008年发给ACM的文档。文章中描述了一个最多见的场景,若是产生了一笔交易,须要在交易表增长记录,同时还要修改用户表的金额。这两个表属于不一样的远程服务,涉及到分布式事务一致性的问题。经典的解决方法,引入本地消息表和消息中间件,将写入本地消息表、更新用户表,发送Q放在一个本地事务中完成。同时为了不重复消费用户表消息带来的问题,保证幂等性增长一个接收端记录表。
举个例子,系统中有如下两个表user,transaction.
其中user表记录用户交易汇总信息,transaction表记录每一个交易的详细信息。transaction表和user表存储在不一样的节点上,那么上述事务是一个分布式事务。要消除这一分布式事务,将它拆分红两个子事务。保证消息的幂等性还须要在接收端建立消息接收表message_applied(msg_id),保证不能重复接收消息。
begin transaction insert into transaction(xid, $seller_id, $buyer_id, $amount); put_to_queue "update user("seller",$seller_id,amount)"; put_to_queue "update user("buyer",$buyer_id,amount)"; end transaction -------------------------------------------------------- for each message in queue peek message begin transaction select count(*) as cnt from message_applied where msg_id=message.id and balance = message.balance and user_id=message.user_id if cnt == 0 then if message.type = “seller” then update user SET amt_sold = amt_sold + message.amount where id = message.user_id; else update user set amt_bought = amt_bought + message.amount where id = message.user_id; insert into message_applied values(message.id); end end transaction end fors
第一阶段:transation表和操做队列在同一实例上,能够经过本地的数据库的事务保证。
第二阶段:message_applied表和user表在同一个实上,能够经过本地的数据的事务保证,当有重试或者重复消息过来时,经过message_applied表能够判断是不是重复数据。
3、实际项目对理论的实践
①应用A的业务逻辑、写本地消息表、写消息队列放在一个队列里。写消息队列业务逻辑放在最后。
②全部步骤执行成功,事物提交。
③有一步骤执行失败,则进行回滚。
④此方案须要在记录服务A发送的完整消息日志,以防Q出现问题进行消息补发,在B端建立接收表,去重,保证消息的幂等性。
本场景保证事物一致性有两种解决方案,一种是调用中间服务出错后回滚掉其余全部服务的数据,这须要每一个应用提供回滚服务。另外一种认为服务必定会调用成功,一旦出现问题,会不断重试。下面会根据这两种状况进行详细说明。
第一, 服务调用采用回滚方式处理,以下示意图所示
以上思路仍是借鉴ebay模型来解决最终一致性问题。第二,不断重试保证消息一致性相对第一种状况要简单一些。一旦调用异常须要记录到错误表中不断的进行重试。