精华内容
参与话题
问答
  • using System; using System.Collections.Generic;... /// 一致性HASH,解决传统HASH的扩容难的问题 /// 注:常用与分布式缓存与分表 /// </summary> /// <typeparam name="T">泛型</typ
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Core.Util
    {
        /// <summary>
        /// 一致性HASH,解决传统HASH的扩容难的问题
        /// 注:常用与分布式缓存与分表
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        public class ConsistentHash<T>
        {
            SortedDictionary<int, T> circle { get; set; } = new SortedDictionary<int, T>();//虚拟的圆环,对2的32方取模
            int _replicate = 100;    //虚拟节点数 count
            int[] ayKeys = null;    //缓存节点hash
    
            /// <summary>
            /// 初始化可迭代的节点数
            /// </summary>
            /// <param name="nodes">节点</param>
            public void Init(IEnumerable<T> nodes)
            {
                Init(nodes, _replicate);
            }
            /// <summary>
            /// 初始化可迭代的节点数,默认不缓存
            /// </summary>
            /// <param name="nodes">节点</param>
            /// <param name="replicate"></param>
            public void Init(IEnumerable<T> nodes, int replicate)
            {
                _replicate = replicate;
    
                foreach (T node in nodes)
                {
                    this.Add(node, false);
                }
                ayKeys = circle.Keys.ToArray();
            }
            /// <summary>
            /// 添加节点,缓存
            /// </summary>
            /// <param name="node"></param>
            public void Add(T node)
            {
                Add(node, true);
            }
            /// <summary>
            /// 添加虚拟的圆环的节点
            /// </summary>
            /// <param name="node">节点</param>
            /// <param name="updateKeyArray">是否缓存node的hash</param>
            private void Add(T node, bool updateKeyArray)
            {
                for (int i = 0; i < _replicate; i++)
                {
                    int hash = BetterHash(node.GetHashCode().ToString() + i);
                    circle[hash] = node;
                }
    
                if (updateKeyArray)
                {
                    ayKeys = circle.Keys.ToArray();
                }
            }
            /// <summary>
            /// 删除真实机器节点,更新缓存
            /// </summary>
            /// <param name="node"></param>
            public void Remove(T node)
            {
                for (int i = 0; i < _replicate; i++)
                {
                    int hash = BetterHash(node.GetHashCode().ToString() + i);
                    if (!circle.Remove(hash))
                    {
                        throw new Exception("can not remove a node that not added");
                    }
                }
                ayKeys = circle.Keys.ToArray();
            }
    
            /// <summary>
            /// 判断是否存在key对应的hash,有则返回没有返回最近一个节点
            /// </summary>
            /// <param name="key"></param>
            /// <returns></returns>
            private T GetNode_slow(String key)
            {
                int hash = BetterHash(key);
                if (circle.ContainsKey(hash))
                {
                    return circle[hash];
                }
                // 沿环的顺时针找到一个节点
                int first = circle.Keys.FirstOrDefault(h => h >= hash);
                if (first == new int())
                {
                    first = ayKeys[0];
                }
                T node = circle[first];
                return node;
            }
    
            int First_ge(int[] ay, int val)
            {
                int begin = 0;
                int end = ay.Length - 1;
    
                if (ay[end] < val || ay[0] > val)
                {
                    return 0;
                }
    
                int mid = begin;
                while (end - begin > 1)
                {
                    mid = (end + begin) / 2;
                    if (ay[mid] >= val)
                    {
                        end = mid;
                    }
                    else
                    {
                        begin = mid;
                    }
                }
    
                if (ay[begin] > val || ay[end] < val)
                {
                    throw new Exception("should not happen");
                }
    
                return end;
            }
    
            public T GetNode(String key)
            {
    
                int hash = BetterHash(key);
    
                int first = First_ge(ayKeys, hash);
    
                return circle[ayKeys[first]];
            }
    
            /// <summary>
            /// MurMurHash2算法,性能高,碰撞率低
            /// </summary>
            /// <param name="key">计算hash的字符串</param>
            /// <returns>hash值</returns>
            public static int BetterHash(String key)
            {
                uint hash = MurmurHash2.Hash(Encoding.ASCII.GetBytes(key));
                return (int)hash;
            }
        }
    }
    

    MurMurHash2算法

    using System;
    using System.Runtime.InteropServices;
    
    namespace Core.Util
    {
        public class MurmurHash2
        {
            public static UInt32 Hash(Byte[] data)
            {
                return Hash(data, 0xc58f1a7b);
            }
            const UInt32 m = 0x5bd1e995;
            const Int32 r = 24;
    
            [StructLayout(LayoutKind.Explicit)]
            struct BytetoUInt32Converter
            {
                [FieldOffset(0)]
                public Byte[] Bytes;
    
                [FieldOffset(0)]
                public UInt32[] UInts;
            }
    
            public static UInt32 Hash(Byte[] data, UInt32 seed)
            {
                Int32 length = data.Length;
                if (length == 0)
                    return 0;
                UInt32 h = seed ^ (UInt32)length;
                Int32 currentIndex = 0;
                // array will be length of Bytes but contains Uints
                // therefore the currentIndex will jump with +1 while length will jump with +4
                UInt32[] hackArray = new BytetoUInt32Converter { Bytes = data }.UInts;
                while (length >= 4)
                {
                    UInt32 k = hackArray[currentIndex++];
                    k *= m;
                    k ^= k >> r;
                    k *= m;
    
                    h *= m;
                    h ^= k;
                    length -= 4;
                }
                currentIndex *= 4; // fix the length
                switch (length)
                {
                    case 3:
                        h ^= (UInt16)(data[currentIndex++] | data[currentIndex++] << 8);
                        h ^= (UInt32)data[currentIndex] << 16;
                        h *= m;
                        break;
                    case 2:
                        h ^= (UInt16)(data[currentIndex++] | data[currentIndex] << 8);
                        h *= m;
                        break;
                    case 1:
                        h ^= data[currentIndex];
                        h *= m;
                        break;
                    default:
                        break;
                }
    
                // Do a few final mixes of the hash to ensure the last few
                // bytes are well-incorporated.
    
                h ^= h >> 13;
                h *= m;
                h ^= h >> 15;
    
                return h;
            }
    
        }
    }
    
    
    展开全文
  • 最近在看ES一些深入的功能,就很好奇分片与副本的数据一致性是怎么处理的(类似于数据库分布式事务问题),果然在官方文档上找到了相关描述,以及大概的方案,本想整理一篇文章,但是看到了有类似的,我就奔着不重复造...

    最近在看ES一些深入的功能,就很好奇分片与副本的数据一致性是怎么处理的(类似于数据库分布式事务问题),果然在官方文档上找到了相关描述,以及大概的方案,本想整理一篇文章,但是看到了有类似的,我就奔着不重复造轮子的记录一下。
     

    官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-index_.html#index-wait-for-active-shards

    官方文档的解读:https://my.oschina.net/manmao/blog/3103640

    相关的验证以及思考:https://www.cnblogs.com/hapjin/p/9821073.html

    展开全文
  • 目录 一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题 (一)本地事务和分布式事务(2PC+3PC) ...一致性Consistency: 可用性Availability: 分区容错性PartitionToler...

    目录

    一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题

    (一)本地事务和分布式事务(2PC+3PC)

    (1)两阶段提交协议2PC

    (2)三阶段提交协议3PC

    (二)对于微服务,传统分布式事务存在的问题

    二、CAP理论和BASE思想

    1.CAP理论

    一致性Consistency:

    可用性Availability:

    分区容错性PartitionTolerance:

    2.BASE思想

    BasicallyAvailiable(基本可用):

    SoftState(软状态):

    EventualConsistency(最终一致性):

    3.CAP理论和BASE思想的关联性

    三、可靠事件模式

    1.基本思路

    (1)用户下单

    (2)交易支付

    (3)订单更新

    2.关键点

    3.解决方案

    4.实现策略

    (1)事件确认组件

    (2)事件恢复组件

    (3)实时消息传递组件

    四、补偿模式

    1.基本思路

    2.关键点

    3.解决方案

    五、Sagas长事务模式--错误管理模式,同时用于控制复杂事务的执行和回滚

    1.基本思路

    2.解决方案

    六、TCC模式

    1.基本思路

    2.解决方案

    3.实现策略

    七、最大努力通知模式

    1.基本思路

    2.解决方案

    八、人工干预模式

    1.基本思路

    2.解决方案

    九、数据一致性模式总结

    参考书籍、文献和资料:


    一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题

    (一)本地事务和分布式事务(2PC+3PC)

    传统单体应用一般都会使用一个关系型数据库,好处是使用ACID事务特性,保证数据一致性只需要开启一个事务,然后执行更新操作,最后提交事务或回滚事务。更方便的是可以以借助于Spring等数据访问技术和框架后只需要关注引起数据改变的业务本身即可。

    但是随着组织规模不断扩大、业务量不断增加,单块应用不足以支持庞大业务量和数据量,就需要对服务和数据进行拆分,拆分后就会出现两个或两个以上的数据库的情况,此时不能依靠本地事务得以解决,需要借助于分布式事务来保证一致性,常说的就是保持强一致性的两阶段提交协议和三阶段提交协议。

    (1)两阶段提交协议2PC

    准备阶段:由协调者提议并收集其他节点参与者的反馈(提议的节点为协调者,参与决议的节点为参与者)。

    执行阶段:根据反馈决定提交或中止事务。

    如图,协调者发起一个提议分别询问各参与者是否接受场景,然后协调者根据参与者的反馈,提交或中止事务。

    注意:只有当所有参与者都同意才提交,否则只有有一个不同意就中止。            

    但是,2PC有其固有的三大问题:

    问题一:同步阻塞问题

    执行过程中,所有参与者都是事务阻塞型的。当参与者占用公共资源时,其他第三方节点访问公共资源就不得不处于阻塞状态。

    问题二:单点故障

    一旦协调者发生故障,参与者会一直阻塞下去。特别是在第二阶段,协调者发生故障,则所有参与者还处于锁定资源的状态中,但是无法完成后续的事务操作。

    问题三:数据不一致

    当协调者向参与者发送提交请求后发生了局部网络异常,或者在发送提交请求过程中协调者发生了故障,就会导致只有一部分参与者接收到了提交请求,这部分参与者接到请求后就会执行提交操作,而未接收到提交请求的机器就无法执行事务提交,于是就出现了数据不一致的问题。

    (2)三阶段提交协议3PC

    与2PC相比,3PC主要有两个改动点:在协调者和参与者之间都引入了超时机制+把准备阶段一分为二

    3PC:CanCommit + PreCommit + DoCommit,具体操作如下

    CanCommit阶段:协调者向参与者发送提交请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

    PreCommit阶段:协调者根据参与者的响应情况来决定是否可以进行事务的PreCommit操作。

    如果协调者从所有参与者获得反馈都是Yes响应,那么就执行事务的预执行;

    如果有任何一个参与者向协调者发送了No响应,或者等待超时后没有收到参与者的响应,那么就执行事务的中断。

    DoCommit阶段:执行提交或中断事务。当协调者没有收到参与者发送的ACK响应,就会执行中断事务。

    可见,3PC主要解决了2PC的单点问题和同步阻塞问题。

    (二)对于微服务,传统分布式事务存在的问题

    在微服务架构中,传统分布式事务并不是实现数据一致性的最佳选择,主要有三大问题:

    问题一:对于微服务架构来说,数据访问变得更加复杂,为达到微服务间的松耦合和高度独立,数据是微服务私有的,唯一可访问的方式就是通过API,采用2PC/3PC难度太大。

    问题二:不同的微服务经常使用不同的数据库,但是在微服务架构中,服务会产生不同类型的数据,关系数据库不一定是最佳选择,很多微服务会采用SQL和NoSQL结合模式,如搜索引擎、图数据库等NoSQL数据库大多数都不支持2PC/3PC。

    问题三:当数据被拆分了或者在不同的数据库存在重复数据的时候,锁定资源和序列化数据来保证一致性就会变成一个非常昂贵的操作,会给系统的吞吐量以及扩展性带来巨大的挑战。

    对于微服务架构,建议采用一种更为松散的方式来维护一致性,也就是所谓的最终一致性,对于开发者而言,实现最终一致性的方案可以根据不同的业务场景做不同的选择。

    二、CAP理论和BASE思想

    1.CAP理论

    指的是在一个分布式系统中,无法同时实现一致性Consistency、可用性Availability和分区容错性PartitionTolerance。

    对于典型分布式系统而言,如图所以,这三个概念可以做以下解释:

    一致性Consistency:

    指分布式系统中的所有数据备份在同一时刻是否拥有同样的值。

    可用性Availability:

    指在集群中一部分节点故障后,集群整体是否还能正常响应请求。

    分区容错性PartitionTolerance:

    相当于通信的时限要求,系统如果不能在一定时限内达到数据一致性,就意味着发生了分区情况,也就是说整个分布式系统就不在互联了。

    由于当前网络硬件肯定会出现延迟丢包等通信异常问题,三态性不可避免,所以分区容错性必须实现。

    2.BASE思想

    BASE=BasicallyAvailiable(基本可用)+SoftState(软状态)+EventualConsistency(最终一致性)

    BASE理论是对CAP理论的延伸,基本思想是即使无法做到强一致性,应用可以采用适合的方式达到最终一致性。

    BasicallyAvailiable(基本可用):

    指分布式系统在出现故障的时候,可以损失部分可用性,需要保证和核心可用。服务限流和降级就是其基本表现。

    SoftState(软状态):

    指允许系统存在中间状态,而中间状态不会影响整体可用状态。分布式存储中一般一份数据都有有若干个副本,允许不同节点间副本同步的延时就是软状态的体现。

    EventualConsistency(最终一致性):

    指系统中所有的数据副本经过一定时间后,最终能够达到一致的状态。

    CAP的一致性就是强一致性,这种一致性级别是最符合用户直觉的,用户体验好,但实现起来对系统性能影响较大弱一致性正好相反。BASE中的最终一致性可以看做是弱一致性的一种特殊情况。

    3.CAP理论和BASE思想的关联性

    CAP理论和BASE思想实际上是有关联的,基本关系如图所示:                                                          

    CAP理论和BASE思想讨论的意义在于即使无法做到强一致性,我们也可以采用合适的方式达到最终一致性,这点对微服务架构很重要。最终一致性的方法重要有以下六种分别讲解。

    在微服务架构中,比如,对于一个完整的下单操作而言,订单服务和支付服务都是业务闭环中的一部分,在一个完整的业务操作流程中需要保证各自数据的正确性和一致性。

    三、可靠事件模式

    1.基本思路

    尝试将订单和支付两个微服务进行分别管理,并需要一个媒介用于这两个微服务之间进行数据传递,一般而言,消息中间件MOM适合扮演数据传递媒介的角色。引入消息中间件后下单操作流程可以拆分为如下三个步骤:

    (1)用户下单

    当用户使用订单服务下单时,一方面订单服务需要对所产生的订单数据进行持久化操作,另一方面,也需要同时发送一条创建订单的消息到消息中间件。

    (2)交易支付

    当消息中间件接收到订单创建消息,就会把消息发送到支付服务。

    支付服务接收到订单创建消息后,同样对该消息进行业务处理并持久化。当所有关于支付相关的业务逻辑执行完成以后,支付服务需要向消息中间件发送一条支付成功的消息。

    (3)订单更新

    支付成功消息通过消息中间件传递到订单服务时,订单服务根据支付的结果处理后续业务流程,一般涉及订单状态更新、向用户发送通知内容等内容。

    2.关键点

    对于以上三个闭环管理,仔细分析整个过程,不难发现存在如下三个基本问题:

    (1)某个服务在更新了业务实体后发布消息失败;

    (2)服务发布事件成功,但是消息中间件未能正确推送事件到订阅的服务;

    (3)接受事件的服务重复消费事件。

    针对第三个问题,是最为容易解决的,一般处理方法是由业务代码控制幂等性,例如支付服务传入一个订单时,可以通过判断该订单所对应的唯一ID是否已经处理方式来避免对其再次处理。

    但是前两个问题概括起来就是要解决消息传递的可靠性问题,这个是可靠性事件模式实现数据最终一致性的关键点。要做到可靠消息传递,需要消息中间件确保至少投递一次消息,目前主流的消息中间件都支持消息持久化和至少一次投递的功能。所以,我们需要考虑的是,如何原子性的完成业务操作和发布消息。

    订单服务同时需要处理数据持久化和消息发送两个操作,这就需要考虑两个场景:

    (1)如果数据持久化操作失败,显然消息不该被发送;

    (2)如果数据持久化操作成功,但消息发送失败,那么已经被持久化的数据需要被回滚以还原到初始状态。

    对应这两个场景基本实现流程图如下:

    在逻辑上是可行的,但在运行中,需要考虑很多意想不到的场景,主要有以下两个实际问题:

    (1)实际问题一:典型的依旧是分布式环境下所固有的网络通信异常问题,消息中间件返回通信发生故障,如下图分析:              

    (2)实际问题二:订单服务投递消息后等待消息返回,当消息返回时,订单服务挂了,也会导致数据不一致,如下图分析:

    3.解决方案

    解决上面的问题可以使用一个本地事件表。

    微服务在进行业务操作时需要将业务数据和事件保存在同一个本地事务中,由本地事务保证更新业务和发布事件的原子性。

    发布的事件被保存在本地事务表中,然后该服务实时发布一个事件通知关联的业务服务,如果事件发布成功则立即删除本地事件表中的事件。      

    由于事件消息发布可能会失败或无法获取返回结果,我们需要使用一个额外的“事件恢复”服务来恢复事件,该事件恢复服务定时从事件表中恢复未发布成功的事件并重新发布,只有重新发布才删除保存在本地事件表中的事件。

    注意,事件恢复服务保证了事件一定会被发布,从而确保数据的最终一致性。

    4.实现策略

    在实现上,首先考虑在可靠事件模式中存在一个事件生产者。该事件生产者处于操作的主导地位, 并根据业务操作通过事件的方式发送业务操作的结果(在上例中,订单服务就是事件的生产者),其次,事件消费者是被动方,负责根据事件来处理自身业务逻辑(上例中的支付服务属于事件消费者)。

    有了事件生产者和事件消费者后,我们关注事件服务,事件服务的主要作用就是管理本地事件表,它能存储、确认并发送事件,同时根据不同状态查询事件信息并确定事件已被事件消费者成功消费。事件服务有三大组件:       

    (1)事件确认组件

    表现为一种定时机制,用于处理事件没有被成功发送的场景。

    例如,订单服务在完成业务操作之后需要发送事件到本地事件表,如果这个过程中事件没有发送成功,我们就需要对这些事件重新发送,这个过程为事件确认。

    (2)事件恢复组件

    同样表现为一种定时机制,根据本地事件表中的事件状态,专门处理状态为已确认但还没有成功消费且已超时的事件。

    基本的事件恢复策略就是向消费者重新发送事件,并在消费成功后更新事件状态,并在本地事件表中进行逻辑删除。

    (3)实时消息传递组件

    基于特定的消息中间件工具和框架将事件作为消息进行发送的组件。

    目前可供选择的主流消息中间件包括RabbitMQ、ActiveMQ、RocketMQ、Kafka等。

    四、补偿模式

    1.基本思路

    基本思路在于使用一个额外的补偿服务来协调各个需要保证一致性的微服务,补偿服务按顺序依次调用各个微服务,如果某个微服务调用失败就撤销之前所有已经完成的微服务,补偿服务对需要保证一致性的微服务提供补偿操作。

    举例当中涉及两个微服务,订单微服务和支付微服务,为其提供补偿操作,如果支付服务失败,就需要取消之前的下单服务。

    为降低开发的复杂性和提高效率,补偿服务通常实现为一个通用的补偿框架,补偿框架提供服务编排和自动完成补偿的能力。

    2.关键点

    对于补偿服务而言,所有服务的操作记录是一个关键点,操作记录是执行取消操作的前提。

    举例中,订单服务与支付服务需要保存详细的操作记录和日志,这些日志和记录有助于确定失败的步骤和状态,进而明确需要补偿的范围,然后获取所需补偿的业务数据。

    如果只是订单服务失败,那么只需要补偿一个服务就可以,如果支付服务也失败了,对两个服务进行回滚。

    补偿操作要求业务数据包括支付时的业务流水号、账号和金额。理论上可以根据唯一的业务流水号就能完成补偿操作,但提供更多的数据有益于微服务健壮性。

    3.解决方案

    实现补偿模式的关键要素就是记录完整的业务流水,可以通过业务流水为补偿操作提供需要的业务数据。

    补偿服务可以从业务流水的状态中知道补偿的范围,补偿过程中需要的业务数据同样可以从记录的业务流水中获取。

    补偿服务作为一个服务调用过程同样存在调用不成功的情况,需要通过一定的健壮性机制来保证补偿的成功率,补偿的相关操作本身需要具有幂等性。

    补偿服务健壮性策略:需要根据服务执行失败的原因来选择不同的重试策略,如图所示:                                         

    (1)服务重启:如果失败的原因不是暂时的,而是由业务因素导致的业务错误,需要对问题进行修正后重新执行。

    (2)立即重试:对于网络失败或数据库锁等瞬时异常,重试在很大程度上能够确保任务正常执行。

    (3)定时调用:一般会指定调用的次数上限,如果调用次数达到上限也就不再进行重试。

    如果通过服务重启、立即重试、定时调用等策略依旧不能解决问题,则需要通知相关人员进行处理,即人工干预模式。

    五、Sagas长事务模式--错误管理模式,同时用于控制复杂事务的执行和回滚

    1.基本思路

    长时间持续的事务无法简单地通过一些典型的ACID模型以及使用多段提交配合持有锁的方式来实现。Sagas用于解决这个问题,和多段式分布式事务处理不同,Sagas会将工作分成单独的事务,包含正常额操作和回滚的操作。

    对于开发者而言,不是将所有微服务都定义为分布式的ACID事务,以下单行为为例,将其定义为一个整体,其中包含如何去生成订单以及如何去取消订单,对于支付而言也需要提供同样的逻辑。

    可以将订单和支付服务组合在一起构成一个服务链,然后将整个服务链加密,这样只有该服务链的接收者才能够操控这个服务链。

    当一个服务完成后,会将完成的信息记录到一个集合中,之后可以通过这个集合访问到对应的服务。

    当一个服务失败时,服务本身将本地清理完毕并将消息发送给该集合,从而路由到之前执行成功的服务,然后回滚所有的事务。

    2.解决方案

    在Sagas事务模型中,一个长事务是由一个预定义好执行顺序的子事务集合和他们对应的补偿子事务集合所组成。

    典型的一个完整的交易由T1、T2、......、Tn等多个业务活动组成,每个业务活动可以是本地操作或者是远程操作,而每个业务活动都有对应的取消活动C1、C2、......、Cn。

    所有的业务活动在Sagas事务下要么全部成功,要不全部回滚,不存在中间状态。对于一个Sagas链路而言,各个业务活动执行过程中都会依赖上下文,每个业务活动都是一个原子操作,并提供执行和取消两个入口。

    需要设计一个存储模型来保存执行上下文并通过该存储模型来索引到对应的服务。

    存储模型中包含两个内部结构,一个是完成的任务,一个是等待执行的任务。如果成功就会将任务向前执行,如果失败就向后执行。

    实现上的一种思路可以采用队列和栈数据结构,一方面使用队列来向前执行,另一方面使用栈来向后执行。

    注意,当执行取消操作进行事务操作失败时需要记录失败事务日志,通过重试策略进行重试,对重试失败的执行定时重试,在有问题时则进行人工干预。

    六、TCC模式

    1.基本思路

    一个完整的TCC业务由一个主服务和若干个从服务组成,主服务发起并完成整个业务流程。

    从服务提供三个接口:Try、Confirm、Cancel:

    Try接口:完成所有业务规则检查,预留业务资源。

    Confirm接口:真正执行业务,其自身不做任何业务检查,只使用Try阶段预留的业务资源,同时该操作需要满足幂等性。

    Cancel接口:释放Try阶段预留的业务资源,同样也需要满足幂等性。

    举例来看,订单系统拆分成订单下单和订单支付两个场景,使用TCC模式后执行效果如下:

    (1)Try阶段:尝试执行业务。

    一方面完成所有业务检查,如针对该次订单下单操作,需要验证商品的可用性以及用户账户金额是否够。

    另一方面需要预留业务资源,如把用户账户余额进行冻结用于支付该订单,确保不会出现其他并发进程扣减账户余额导致后续支付无法进行。

    (2)Confirm阶段:执行业务。

    Try阶段一切正常,则执行下单操作并扣除用户账号中的支付金额。

    (3)Cancel阶段:取消执行业务。

    释放Try阶段预留的业务资源,如果Try阶段部分成功,如商品可用且正常下单,但账户余额不够而冻结失败,则需要对产品下单做取消操作,释放被占用的该商品。

    2.解决方案

    TCC服务框架不需要记录详细的业务流水,完成Confirm和Cancel操作的业务由业务服务提供。

    TCC模式同样有两个阶段组成

    第一阶段:

    主业务服务分别调用所有从业务的Try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的Try操作都调用成功或者某个从业务的Try操作失败,进入第二个阶段。

    第二阶段:

    主业务根据第一阶段的执行结果来执行Comfirm或Cancel操作。

    如果第一阶段所有Try操作都成功,则主业务服务调用所有从业务活动的Confirm操作。

    如果第一阶段中失败,则调用Cancel操作。

    整体上,一个完整的TCC事务参与方包括三个部分:

    (1)主业务服务

    整个业务的发起方,在订单处理场景中,订单应用系统即是主业务服务。

    (2)从业务服务

    负责提供TCC业务操作,是整个业务活动的操作方。

    从业务必须实现Try、Confirm和Cancel三个接口,供主业务服务调用。Confirm和Cancel接口需要具备幂等性,订单的下单服务与支付服务即是从业务服务。

    (3)业务活动管理

    管理控制整个业务活动,包括记录维护TCC全局事务状态和每个从业务服务的子事务状态,并在业务活动提交时确认所有从业务服务Confirm操作,在业务活动取消时调用所有从业务服务的Cancel操作。

    3.实现策略

    在实现TCC模式上,最重要的工作是设计一个稳定的、高可用的、扩展性强的TCC事务管理器。

    在一个跨服务的业务操作中,首先通过Try锁住服务中业务资源进行资源预留,只有资源预留成功了,后续的操作才能正常进行。Confirm操作是在Try之后进行的对Try阶段锁定的资源进行业务操作,Cancel在所有操作失败时用于回滚。

    TCC的操作都需要业务方提供对应的功能,在开发成本上比较高,推介TCC框架有:

    (1)http://github.com/protera/spring-cloud-rest-tcc;

    (2)http://github.com/changmingxie/tcc-transaction;

    (3)http://github.com/liuyangming/ByteTCC;

    (4)http://github.com/QNJR-GROUUP/EasyTransaction;

    (5)http://github.com/yu199195/happylifeplat-tcc;

    (6)https://www.atomikos.om/Blog/TCCForTrasationMangementAcrossMicroservices。

    七、最大努力通知模式

    1.基本思路

    本质上是一种通知类的实现方案。

    基本思路是通知发送方在完成业务处理后向通知接收方发送通知消息。

    当消息接收方没有成功消费消息时,消息的发送方还需要进行重复发送直到消费者消费成功或达到某种发送总之条件。

    消息发送方可以设置复杂的通知规则,利于采用阶梯式事件通知方式。

    通知接收方也可以使用发送方所提供的查询和对账接口获取数据,用于恢复通知失败所导致的业务数据。

    以支付宝为例,通知回调商户提供的回调接口,通过多次通知、查询对账等手段完成交易业务平台间的商户通知。

    2.解决方案

    实现上比较简单,基本系统结构中的通知服务包括三大组件:

    (1)查询组件

    通发送方处理业务并把业务记录保存起来,查询组件提供查询入口供通知接收方主动查询业务数据,避免数据丢失。

    (2)确认组件

    当通知接收方成功接收到通知时,需要与通知发送方确认通知已被正常接收。确认组件接收到确认消息之后就会更新业务记录中的状态,通知组件根据状态就不需要再发送通知。

    (3)通知组件

    通知组件根据业务记录向通知接收方发送通知,在发送通知的过程中需要保存通知记录,并根据业务记录的状态以及现有的通知记录确定下一次发送通知的策略。

    注:最大努力通知模式适合于业务最终一致性的时间敏感度比较低的场景,一般用于类似支付宝与商户集成类跨企业的业务活动。

    八、人工干预模式

    1.基本思路

    严格意义上并不是一种数据一致性的实现机制,当前面讲的各种模式都无法满足需要时,人工干预模式更多的是一种替代方案。

    对于一些重要的业务场景下,由于前面几种模式中因为网络三态性无法解决问题的情况,需要人工干预来保证真正的一致性。常用手段为定期对账等。

    2.解决方案

    实施前提是需要一个后台管理系统,提供操作不一致的基本入口。

    周期性的对账机制需求,对账机制基于业务数据,业务双方根据各自系统内所产生的订单或支付记录,相互对比发现数据不一致的情况,然后通过线下付款等形式形成一致的操作结果。

    九、数据一致性模式总结

    对于金融、支付等业务体系,数据一致性要求极高,需要保证严格的实时一致性要求。

    对于基于社交类的应用场景,可以采用局部实时一致、最终全局一致的实现思路。

    微服务架构里面建议“兜底”思维,即不管实现方案是否完美,最后都要有一个备选方案,备选方案不一定满足日常业务场景,但当出现异常情况时,可通过备选完成正常业务的闭环。

    参考书籍、文献和资料:

    【1】郑天民. 微服务设计原理与架构. 北京:人民邮电出版社,2018.05

    【2】尹吉欢. Spring Cloud微服务全栈技术与案例分析. 北京:机械工业出版社,2018.08

    展开全文
  • 有关一致性,实践中又可以分为:强一致性、单调一致性、最终一致性。 CAP中的C默认就是指:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)。 1、强一致性...

    我们都了解分布式CAP原则,其中的C就是一致性。有关一致性,实践中又可以分为:强一致性、单调一致性、最终一致性。

    CAP中的C默认就是指:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)。

    1、强一致性(Strong Consistency)

    在任何时刻所有的用户或者进程查询到的都是最近一次成功更新的数据。强一致性是程度最高一致性要求,也是最难实现的。关系型数据库更新操作就是这个案例。

    2, 单调一致性(Monotonic Consistency)

    单调一致性会从读写两个角度有各自的定义。

    单调读一致性

    如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回该值之前的值。(“If a process has seen a particular value for the object any subsequent accesses will never return any previous values”)

    单调写一致性

    系统保证来自同一个进程的写操作顺序执行。(Write operations that must precede other writes are executed before those other writes.)

    3、最终一致性(Eventual Consistency)

    和强一致性相对,在某一时刻用户或者进程查询到的数据可能都不同,但是最终成功更新的数据都会被所有用户或者进程查询到。当前主流的nosql数据库都是采用这种一致性策略。

    额外内容:

    zookeeper的一致性

    我们以官方文档为准:(2019-11-17摘录的)

    ZooKeeper is a high performance, scalable service. Both reads and write operations are designed to be fast, though reads are faster than writes. The reason for this is that in the case of reads, ZooKeeper can serve older data, which in turn is due to ZooKeeper's consistency guarantees:
    
    Sequential Consistency : Updates from a client will be applied in the order that they were sent.
    
    Atomicity : Updates either succeed or fail -- there are no partial results.
    
    Single System Image : A client will see the same view of the service regardless of the server that it connects to.
    
    Reliability : Once an update has been applied, it will persist from that time forward until a client overwrites the update. This guarantee has two corollaries:
    
    If a client gets a successful return code, the update will have been applied. On some failures (communication errors, timeouts, etc) the client will not know if the update has applied or not. We take steps to minimize the failures, but the guarantee is only present with successful return codes. (This is called the monotonicity condition in Paxos.)
    Any updates that are seen by the client, through a read request or successful update, will never be rolled back when recovering from server failures.
    Timeliness : The clients view of the system is guaranteed to be up-to-date within a certain time bound (on the order of tens of seconds). Either system changes will be seen by a client within this bound, or the client will detect a service outage.
    
    Using these consistency guarantees it is easy to build higher level functions such as leader election, barriers, queues, and read/write revocable locks solely at the ZooKeeper client (no additions needed to ZooKeeper). See Recipes and Solutions for more details.
    

    简单总结:zk提供的是单调一致性,最终一致性,不是强一致性。 zk的文档清楚强调了ZooKeeper does not guarantee that at every instance in time, two different clients will have identical views of ZooKeeper data.

    原文链接:https://zookeeper.apache.org/doc/current/zookeeperProgrammers.html#ch_zkGuarantees

    mongoDB的一致性

    原文链接:https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/

    mongodb提供单调写一致性。
    Monotonic Writes
    MongoDB provides monotonic write guarantees, by default, for standalone mongod instances and replica set.

    For monotonic writes and sharded clusters, see Causal Consistency.
    Write operations that must precede other writes are executed before those other writes.

    后记:我们实际开发业务分布式系统,必须在强一致性和最终一致性中间进行tradeoff。 根据业务要求,进行取舍。强一致性会增加延时(所有实例和副本进行数据同步)和降低可用性(只要有节点无法完成同步数据, 可用性就有问题)。
    现在很多软件直接提供可配置的一致性,用户可以根据业务要求进行配置。 例如微软的cosmos-db。

    参考:
    1, https://hellokangning.github.io/en/post/consistency-in-distributed-system/
    2,https://docs.microsoft.com/en-us/azure/cosmos-db/consistency-levels
    3, https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/

    展开全文
  • Matlab源程序代码如下: clc clear disp('请输入判断矩阵A') A=input('A='); [n,n] = size(A) %方法1: 算术平均法 Sum_A = sum(A); SUM_A = repmat(Sum_A,n,1);...disp('算术平均法求权重的结果...
  • 浅析数据一致性

    万次阅读 2016-02-19 15:27:38
    什么是数据一致性?  在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。 实践中,导致数据不一致的情况...
  • 数据库的ACID(原子性、一致性、隔离性与持久性)

    万次阅读 多人点赞 2018-05-08 15:45:37
    我的机器学习教程「美团」算法工程师带你入门机器学习 已经开始更新了,欢迎大家订阅~ 任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑...
  • 一致性哈希

    万次阅读 2019-11-08 10:13:16
    一致性哈希 通俗说活
  • 一致性、顺序一致性、弱一致性和共识

    万次阅读 多人点赞 2018-07-21 21:57:37
    1. 一致性(Consistency) 一致性(Consistency)是指多副本(Replications)问题中的数据一致性。可以分为强一致性、顺序一致性与弱一致性。 1.1 强一致性(Strict Consistency) 也称为: 原子一致性(Atomic ...
  • 在足球比赛里,一个球员在一场比赛中进三个球,称之...一致性(Consistency) 可用性(Availability) 分区容忍性(Partition tolerance) CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。因此在进行...
  • 面试必备:什么是一致性Hash算法?

    万次阅读 多人点赞 2018-03-14 00:00:00
    最近有小伙伴跑过来问什么是Hash一致性算法,说面试的时候被问到了,因为不了解,所以就没有回答上,问我有没有相应的学习资料推荐,当时上班,没时间回复,晚上回去了就忘了这件事,今天突然看到这个,加班为大家...
  • 介绍内容转载自:http://www.blogjava.net/hello-yun/archive/2012/04/27/376744.html https://blog.csdn.net/c289054531/article/details/15337575CAP原理中,有三个要素:一致性(Consistency)可用性(Availability...
  • 一致性、弱一致性、最终一致性

    千次阅读 2015-09-27 22:01:39
    在足球比赛里,一个球员在一场比赛中进三个球,称之为帽子戏法(Hat-trick)。在分布式数据系统中,也有一个帽子原理(CAP Theorem),不过此帽子非彼帽子。...一致性(Consistency)可用性(Availabil
  • 在分布式数据系统中,也有一个帽子原理(CAP Theorem),不过此帽子非彼帽子。CAP原理中,有三个要素,CAP原理指的是,这三个要素... 一致性就是数据保持一直,可以理解为多个节点中数据的值是一致的,一致性又可以分...
  • 一致性(Consistency) 是指多副本(Replications)问题中的数据一致性。可以分为强一致性、顺序一致性与弱一致性。 强一致性(Strict Consistency) 系统中的某个数据被成功更新后,后续任何对该数据的读取操作...
  • 无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。 如题,这种情况一般在数字类数据...
  • 理解事务——原子性、一致性、隔离性和持久性

    万次阅读 多人点赞 2013-08-17 23:50:37
    介绍事务的ACID特性
  • 在信息融合中经常使用卡尔曼滤波,现在我们对其进行讲解: 百度百科上写到: 卡尔曼滤波公式如上 这是另一种表述,涉及的符号见下表: 对于联邦卡尔曼滤波: 对于一致性卡尔曼滤波: ...
  • 在分布式系统中会涉及到CAP原理,来保证数据的一致性, 1.什么是CAP: 一致性(Consistency) 可用性(Availability) 分区容忍性(Partition tolerance) CAP原理是说这三个要素最多只能同时满足两点,不可能...
  • ZooKeeper能保证任何时刻读到的数据绝对一致吗? Zookeeper的特点就是,分布式,高可用,...也就是说可用性和一致性是Zookeeper的关键特性,作为一个消息的中间商,做了一个可靠的信息传递和存储的角色。 但是了解下ZooKeep
  • 你知道的越多,你不知道的越多 点赞再看,养成习惯 前言 Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难。作为一个在互联网公司面一次拿...
  • 数据存储一致性

    千次阅读 2011-07-24 22:12:55
    数据存储的一致性模型是存储系统和数据使用者之间的约定。一致性有不同程度,大致如下: 1. 强一致性:更新完成后,任何后续访问都将返回更新过的值。 2. 弱一致性:系统不保证后续访问将返回更新过的值,在那之前...
  • 沃趣科技罗小波:MySQL数据一致性

    千人学习 2016-08-17 14:10:53
    该视频教程深度讲解MySQL主备数据一致性:1、MySQL复制发展史简介,及其复制原理2、MySQL 崩溃恢复安全性,包括:如何使用sync_binlog,innodb_flush_log_at_trx_commit来保证3、MySQL崩溃时不丢失数据,如何使用...
  • 关于事务的一致性,《数据库系统概念》中是这样描述的 第二段说的三个特性是指原子性、隔离性、持久性。 就算这样,相信大家也是懵懵的,我也是,所以才会写下这篇博客。 看到别的博客说,一致性是事务的最终...
  • Raft 一致性算法论文

    万次阅读 2019-05-17 09:52:13
    本篇博客为著名的 RAFT 一致性算法论文的中文翻译,论文名为《In search of an Understandable Consensus Algorithm (Extended Version)》(寻找一种易于理解的一致性算法)。 Raft 是一种用来管理日志复制的一致性...
  • 随着微服务的越来越多,一致性问题也越来越被重视。纠结是怎样才能ACID呢?CAP还是Base呢?其实强一致性的方案也特别多,比如net的msdtc、java的atomikos...等。但他们这类基于2pc(两阶段提交协议)实现,基本上性能...
  • 加速器一致性接口

    千次阅读 2015-08-08 16:37:54
    Zynq PS上的加速器一致性接口(Accelerator Coherency Port, ACP)是一个兼容AXI3的64位从机接口,连接到SCU(Snoop Control Unit),为PL提供异步缓存一致性直接访问PS的入口。 处理器可以标记ACP上的传输为一致...
  • 一致性成本和非一致性成本

    千次阅读 2020-08-25 09:59:50
    对于质量成本中的一致性成本与非一致性成本的区别。一图看懂

空空如也

1 2 3 4 5 ... 20
收藏数 110,053
精华内容 44,021
关键字:

一致性