精华内容
下载资源
问答
  • 信息的不一致性
    千次阅读
    2020-05-30 13:31:25

    Zookeeper能保证任何时刻读到的数据绝对一致吗?

    Zookeeper的特点就是,分布式,高可用,自带容错,所有节点读到的数据都是一致的。使用的场景通常是微服务的注册中心,或者一些分布式的开源软件用来保存元数据,或者监测生命状态。

    这些使用场景默认Zookeeper永远是可用的,而且去Zookeeper集群旗下的每家分号,获取的数据都是一样的,通常情况下也确实如此。
    也就是说可用性一致性是Zookeeper的关键特性,作为一个消息的中间商,做了一个可靠的信息传递和存储的角色。

    但是了解下Zookeeper的ZAB协议,特别是写入部分和读部分,发现了一点细节,Zookeeper不能保证永远读到最新的数据,这里简述下Zookeeper读写过程、

    写过程
    Leader接收Client的写请求,广播给其他Follower节点,其他节点将消息加入待写队列,向Leader发送成功消息,过半的Follower同意后,Leader向所有节点发送提交消息,Follower会落实写请求

    读过程
    Client直接访问一台 Zookeeper获取信息

    也就是说,如果在写的过程中,过半的follower同意了,这条消息通过写入,但有一台Zookeeper和Leader无法通信了,或者因为磁盘,内存等原因拒绝写入了此时一个client来这个zookeeper节点取数据,那么取的和最新版本的就不一致

    Zookeeper保证了怎样的一致性

    有些地方会写Zookeeper不保证强一致性,保证了最终一致性。
    只是从字面来看,最终一致性听上去也没错,但是从细节来看,还是不准确或者说不对。

    首先,CAP中,Zookeeper保证的是CP还是AP。
    随便搜一个科普CAP理论和Zookeeper关系的文章
    Zookeeper和CAP理论及一致性原则
    可以知道,Zookeeper保证的是CP,即一致性(Consistency)分区容错性(Partition-Tolerance)
    牺牲了部分可用性(Available),
    强一致性的条件下,必须先暂停服务,达成一致性再提供服务,这个同步过程中,请求得不到回应,降低了可用性
    而Zookeeper作为协调服务,需要在大部分情况下都可用,不能出现太明显的不可用,因此明显不可用的时段只有Leader选举阶段,此时无法写入,
    Zookeeper选举机制本身是一种快速选举的机制,触发选举的时候有崩溃恢复启动选举 两种情况,所以这个问题也可以控制。

    从上文简述的写入机制来看,Zookeeper是通过Leader来让各节点的写入达到一致性
    而达成的一致性,但是这个过程中为了快速响应客户端,只要follower过半回应即可。
    下面说一下几种一致性的概念
    强一致性:又称线性一致性(linearizability ),
    1.任意时刻,所有节点中的数据是一样的,
    2.一个集群需要对外部提供强一致性,所以只要集群内部某一台服务器的数据发生了改变,那么就需要等待集群内其他服务器的数据同步完成后,才能正常的对外提供服务
    3.保证了强一致性,务必会损耗可用性

    弱一致性:
    1.系统中的某个数据被更新后,后续对该数据的读取操作可能得到更新后的值,也可能是更改前的值。
    2.即使过了不一致时间窗口,后续的读取也不一定能保证一致。

    最终一致性:
    1.弱一致性的特殊形式,不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化
    2.存储系统保证在没有新的更新的条件下,最终所有的访问都是最后更新的值

    顺序一致性:
    1.任何一次读都能读到某个数据的最近一次写的数据。
    2.系统的所有进程的顺序一致,而且是合理的。即不需要和全局时钟下的顺序一致,错的话一起错,对的话一起对
    (目前网上能查到的原话)

    前三种应该都好理解。强一致性就是在任意时刻,所有节点中的数据都是一样的

    弱一致性就是可能访问的到更新后的值,也可能访问不到。

    最终一致性,不保证任何节点都是相同的,也就是说各节点的数据版本可能完全是混乱的,a节点是1,b节点是2,c节点是3,然后a节点更新到2,b节点更新到3,但能保证在没有更新后达成一致。

    顺序一致性第一句比较好理解,第二句就比较抽象了,字看的懂,但还是不知道具体说啥,经过查阅资料

    123
    来看这张水印重重的图,
    最上面的(a),
    两个进程p1和p2,p1先写了x=4,然后进行读操作,读了y=2,
    p2写了y=2,然后进行读操作,读了x=0

    从全局时钟来看,p2对x的读取在P1的写操作之后,但是数值却是旧的,也就是说这个系统中两个进程并不是每个时刻都能保持数值的一致,不满足强一致性。
    但是如果从进程角度来看,我p2执行的操作,与p1并不冲突,如果p2先执行,p1后执行,
    p2写了y=2,读到x=0,之后p1才写了x=4,并且读到的y确实也是2
    ,
    那他就符合顺序一致性,而且也确实读到了某个数据的最近一次写的数据

    (b)图b满足强一致性,因为每个读操作都读到了该变量的最新写的结果,同时两个进程看到的操作顺序与全局时钟的顺序一样,都是Write(y,2) , Read(x,4) , Write(x,4), Read(y,2)

    ©可以看出,假如p2在先,那么p1读到的y值应该是2而不是0。假如p1在先,那么p2读到的x值应该应该是4而不是0。所以他不符合顺序一致性,更不符合强一致性。

    再说下Java内存模型中顺序一致性,如果对多线程并发有理解,可以结合下来理解

    顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证。顺序一致性内存模型有两大特性:
    一个线程中的所有操作必须按照程序的顺序来执行
    (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见

    这里的顺序一致性,讲的是一种多线程并发执行下理想情况,包含两种要求
    1.线程中的操作必须按照程序的顺序执行,也就是说,不能自己自作主张,更换执行顺序
    2.线程中的操作是原子性的,执行了就是执行了,没执行就是没执行,不存在中间状态,而且一旦执行,其他变量应该立刻可见

    联系到zookeeper,说点结论
    1.各节点的数据更新必须按照顺序进行
    2.数据写入执行情况,数据版本应对其他节点可见(leader能知道写入是否成功)。

    结合以上,你会发现,zookeeper并不是最终一致性,而是顺序一致性。
    1.最终一致性的特点是,无法保证任意节点在同一时间某份数据是相同的,但是最终在没有新的更新时会达成一致
    而Zookeeper所有节点的数据版本都是递增的,可能会有某个节点因故障版本低于大多数,但是是有序的,不会出现各自增长的情况

    比如,Zookeeper节点可能会出现4台数据是version 5,一台数据是version4。但是不会是5台机器各自更新。

    所以这里对顺序一致性的定义是
    1.任何一次读都能读到某个数据的最近一次写的数据

    2.对其他节点之前的修改是可见(已同步)且确定的,并且新的写入建立在已经达成同步的基础上

    结论:Zookeeper写入是强一致性,读取是顺序一致性。

    然后这只是基于现有的资料的一点思考,如果以后发现有不对的,随时修改。

    参考:
    Zookeeper并不保证读取的是最新数据
    强一致性、顺序一致性、弱一致性和共识
    深入理解Java内存模型(三)——顺序一致性
    ZooKeeper和CAP理论及一致性原则
    分布式架构之Consistency(一致性、强一致性,弱一致性,顺序一致性,最终一致性)

    ============
    作者:花灯渔火
    版权归作者所有,转载请注明出处

    更多相关内容
  • 【RPC】分布式一致性一致性协议

    千次阅读 2022-07-06 09:44:27
    数据一致性只有存在与存在两种情况,就像可以用0%到100%之间的任意数值来代表可用性的程度一样,一致性也有一些分类。一致性模型按照强弱可以粗略地分为弱一致性模型、最终一致性模型和强一致性模型。下面就...


    分布式一致性

    在CAP、ACID和BASE中都提到了一致性。但是对于一致性的整个定义还是非常模糊的,所以本文会详细介绍一致性的模型,以及目前比较流行的一致性协议。

    数据一致性并不只有存在与不存在两种情况,就像可以用0%到100%之间的任意数值来代表可用性的程度一样,一致性也有一些分类。一致性模型按照强弱可以粗略地分为弱一致性模型、最终一致性模型和强一致性模型。

    • 弱一致性模型的特点是向系统更新或者写入一个数值后,后续的读操作不一定能够读到这个最新的数值。
    • 最终一致性模型的特点是向系统更新或者写入一个数值后,后续一段时间内的读操作可能读取不到这个最新的值,但在该时间段过后,一定能够读到最新的数值。最终一致性模型又可细分为:因果一致性、单调一致性和最终一致性
    • 强一致性模型的特点是向系统更新或写入一个数值后,无论何时都能够读到这个最新的数值。强一致性模型又可以细分为两类:线性一致性和顺序一致性

    下面就分别介绍一下这几种一致性模型。

    1. 线性一致性

    线性一致性又称为原子一致性和强一致性。

    Linearizability: A Correctness Condition for Concurrent Objects. Maurice P. Herlihy, Jeannette M. Wing. 1987.

    如果需要达到线性一致性,则需要满足如下条件:

    • 1)任何一次读操作都可以读到某个数据的最新值。
    • 2)系统中所有的节点内执行的时间顺序都和系统级别时钟下看到的时间顺序一致。

    第一点非常容易理解,第二点则约束了两个维度下时间的执行顺序都必须是一样的。这两个维度是指单个进程内的时钟顺序和整个系统的时钟顺序。下面通过一个例子来理解一下。
    在这里插入图片描述

    图中有三个进程P1、P2、P3,从P1进程来看,只是执行了一次写操作。从P2和P3进程来看。事件顺序都是先执行一次写操作,然后执行一次读操作。从整个系统的时钟顺序来看,先执行三次写操作,然后执行两次读操作,所以最近一次的值是3,读操作读到的值也就是3。

    2. 顺序一致性

    上面的例子中五个事件在时间上没有重叠,但是在实际的场景中,不同进程的事件执行的时间点一定会有重叠,如下面的例子:
    在这里插入图片描述

    这三个进程在执行三次写操作和三次读操作时的时间点有重叠,从系统级时钟的维度来看,整个事件的执行顺序应该是write1→write2→write3→read2→read3→read2(数字代表进程标识)。如果按照线性一致性的约束,则第一次read2读到的值应该是a=3,因为第一次read前一次的写操作时write3。但是图中的结果却是a=2,这是因为write3操作虽然实在read2操作之前开始执行的,但是write3操作结束的事件却是在第一次read2操作开始之后。从进程P2的视角看,事件执行顺序应该时write2→read2→write3→write1→read2,P2进程和系统级别时钟的视角看到的事件执行顺序是完全不一样的。而这种时间点重叠,并且不满足系统级别时钟的事件执行顺序的一致性成为顺序一致性。

    顺序一致性的约束如下:

    • 1)任何一次读操作都可以读到某个数据的最新值,这一点和线性一致性是相同的。
    • 2)任何进程看到的事件顺序是合理的,达成一致即可,并不需要所有进程的时间顺序和系统级时钟下的事件顺序一致,它放宽了对一致性的要求,并不像线性一致性一样严格。

    3. 因果一致性

    线性一致性和顺序一致性有一个相同的约束,就是任何一次读操作都可以读到某个数据的最新值,这是强一致性的表现。而因果一致性属于最终一致,它的约束如下:

    • 1)对于具有因果关系的读/写事件,所有进程看到的事件顺序必须一致。
    • 2)对于没有因果关系的读/写事件,进程可以以不同的顺序看到这些并发的事件。

    4. 单调一致性

    单调一致性可以分为单调读一致性和单调写一致性。

    单调读一致性指的是任何时刻一旦读到某个数据项在某次更新后的值,就不会再读到比这个值更旧的值。

    单调写一致性指的是一个进程对某一个数据项执行的写操作必须在该进程对数据项执行任何后续写操作之前完成。

    相较于因果一致性,单调一致性更聚焦于单个进程内的读/写操作顺序。

    5. 最终一致性

    因果一致性和单调一致性都属于最终一致性中的一种,只是在最终一致性的基础上增加了一致性的强度,因果一致性是在最终一致性的基础上增加了因果关系的约束,单调一致性是在最终一致性的基础上增加了单进程内的约束。而没有增加其他约束的最终一致性也就是字面上的最终一致性的意思,只有一个约束,就是向系统写入更新或者写入一个数值,后续一段时间内的读操作可能读取不到这个最新的值,但是在该时间段过后,一定能够读到这个最新的数值。

    对比以上几个一致性模型的约束条件,可以发现它们之间也有一定的强弱之分,由弱到强,分别是最终一致性、单调一致性、因果一致性、顺序一致性、线性一致性。

    一致性模型是理论总结,一致性协议则是一致性模型的具体表现形式。与之经常混淆的是共识算法。共识和一致性描述的内容并不相同,共识用来描述一组进程间的协作过程,并且确定下一个操作,而一致性描述的是进程间某一时刻的状态。共识和一致性相比,共识的概念更狭隘一些,因为达成共识就可以达到一致性。但是需要达到一致性,并不一定需要达成共识,而一致性有强弱之分,共识算法是一致性协议的一种具体实现手段。


    一致性协议

    1. Paxos算法

    Paxos是一种基于消息传递且具有高度容错特性的共识算法。

    在分布式系统中,只能减少却无法避免进程挂掉、进程重启、通信消息丢失、延迟等情况,Paxos算法实现的就是在发生这些异常时,不会破坏分布式系统决议的一致性。一些进程可以提出各种请求,最终只有一个请求会被选中,只要有一个请求被选中,那么其他进程都能获得该请求带来的变化。

    在Paxos算法中有三类角色,分别是

    • Acceptor(决策者):用于决策最终选用哪个决议。
    • Proposer(提议者):该角色的职责为向决策者提出提议。
    • Learner(最终决策执行者):该角色的职责是执行最终选定的提议。

    从提议的提出到提议的选定,再到提议的执行,可以大致分为两个阶段,第一个阶段就是决策者选出一个最终的提议(其实是Proposer与Acceptor之间的交互),第二阶段就是最终决策执行者如何获取并执行该提议。

    由于Paxos算法是一套偏向理论的算法,难以理解且实现,所以本文就不展开介绍细节了。

    2. Raft算法

    Raft是一种用来管理复制状态机的算法,复制状态机通常使用复制日志实现,Raft也不例外。每个服务器存储一个包含一系列命令的日志,其状态机按顺序执行日志中的命令。每个日志中的命令都相同且顺序也一样,因此只要处理相同的命令序列,就能得到相同的状态和相同的输出序列。这也是Raft实现一致性的核心思想。

    Raft算法有三种角色:

    • Leader(领袖):该角色的职责是接受和处理一切请求,并同步数据给Follower。
    • Follower(群众):该角色的职责是转发请求给Leader,接受Leader的同步数据,以及参与投票。
    • Candidate(候选人):该角色的职责是竞选Leader。

    这三种角色分别代表服务节点的三种状态,这三种状态之间可以互相转化。

    对于raft角色的转化,在官网有一个动画,可以自己设置各节点的状态:https://raft.github.io/

    在这里插入图片描述

    从图中可以看出,集群最初的状态——所有服务器都是Follower,当这些服务启动完成后,由于起初没有Leader,所以Follower一定不会收到Leader的心跳信息。经过一段时间后,发生选举,此时Follower先增加自己的当前任期号并且转换到Candidate身份,然后投票给自己并且并行地向集群中的其他服务器节点发送竞选请求。

    当满足以下三个条件中的一个时,Candidate身份会发生转变:

    • 集群内超过半数的服务节点同意该 Candidate 成为 Leader,也就是超过半数的节点响应了竞选请求,此时 Candidate 会变成 Leader。(这是对图中“receives votes from majority of servers”的解释)
    • 集群内其他的某个服务器节点已经成为 Leader,此时 Candidate 会变回 Follower。 因为当 Leader 产生后,它会向其他的服务器节点发送心跳消息来确定自己的地位并阻止新的选举。(这是对图中“discovers current leader or new term”的解释)
    • 如果有多个 Follower 同时成为 Candidate,那么选票可能会被瓜分,以至于没有Candidate 赢得过半的投票,也就是选举超时后还是没有选出 Leader,会通过增加当前任期号来开始一轮新的选举,但是这种情况有可能无限重复(这是对图中“times out, new election”的解释)。为了防止这种情况发生,Raft 算法使用超机选举超时时间的方法来确保很少发生选票瓜分的情况。也就是每个 Candidate 在开始一次选举的时候重置一个随机的选举超时时间,然后一直等待直到选举超时。该 Candidate 会增加当前的任期号,重新发起竞选请求,此时其他 Candidate 可能还在等待中,那么其他服务节点认为该超时的 Candidate 的任期号最大,所以它会被选为 Leader。

    还有一种从Leader直接变成Follower的情况,这种情况多数出现在Leader发生了网络分区的时候。当Leader发生网络分区后恢复时,新的Leader已经产生,它会接受新Leader的心跳请求,发现新的Leader的任期号比自己的大,它会自动变成Follower。 而旧的Leader如果发送心跳请求给其他服务器节点时,Candidate和Follower都会对比任期号,如果任期号小于自己的任期号,则直接拒绝此次心跳请求。

    展开全文
  • Redis 与 MySQL 数据一致性问题

    万次阅读 2022-06-24 14:40:06
    Redis 与 MySQL 数据一致性问题

    Redis 拥有高性能的数据读写功能,被我们广泛用在缓存场景,一是能提高业务系统的性能,二是为数据库抵挡了高并发的流量请求。

    把 Redis 作为缓存组件,需要防止出现以下问题,否则可能会造成生产事故。

    • Redis 缓存满了

    • 缓存穿透、缓存击穿、缓存雪崩

    • Redis 数据过期了

    • Redis 突然变慢了

    • Redis 与 MySQL 数据一致性问题

    在本文正式开始之前,需要大家先取得以下两点共识: 

    • 缓存必须要有过期时间;

    • 保证数据库跟缓存的最终一致性即可,不必追求强一致性

    1. 什么是数据库与缓存一致性? 

    数据一致性指的是:

    • 缓存中存有数据,缓存的数据值 = 数据库中的值;

    • 缓存中没有该数据,数据库中的值 = 最新值。

    反推缓存与数据库不一致:

    • 缓存的数据值 ≠ 数据库中的值;

    • 缓存或者数据库存在旧的数据,导致线程读取到旧数据。

    为何会出现数据一致性问题呢?

    把 Redis 作为缓存的时候,当数据发生改变我们需要双写来保证缓存与数据库的数据一致性。

    数据库跟缓存毕竟是两套系统,如果要保证强一致性,势必要引入 2PC 或 Paxos 等分布式一致性协议,或者分布式锁等等。这个在实现上是有难度的,而且一定会对性能有影响。

    如果真的对数据的一致性要求这么高,那引入缓存是否真的有必要呢?

    2. 缓存的使用策略

    在使用缓存时,通常有以下几种缓存使用策略用于提升系统性能:

    • Cache-Aside 模式(旁路缓存,业务系统常用)

    • Read-Through 模式(直读)

    • Write-Through 模式(同步直写)

    • Write-Behind 模式

    2.1 Cache-Aside (旁路缓存)

    所谓「旁路缓存」,就是读取缓存、读取数据库和更新缓存的操作都在应用系统来完成,业务系统最常用的缓存策略。

    2.1.1 读取数据

    读取数据逻辑如下:

    1. 当应用程序需要从数据库读取数据时,先检查缓存数据是否命中;

    2. 如果缓存未命中,则查询数据库获取数据。同时将数据写到缓存中,以便后续读取相同数据会命中缓存。最后再把数据返回给调用者;

    3. 如果缓存命中,直接返回。

    时序图如下:

     

     

    优点

    • 缓存中仅包含应用程序实际请求的数据,有助于保持缓存大小的成本效益;

    • 实现简单,并且能获得性能提升。

    实现的伪代码如下:

    
    String cacheKey = "码哥字节";
    String cacheValue = redisCache.get(cacheKey);
    //缓存命中
    if (cacheValue != null) {
      return cacheValue;
    } else {
      //缓存缺失, 从数据库获取数据
      cacheValue = getDataFromDB();
      // 将数据写到缓存中
      redisCache.put(cacheValue)
    }

    缺点

    由于数据仅在缓存未命中后才加载到缓存中,因此初次调用的数据请求响应时间会增加一些开销,因为需要额外的缓存填充和数据库查询耗时。

     2.1.2 更新数据

    使用 cache-aside 模式写数据时,如下流程。

    1. 写数据到数据库;

    2. 将缓存中的数据失效或者更新缓存数据。

    使用 cache-aside 时,最常见的写入策略是直接将数据写入数据库,但是缓存可能会与数据库不一致。

    我们应该给缓存设置一个过期时间,这个是保证最终一致性的解决方案。

    如果过期时间太短,应用程序会不断地从数据库中查询数据。同样,如果过期时间过长,并且更新时没有使缓存失效,缓存的数据很可能是脏数据。

    最常用的方式是删除缓存使缓存数据失效。

    为啥不是更新缓存呢?

    性能问题

    当缓存的更新成本很高需要访问多张表联合计算时,建议直接删除缓存,而不是更新缓存数据来保证一致性。

    安全问题

    在高并发场景下,可能会造成查询查到的数据是旧值,具体原因稍后分析。

    2.2 Read-Through 模式(直读)

    当缓存未命中,也是从数据库加载数据,同时写到缓存中并返回给应用系统。

    虽然 read-through 和 cache-aside 非常相似,在 cache-aside 中应用系统负责从数据库获取数据和填充缓存。

    而 Read-Through 将获取数据存储中的值的责任转移到了缓存提供者身上。

    Read-Through 实现了关注点分离原则。代码只与缓存交互,由缓存组件来管理自身与数据库之间的数据同步。

    2.3 Write-Through 模式(同步直写) 

    与 Read-Through 类似,发生写请求时,Write-Through 将写入责任转移到缓存系统,由缓存抽象层来完成缓存数据和数据库数据的更新,时序流程图如下:

    Write-Through 的主要好处是应用系统的不需要考虑故障处理和重试逻辑,交给缓存抽象层来管理实现。

    优缺点

    单独直接使用该策略是没啥意义的,因为该策略要先写缓存,再写数据库,对写入操作带来了额外延迟。

    当 Write-Through 与 Read-Through 配合使用,就能充分发挥 Read-Through 的优势,同时还能保证数据一致性,不需要考虑如何将缓存设置失效。

    这个策略颠倒了 Cache-Aside 填充缓存的顺序,并不是在缓存未命中后延迟加载到缓存,而是在数据先写缓存,接着由缓存组件将数据写到数据库。

    优点

    • 缓存与数据库数据总是最新的;

    • 查询性能最佳,因为要查询的数据有可能已经被写到缓存中了。

    缺点

    不经常请求的数据也会写入缓存,从而导致缓存更大、成本更高。

    2.4 Write-Behind 模式

    这个图一眼看去似乎与 Write-Through 一样,其实不是。区别在于最后一个箭头的箭头:它从实心变为线。

    这意味着缓存系统将异步更新数据库数据,应用系统只与缓存系统交互。

    应用程序不必等待数据库更新完成,从而提高应用程序性能,因为对数据库的更新是最慢的操作。

    这种策略下,缓存与数据库的一致性不强,对一致性高的系统不建议使用。

    3. 旁路缓存下的一致性问题分析

    业务场景用的最多的就是 Cache-Aside (旁路缓存) 策略,在该策略下,客户端对数据的读取流程是先读取缓存,如果命中则返回;未命中,则从数据库读取并把数据写到缓存中,所以读操作不会导致缓存与数据库的不一致。

    重点是写操作,数据库和缓存都需要修改,而两者就会存在一个先后顺序,可能会导致数据不再一致。针对写,我们需要考虑两个问题:

    • 先更新缓存还是更新数据库?

    • 当数据发生变化时,选择修改缓存(update),还是删除缓存(delete)?

    将这两个问题排列组合,会出现四种方案:

    • 先更新缓存,再更新数据库;

    • 先更新数据库,再更新缓存;

    • 先删除缓存,再更新数据库;

    • 先更新数据库,再删除缓存。

    接下来的分析大家不必死记硬背,关键在于在推演的过程中大家只需要考虑以下两个场景会不会带来严重问题即可:

    • 其中第一个操作成功,第二个失败会导致什么问题?

    • 在高并发情况下会不会造成读取数据不一致?

    为啥不考虑第一个失败,第二个成功的情况呀?

    既然第一个都失败了,第二个就不用执行了,直接在第一步返回 50x 等异常信息即可,不会出现不一致问题。

    只有第一个成功,第二个失败才让人头痛,想要保证他们的原子性,就涉及到分布式事务的范畴了。

    3.1 先更新缓存,再更新数据库

     

    如果先更新缓存成功,写数据库失败,就会导致缓存是最新数据,数据库是旧数据,那缓存就是脏数据了。

    之后,其他查询立马请求进来的时候就会获取这个数据,而这个数据数据库中却不存在。

    数据库都不存在的数据,缓存并返回客户端就毫无意义了。

    该方案直接 Pass。

    3.2 先更新数据库,再更新缓存

    一切正常的情况如下:

    1. 先写数据库,成功;

    2. 再 update 缓存,成功。

    更新缓存失败

    这时候我们来推断下,假如这两个操作的原子性被破坏:第一步成功,第二步失败会导致什么问题?

    会导致数据库是最新数据,缓存是旧数据,出现一致性问题。

    该图与上一个图类似,把 Redis 和 MySQL 的位置对调即可。

    高并发场景

    谢霸歌经常 996,腰酸脖子疼,bug 越写越多,想去按摩推拿放提升下编程技巧。

    疫情影响,单子来之不易,高端会所的技师都争先恐后想接这一单,高并发啊兄弟们。

    在进店以后,前台会将顾客信息录入系统,执行 set xx的服务技师 = 待定的初始值表示目前无人接待保存到数据库和缓存中,之后再安排技师按摩服务。

    如下图所示:

    1. 98 号技师先下手为强,向系统发送 set 谢霸歌的服务技师 = 98 的指令写入数据库,这时候系统的网络出现波动,卡顿了,数据还没来得及写到缓存;

    2. 接下来,520 号技师也向系统发送 set 谢霸哥的服务技师 = 520写到数据库中,并且也把这个数据写到缓存中了;

    3. 这时候之前的 98 号技师的写缓存请求开始执行,顺利将数据 set 谢霸歌的服务技师 = 98 写到缓存中。

    最后发现,数据库的值 = set 谢霸哥的服务技师 = 520,而缓存的值= set 谢霸歌的服务技师 = 98。

    520 号技师在缓存中的最新数据被 98 号技师的旧数据覆盖了。

    所以,在高并发的场景中,多线程同时写数据再写缓存,就会出现缓存是旧值,数据库是最新值的不一致情况。

    该方案直接 pass。

    如果第一步就失败,直接返回 50x 异常,并不会出现数据不一致。

    3.3 先删缓存,再更新数据库

    按照「码哥」前面说的套路,假设第一个操作成功,第二个操作失败推断下会发生什么?高并发场景下又会发生什么?

    第二步写数据库失败

    假设现在有两个请求:写请求 A,读请求 B。

    写请求 A 第一步先删除缓存成功,写数据到数据库失败,就会导致该次写数据丢失,数据库保存的是旧值。

    接着另一个读请 B 求进来,发现缓存不存在,从数据库读取旧数据并写到缓存中。

    高并发下的问题

    1. 还是 98 号技师先下手为强,系统接收请求把缓存数据删除,当系统准备将 set 肖菜鸡的服务技师 = 98写到数据库的时候发生卡顿,来不及写入;

    2. 这时候,大堂经理向系统执行读请求,查下肖菜鸡有没有技师接待,方便安排技师服务,系统发现缓存中没数据,于是乎就从数据库读取到旧数据 set 肖菜鸡的服务技师 = 待定,并写到缓存中;

    3. 这时候,原先卡顿的 98 号技师写数据 set 肖菜鸡的服务技师 = 98到数据库的操作完成。

    这样子会出现缓存的是旧数据,在缓存过期之前无法读取到最数据。肖菜鸡本就被 98 号技师接单了,但是大堂经理却以为没人接待。

    该方案 pass,因为第一步成功,第二步失败,会造成数据库是旧数据,缓存中没数据继续从数据库读取旧值写入缓存,造成数据不一致,还会多一次 cahche。

    不论是异常情况还是高并发场景,会导致数据不一致。miss。

     3.4 先更新数据库,再删缓存

    经过前面的三个方案,全都被 pass 了,分析下最后的方案到底行不行。

    按照「套路」,分别判断异常和高并发会造成什么问题。

    该策略可以知道,在写数据库阶段失败的话就直返返回客户端异常,不需要执行缓存操作了。

    所以第一步失败不会出现数据不一致的情况。

    删缓存失败

    重点在于第一步写最新数据到数据库成功,删除缓存失败怎么办?

    可以把这两个操作放在一个事务中,当缓存删除失败,那就把写数据库回滚。

    高并发场景下不合适,容易出现大事务,造成死锁问题。

    如果不回滚,那就出现数据库是新数据,缓存还是旧数据,数据不一致了,咋办?

    所以,我们要想办法让缓存删除成功,不然只能等到有效期失效那可不行。

    使用重试机制。

    比如重试三次,三次都失败则记录日志到数据库,使用分布式调度组件 xxl-job 等实现后续的处理。

    在高并发的场景下,重试最好使用异步方式,比如发送消息到 mq 中间件,实现异步解耦。

    亦或是利用 Canal 框架订阅 MySQL binlog 日志,监听对应的更新请求,执行删除对应缓存操作。

    高并发场景

    再来分析下高并发读写会有什么问题。

     

    1. 98 号技师先下手为强,接下肖菜鸡的这笔生意,数据库执行 set 肖菜鸡的服务技师 = 98;还是网络卡顿了下,没来得及执行删除缓存操作;

    2. 主管 Candy 向系统执行读请求,查下肖菜鸡有没有技师接待,发现缓存中有数据 肖菜鸡的服务技师 = 待定,直接返回信息给客户端,主管以为没人接待;

    3. 原先 98 号技师接单,由于卡顿没删除缓存的操作现在执行删除成功;

    4. 读请求可能出现少量读取旧数据的情况,但是很快旧数据就会被删除,之后的请求都能获取最新数据,问题不大。

    还有一种比较极端的情况,缓存自动失效的时候又遇到了高并发读写的情况。假设这会有两个请求,一个线程 A 做查询操作,一个线程 B 做更新操作,那么会有如下情形产生:

    1. 缓存的过期时间到期,缓存失效;

    2. 线程 A 读请求读取缓存,没命中,则查询数据库得到一个旧的值(因为 B 会写新值,相对而言就是旧的值了),准备把数据写到缓存时发送网络问题卡顿了;

    3. 线程 B 执行写操作,将新值写数据库;

    4. 线程 B 执行删除缓存;

    5. 线程 A 继续,从卡顿中醒来,把查询到的旧值写到入缓存。

    这还是出现了不一致的情况啊。

    不要慌,发生这个情况的概率微乎其微,发生上述情况的必要条件是:

    • 步骤 3 写数据库操作要比步骤 2 读操作耗时短速度快,才可能使得步骤 4 先于步骤 5;

    • 缓存刚好到达过期时限。

    通常 MySQL 单机的 QPS 大概 5K 左右,而 TPS 大概 1K 左右,(ps:Tomcat 的 QPS 4K 左右,TPS = 1K 左右)。

    数据库读操作是远快于写操作的(正是因为如此,才做读写分离),所以步骤(3)要比步骤(2)更快这个情景很难出现,同时还要配合缓存刚好失效。

    所以,在用旁路缓存策略的时候,对于写操作推荐使用:先更新数据库,再删除缓存。

     4. 一致性解决方案有哪些?

    最后,针对 Cache-Aside (旁路缓存) 策略,写操作使用先更新数据库,再删除缓存的情况下,我们来分析下数据一致性解决方案都有哪些?

    4.1 缓存延时双删

    如果采用先删除缓存,再更新数据库如何避免出现脏数据?

    采用延时双删策略:

    1. 先删除缓存;

    2. 写数据库;

    3. 休眠 500 毫秒,再删除缓存。

    这样子最多只会出现 500 毫秒的脏数据读取时间。关键是这个休眠时间怎么确定呢?

    延迟时间的目的就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

    所以我们需要自行评估项目的读数据业务逻辑的耗时,在读耗时的基础上加几百毫秒作为延迟时间即可。

    4.2 删除缓存重试机制

    缓存删除失败怎么办?比如延迟双删的第二次删除失败,那岂不是无法删除脏数据。

    使用重试机制,保证删除缓存成功。

    比如重试三次,三次都失败则记录日志到数据库并发送警告让人工介入。

    在高并发的场景下,重试最好使用异步方式,比如发送消息到 mq 中间件,实现异步解耦。

    第 5 步如果删除失败且未达到重试最大次数则将消息重新入队,直到删除成功,否则就记录到数据库,人工介入。

    该方案有个缺点,就是对业务代码中造成侵入,于是就有了下一个方案。启动一个专门订阅 数据库 binlog 的服务读取需要删除的数据进行缓存删除操作。

     4.3 读取 binlog 异步删除

     

    1. 更新数据库;

    2. 数据库会把操作信息记录在 binlog 日志中;

    3. 使用 canal 订阅 binlog 日志获取目标数据和 key;

    4. 缓存删除系统获取 canal 的数据,解析目标 key,尝试删除缓存。

    5. 如果删除失败则将消息发送到消息队列;

    6. 缓存删除系统重新从消息队列获取数据,再次执行删除操作。

    总结

    缓存策略的最佳实践是 Cache Aside 模式。分别分为读缓存最佳实践和写缓存最佳实践。

    读缓存最佳实践

    先读缓存,命中则返回;

    • 未命中则查询数据库,再写到缓存中。

    写缓存最佳实践

    • 先写数据库,再操作缓存;

    • 直接删除缓存,而不是修改。

    当缓存的更新成本很高需要访问多张表联合计算时,建议直接删除缓存,而不是更新。另外,删除缓存操作简单,副作用只是增加了一次 chache miss,建议大家使用该策略。

    在以上最佳实践下,为了尽可能保证缓存与数据库的一致性,我们可以采用延迟双删。

    防止删除失败,我们采用异步重试机制保证能正确删除。异步机制我们可以发送删除消息到 MQ 消息中间件,或者利用 Canal 订阅 MySQL binlog 日志监听写请求删除对应缓存。

    那么,如果我非要保证绝对一致性怎么办?

    先给出结论:

    没有办法做到绝对的一致性,这是由 CAP 理论决定的。缓存系统适用的场景就是非强一致性的场景,所以它属于 CAP 中的 AP。

    所以,我们得委曲求全,可以去做到 BASE 理论中说的最终一致性。

    其实一旦在方案中使用了缓存,那往往也就意味着我们放弃了数据的强一致性,但这也意味着我们的系统在性能上能够得到一些提升。

    所谓 tradeoff 正是如此。

    展开全文
  • MySQL 怎么保证备份数据的一致性

    千次阅读 2022-04-08 12:44:17
    为了数据安全,数据库需要定期备份,这个大家都懂,然而数据库备份的时候,最怕写操作,因为这个最容易导致数据的不一致,松哥举一个简单的例子大家来看下: 假设在数据库备份期间,有用户下单了,那么可能会出现...

    为了数据安全,数据库需要定期备份,这个大家都懂,然而数据库备份的时候,最怕写操作,因为这个最容易导致数据的不一致,松哥举一个简单的例子大家来看下:

    假设在数据库备份期间,有用户下单了,那么可能会出现如下问题:

    • 库存表扣库存。
    • 备份库存表。
    • 备份订单表数据。
    • 订单表添加订单。
    • 用户表扣除账户余额。
    • 备份用户表。

    如果按照上面这样的逻辑执行,备份文件中的订单表就少了一条记录。将来如果使用这个备份文件恢复数据的话,就少了一条记录,造成数据不一致。

    为了解决这个问题,MySQL 中提供了很多方案,我们来逐一进行讲解并分析其优劣。

    1. 全库只读

    要解决这个问题,我们最容易想到的办法就是在数据库备份期间设置数据库只读,不能写,这样就不用担心数据不一致了,设置全库只读的办法也很简单,首先我们执行如下 SQL 先看看对应变量的值:

    show variables like 'read_only';
    

    可以看到,默认情况下,read_only 是 OFF,即关闭状态,我们先把它改为 ON,执行如下 SQL:

    set global read_only=1;
    

    1 表示 ON,0 表示 OFF,执行结果如下:

    这个 read_only 对 super 用户无效,所以设置完成后,接下来我们退出来这个会话,然后创建一个不包含 super 权限的用户,用新用户登录,登录成功之后,执行一个插入 SQL,结果如下:

    可以看到,这个错误信息中说,现在的 MySQL 是只读的(只能查询),不能执行当前 SQL。

    加了只读属性,就不用担心备份的时候发生数据不一致的问题了。

    但是 read_only 我们通常用来标识一个 MySQL 实例是主库还是从库:

    • read_only=0,表示该实例为主库。数据库管理员 DBA 可能每隔一段时间就会对该实例写入一些业务无关的数据来判断主库是否可写,是否可用,这就是常见的探测主库实例是否活着的。
    • read_only=1,表示该实例为从库。每隔一段时间探活,往往只会对从库进行读操作,比如select 1;这样进行探活从库。

    所以,read_only 这个属性其实并不适合用来做备份,而且如果使用了 read_only 属性将整个库设置为 readonly 之后,如果客户端发生异常,则数据库就会一直保持 readonly 状态,这样会导致整个库长时间处于不可写状态,风险很高。

    因此这种方案不合格。

    2. 全局锁

    全局锁,顾名思义,就是把整个库锁起来,锁起来的库就不能增删改了,只能读了。

    那么我们看看怎么使用全局锁。MySQL 提供了一个加全局读锁的方法,命令是 flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的增删改等操作就会被阻塞。

    从图中可以看到,使用 flush tables with read lock; 指令可以锁定表;使用 unlock tables; 指令则可以完成解锁操作(会话断开时也会自动解锁)。

    和第一小节的方案相比,FTWRL 有一点进步,即:执行 FTWRL 命令之后如果客户端发生异常断开,那么 MySQL 会自动释放这个全局锁,整个库回到可以正常更新的状态,而不会一直处于只读状态。

    但是!!!

    加了全局锁,就意味着整个数据库在备份期间都是只读状态,那么在数据库备份期间,业务就只能停摆了。

    所以这种方式也不是最佳方案。

    3. 事务

    不知道小伙伴们是否还记得松哥之前和大家分享的数据库的隔离级别,四种隔离级别中有一个是可重复读(REPEATABLE READ),这也是 MySQL 默认的隔离级别。

    在这个隔离级别下,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。(因为正在执行的事务所产生的数据变化不能被外部看到)。

    换言之,在 InnoDB 这种支持事务的存储引擎中,那么我们就可以在备份数据库之前先开启事务,此时会先创建一致性视图,然后整个事务执行期间都在用这个一致性视图,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作,并且这些更新操作不会被当前事务看到。

    在可重复读的隔离级别下,即使其他事务更新了表数据,也不会影响备份数据库的事务读取结果,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。

    具体操作也很简单,使用 mysqldump 备份数据库的时候,加上 -–single-transaction 参数即可。

    为了看到 -–single-transaction 参数的作用,我们可以先开启 general_loggeneral_log 即 General Query Log,它记录了 MySQL 服务器的操作。当客户端连接、断开连接、接收到客户端的 SQL 语句时,会向 general_log 中写入日志,开启 general_log 会损失一定的性能,但是在开发、测试环境下开启日志,可以帮忙我们加快排查出现的问题。

    通过如下查询我们可以看到,默认情况下 general_log 并没有开启:

    我们可以通过修改配置文件 my.cnf(Linux)/my.ini(Windows),在 mysqld 下面增加或修改(如已存在配置项)general_log 的值为1,修改后重启 MySQL 服务即可生效。

    也可以通过在 MySQL 终端执行 set global general_log = ON 来开启 general log,此方法可以不用重启 MySQL

    开启之后,默认日志的目录是 mysql 的 data 目录,文件名默认为 主机名.log

    接下来,我们先来执行一个不带 -–single-transaction 参数的备份,如下:

    mysqldump -h localhost -uroot -p123 test08 > test08.sql
    

    大家注意默认的 general_log 的位置。

    接下来我们再来加上 -–single-transaction 参数看看:

    mysqldump -h localhost -uroot -p123 --single-transaction test08 > test08.sql
    

    大家看我蓝色选中的部分,可以看到,确实先开启了事务,然后才开始备份的,对比不加 -–single-transaction 参数的日志,多了开启事务这一部分。

    4. 小结

    总结一下,加事务备份似乎是一个不错的选择,不过这个方案也有一个局限性,那就是只适用于支持事务的引擎如 InnoDB,对于 MyISAM 这样的存储引擎,如果要备份,还是乖乖的使用全局锁吧。

    展开全文
  • springboot 缓存一致性常用解决方案

    千次阅读 多人点赞 2022-09-03 16:21:43
    springboot 缓存一致性常用解决方案
  • 分布式一致性协议

    千次阅读 2022-03-22 23:37:11
    分布式一致性协议主要分为单主协议,多主协议。它们的核心区别在于是否允许多个节点发起写操作,单主协议只允许由主节点发起写操作,因此它可以保证操作有序性,一致性更强
  • Flink 状态一致性

    千次阅读 2022-03-27 20:40:25
    Flink 状态一致性
  • 数据一致性解决方案

    千次阅读 2022-03-12 21:21:17
    数据一致性解决方案 CAP理论 C:一致性、A:可用性、P:分区容错性 CAP只能满足两个 CA:两阶段提交的严格选举协议 CP弱A:RAFT协议等多数派选举协议 AP:GOSSIP等冲突解决协议 数据一致性 时间一致性:所有相关数据...
  • 数据库事务一致性的理解

    千次阅读 2022-01-25 12:00:48
    一致性的定义 百度百科-一致性一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。 百度百科-事务一致性: ...事务的一致性是指事务执行前后,数据都是正确的,存在
  • 分布式数据库的数据一致性管理是其最重要的内核技术之一,也是保证分布式数据库满足数据库最基本的ACID特性中的 “一致性”(Consistency)的保障。在分布式技术发展下,数据一致性的解决方法和技术也在不断的演进,...
  • 一致性模型有哪些?

    万次阅读 多人点赞 2021-04-12 00:09:31
    在工程中常用的一致性模型有:强一致性(Strong Consistency), 弱一致性(Weak Consistency),最终一致性(Eventual Consistency 1. 强一致性:系统中的某个数据被成功更新后,后续任何对该数据的读取操作都将得到更新...
  • 一致性聚类

    千次阅读 2020-12-13 22:46:12
    因此,一致性聚类是协调来自不同来源或同一算法的不同运行的关于同一数据集的聚类信息的问题。 非监督学习的一致性聚类类似于监督学习的中的集成学习(顾名思义,就是将多个单一模型进行组合,最后形成一个更好的...
  • 分布式系统的一致性问题(汇总)

    万次阅读 多人点赞 2019-09-02 15:32:19
    保证分布式系统数据一致性的6种方案 问题的起源 在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用...在分布式系统来说,如果想牺牲一致性,CAP 理论告诉我们只能放弃可用性,这显然能接受...
  • 下面通过几个问题来说明kafka如何来保证消息可靠性与一致性 在kafka中ISR是什么? 在zk中会保存AR(Assigned Replicas)列表,其中包含了分区所有的副本,其中 AR = ISR+OSR ISR(in sync replica):是kafka动态...
  • 上一篇:单业务的一致性(CAP中的C【Consistency】)-02CAP介绍 1.使用Seata做强一致性分布式事务 还是我们开头提出的问题:如何保证1.1、1.2、1.3要么同时成功,要么同时失败,本小节,使用alibaba seata作为...
  • Consistency(一致性): 多个副本节点保持数据的一致性。 Availability(可用性): 指系统一直处于可用的状态。 Partition Tolerance(分区容错性): 遇到任何网络分区故障, 仍然能够保证对外提供满足一致性和...
  • 数据库怎么保证(分布式)事务一致性

    万次阅读 多人点赞 2019-03-15 10:15:43
    浅谈事务与一致性问题 原文地址 https://www.jianshu.com/p/f0a1b00a6002 在高并发场景下,分布式储存和处理已经是常用手段。但分布式的结构势必会带来“一致”的麻烦问题,而事务正是解决这一问题而引入的一种...
  • 相位一致性的基本原理及应用问题

    千次阅读 2021-09-02 11:26:01
    Morrone和Owens于1987年提出了基于局部能量特征的检测方法,为解决该问题提供了新思路,即用相位一致性检测特征。P. Kovesi于1995年对该方法做出了改进,克服了噪声等问题,使该方法的应用得以保证。近期阅读有关...
  • 浅析数据一致性

    万次阅读 多人点赞 2016-02-19 15:27:38
    什么是数据一致性?  在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据一致,数据内容冲突。 实践中,导致数据一致的情况...
  • RabbitMQ消息最终一致性解决方案

    千次阅读 2019-09-29 10:50:38
    RabbitMQ消息最终一致性解决方案 随着分布式服务架构的流行与普及,原来在单体应用中执行的多个逻辑操作,现在被拆分成了多个服务之间的远程调用。...无法回滚,那么最终就会导致两边数据不一致性的问题...
  • 如何保证分布式系统数据一致性

    万次阅读 多人点赞 2018-12-24 10:26:05
    面试的时候,有面试官问到:选取你比较熟悉的项目,谈谈如何在做容灾负载的时候数据一致性问题,具体点比如你里面的派单,如何保证一个司机在同一时间内接到两个订单,然后保证实时性?  一般的解决方案是在派单...
  • 数据一致性:数据一致性是评估数据质量的一个关键点,通常我们说的一致性是指用来描述同一信息主体在不同的数据集中是否相同。 数据完整性:描述数据信息缺失的程度,数据缺失的情况可以分为数据信息记录缺失和字段...
  • 微服务系统中的数据一致性,你都会了吗

    万次阅读 多人点赞 2021-09-17 23:11:34
    你好,我是看山。 从单体架构到分布式架构,从巨石架构到微服务...需要注意一下,本文所设计的数据一致性,不是多数据副本之间保持数据一致性,而是系统之间的业务数据保持一致性。 本地事务 在早期的系统中,我们可.
  • 分布式系统可用性与一致性

    千次阅读 2017-06-23 09:55:27
    可用性(Availability)和一致性(Consistency)是分布式系统的基本问题,先有著名的CAP理论定义过分布式环境下二者可兼得的关系,又有神秘的Paxos协议号称是史上最简单的分布式系统一致性算法并获得图灵奖,再有...
  • Redis和MySQL如何保持数据一致性

    千次阅读 多人点赞 2022-03-14 19:48:09
    Redis和MySQL如何保持数据一致性? 在高并发的场景下,大量的请求直接访问Mysql很容易造成性能问题。所以,我们都会用Redis来做数据的缓存,削减对数据库的请求。但是,Mysql和Redis是两种不同的数据库,如何保证不同...
  • Kappa一致性分析

    千次阅读 2020-12-24 07:56:03
    Kappa分析,主要评价的是两种实验方法或检测手段结果的一致性程度;例如,对于幽门螺旋杆菌(Hp)的检测有C13呼气试验和病理活检等手段,其中C13呼气试验已经成为检验患者是否患有幽门螺旋杆菌感染的‘金标准’,那么...
  • ZooKeeper是如何保证数据一致性

    万次阅读 2019-08-05 11:03:02
    前面在讲HDFS和HBase架构分析的时候就提到了Zookeeper。...今天要讨论的是分布式系统一致性与Zookeeper的架构 通过之前的文章大家应该已经了解了HDFS为了保证整个集群的高可用,需要部署两台NameNod...
  • Redis与数据库一致性问题分析

    万次阅读 多人点赞 2019-07-01 20:35:58
    缓存已经在项目中被广泛使用,在读取缓存方面,大家没啥疑问,都是...先做一个说明,从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写...
  • 如何保证Redis缓存和数据库一致性

    千次阅读 2022-04-06 19:44:16
    首先我们先了解下缓存是什么?...另外redis可以完成一部份数据的持久化,而memecache完全将数据保存在内存中,进行持久化,如果服务器出问题,数据将全部丢失,另外一个原因是redis底层实现优化比memecach
  • 多处理机的Cache一致性问题及其解决方案多处理机的Cache一致性问题及其解决方案1 多处理机体系结构2 Cache一致性问题3 监听一致性协议3.1 写作废方式3.1.1 具体流程3.1.2 状态转换图3.2 写更新方式4 目录一致性协议 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,205,219
精华内容 482,087
热门标签
关键字:

信息的不一致性