谈谈数据一致性

什么是数据一致性

数据一致性这个单词在日常开发中,或者各类文章中都能常常看见,咱们经常听见什么东西数据不一致了,形成了必定的损失,赶快修复一下。可是不少同窗对一致性具体表明什么意思,他有什么做用依然不是很了解,今天咱们就来聊聊一致性。java

通常来讲数据一致性咱们能够分红三类,时间点一致性,事务一致性,应用一致性。git

时间点一致性(Point in time Consistency)

时间点一致性我以为也能够叫作副本一致性,时间点一致性的定义为:github

若是全部相关的数据组件在任意时刻都是一致的,那么能够称做为时间点一致性。面试

这个定义若是你了解过CAP理论的话,那么你应该不会太陌生。(若是不熟悉的同窗能够看我这篇文章分布式事务)算法

在CAP中的C的定义为对某个指定的客户端来讲,读操做能返回最新的写操做。咱们能够发现时间点并无规定一致的须要保证是最新的,因此可能有同窗会提出疑问时间点一致性的范围比CAP中的一致性范围要大一点。其实细想一下若是咱们某个数据组件更新了数据,若是为了知足时间点一致性,那么咱们全部相关的数据组件的数据都是一致的,因此其余的数据都会变为最新的,那么其实就和CAP是同样的,都须要知足若是在某个节点更新了数据,那么在其余节点若是都能读取到这个最新的数据。sql

固然CAP和时间点一致性并非彻底的一致:时间点一致性的定义中要求全部数据组件的数据在任意时刻都是彻底一致的,可是通常来讲信息传播的速度最大是光速,其实并不能达到任意时刻一致,总有必定的时间不一致,对于咱们CAP中的一致性来讲只要达到读取到最新数据便可,达到这种状况并不须要严格的任意时间一致。数据库

这里咱们还须要注意的是这个并不老是用于分布式系统中的,在咱们单个机器中若是有多核处理器,咱们再任意时刻访问不一样处理器对同一变量数据都须要是一致的也能够一样适用。编程

事务一致性

一致性不只仅能够表示数据的同时变动或相同性,还能够用来表示约束,而咱们的事务一致性就是其中的一种。事务一致性就是咱们平时所说的ACID中的C,其定义以下:缓存

事务的一致性指的是在一个事务执行以前和执行以后数据库都必须处于一致性状态。若是事务成功地完成,那么系统中全部变化将正确地应用,系统处于有效状态。若是在事务中出现错误,那么系统中的全部变化将自动地回滚,系统返回到原始状态。网络

事务一致性只能存在在事务开始前的和事务完成以后,在事务过程当中数据有可能不一致。举个例子:好比A转100元给B,A扣减100,B加上100,在事务开始前和事务完成以后都能保证他们的账是对上的,那么这就是事务一致性。可是在事务过程当中有可能会出现A扣减了100元,B没有加上100元的状况,这就是不一致。

这里通常的初学者都会把CAP和ACID中的C都会误解成同样的含义,其实他们其中一个表示的数据的相同,而另外一个是用来表示某种约束。

应用一致性

应用一致性能够看作是约束一致性中的一种。上面的事务一致性表明的是单一数据源,若是数据源是多个,好比数据源有多个数据库,文件系统,缓存等。那么就须要咱们应用一致性,这里也看作是分布式事务一致性。

在应用程序中涉及多个不一样的单机事务,只有在全部的单机事务完成以前和完成以后,数据是彻底一致的。好比给用户发送券和积分,券服务和积分服务是两个服务,他们各自有本身单机事务,这两个单机单机事务开始前和完成后都能保证用户的账是对应上的。可是在这两个单机事务执行过程有可能会出现只送了券,没有送积分的状况,有可能状态不正确。

这三种一致性能够简单的看作两类,一个是数据副本一致,另外一个是数据约束一致。接下来我更多的会介绍数据副本的一致的类型,而数据约束的一致,能够参考我以前写过的分布式事务的那篇文章。

一致性的模型

再写这篇文章以前,我一直觉得一致性就那么几个常据说的,强一致,弱一致,最终一致。再查询了一些文献资料以后发现一致性的类型真的是很是的多,这里我挑选一些比较重要的

若是有人问你你知道哪些一致性模型呢?不少人立刻答出,强一致,最终一致。其实一致性的模型远远不止这么点,在《Operational Characterization of Weak Memory Consistency Models》这篇论文当中描述了15种弱内存一致模型,而在维基百科对内存模型的描述还有更多。

不少一致性的模型最开始是用来描述内存是否一致的,也就是最开始并非运用于分布式系统当中的。若是咱们的机器是单核的话,那么他的内存必定是强一致的。若是咱们的机器是多核的话,那么因为处理器并非直接访问的内存而是访问的处理器独享的缓存,那么就有可能会出现不一致。再分布式中咱们的每一个节点其实就能够当作一个独立的处理器,而咱们最初运用于内存一致性模型,也能够运用于咱们分布式系统当中。下面我会从强到弱讲讲一些常见的一致性模型。

线性一致性

线性一致性又叫作原子一致性,强一致性。线性一致性能够看作只有一个单核处理器,或者能够看作只有一个数据副本,而且全部操做都是原子的。在可线性化的分布式系统中,若是某个节点更新了数据,那么在其余节点若是都能读取到这个最新的数据。能够看见线性一致性和咱们的CAP中的C是一致的。

举个非线性一致性的例子,好比有个秒杀活动,你和你的朋友同时去抢购同样东西,有可能他那里的库存已经没了,可是在你手机上显示还有几件,这个就违反了线性一致性,哪怕过了一会你的手机也显示库存没有,也依然是违反了。

线性一致性有什么做用呢?在《DDIA》这本书中描述了下面3个做用:

  • 加锁与主节点选举:主从复制系统须要确保只有一个主节点,不然会产生脑裂。选举新的主节点通常是使用锁:每一个启动的节点都须要得到锁。而这个锁就须要知足可线性化,让全部的节点都同时赞成哪一个节点有锁。咱们的ZooKeeper就能够用来提供分布式锁功能,那么咱们就能够说ZooKeeper是知足线性一致性的吗?这个只能说说对了一部分,后面再顺序一致性的时候会对ZK是什么一致性再次说明。
  • 约束与惟一性保证:好比同一个文件目录下不容许有两个相同的文件名,数据库主键不能重复,这些都须要线性化。其实这些本质和加锁相似,好比相同的文件名,那其实就是对这个文件名去作一个加锁操做,而后去保存,后保存的天然会出错。
  • 跨通道的时间依赖:以前的那个抢购的那个例子为何会被违反呢?缘由是由于咱们经过朋友告知这个通道,让咱们提早知道了这个货物已经卖完。一样的若是咱们计算机中出现了多个通道。举个例子,在用户交易的场景下,用户使用了50元,那么会在其他额中扣减50元,这个时候把这个事件做为一个消息队列给发送出去,而后短信服务会查询用户的余额而后进行发送短信,若是余额数据库的从库这个时候尚未更新数据,那么这个短信就有可能会取到用户旧的余额。这里出现不一致的缘由就是由于多了一个通道,就和咱们上面朋友告知咱们卖完的通道同样。解决这个办法能够控制某一个通道,好比说将这个用户的余额做为参数给传进去,或者只读主库。秒杀的那个例子中,你能够不要本身的手机,去用朋友的手机。

顺序一致性

顺序一致性弱于严格一致性。对变量的写操做不必定要在瞬间看到,可是,不一样处理器对变量的写操做必须在全部处理器上以相同的顺序看到,这里处理器再分布式系统中能够换成不一样的节点。

这里咱们又再回到Zookeeper究竟是什么一致性?有不少面试题都会问到Zookeeper是CP仍是AP呢?不少人都会回答到Zookeeper是CP,其实这个回答并非很严谨的,咱们从线性一致性中知道CAP中的一致性指的是线性一致性,那咱们就能够说Zookeeper是线性一致性的吗?答案是否认的。当咱们写入一个值的时候,会交由Leader去处理,Zab协议只须要保证半数从节点成功便可,那么就会有节点的数据是老的数据,这样客户端就有可能读出的数据并不是是最新的从而破坏了线性一致性。

Zookeeper其实实现的是顺序一致性,在ZK中利用zxid(ZooKeeper Transaction Id),实现了总体顺序一致性,固然也能够认为Zookeeper的的写是线性一致性,读是顺序一致性。从节点经过zxid顺序的接收leader的广播,因此ZK不能保证全部的信息立刻看到,可是最终都会看到。固然Zookeeper其实能够实现线性化,在ZK中有一个sync()命令,只要咱们每次读的时候都去调用sync()强制同步数据,那么咱们都能保证其是最新的。

顺序一致性是由Lamport(Paxos算法的做者)提出的,最开始只用来定义多处理内存的一致性,在Lamport的《How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs》中其定义了什么是顺序一致性:

the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.

这句话的大体意思是多处理器的执行效果和单个处理器的执行效果是同样的,每一个独立的处理器的操做都会按照指定的顺序出如今操做队列。这个最开始是用于并发编程的,可是让多处理器的执行变得和单处理器的确是没啥做用,后来就用于分布式系统当中。在ZK中全部的写操做都会交给Leader节点去作,而且全部操做的更新都会根据zxid的顺序进行更新,这里就是上面所说的指定的顺序,这个队列就是按照zxid的顺序。

因果一致性

因果一致性是弱于顺序一致性的一致性模型,顺序一致性要求全部的操做的顺序都必须按照某个单个处理器(节点)的顺序,而因果一致性只须要知足有因果关系的操做是顺序一致性便可。

怎么理解因果关系呢?简单来讲若是有人问你一个问题,那么你给出答案,这两个就是因果关系,但若是你给出答案再问题以前,那么这个就违反了因果关系。 举个简单的例子若是节点1更新了数据A,节点2读取数据A,并更新数据B,这里的数据B有多是根据数据A计算出来的,全部具有因果关系,可是若是节点3看到的是先更新的B,再更新的A那么就破坏了因果一致性。

处理器一致性

处理器一致性是更加弱的一致性模型,他只须要保证处理器看到某个处理器或者多个不一样处理对相同位置的写入都是一致的。不须要考虑因果关系,而是对同一个内存或者同一个数据更新须要看到一致的顺序。

FIFO一致性

FIFO一致性是比处理器一致性还更加弱的一种,它不须要保证对相同位置的写入是一致的。 是指在一个处理器上完成的全部写操做,将会被以它实际发生的顺序通知给全部其它的处理器;可是在不一样处理器上完成的写操做也许会被其它处理器以不一样于实际执行的顺序所看到。这个在分布式系统中反映了网络中不一样节点的延迟多是不相同的。为了说明其和处理器一致性不一样有以下例子:

上面这个图中,能够发现是违反了处理器一致性的,为何呢由于写入顺序是w(x)1,w(x)2而,p4应该是先R(x)1再R(x)2。可是这个符合FIFO一致性,FIFO只须要把本身的发生顺序通知给其余的处理器或者节点,不须要保证同一个值写入顺序是一致的。

最终一致性

其实除了强一致之外,其余的一致性均可以看做为最终一致性,只是根据一致性不一样模型的不一样要求又衍生出了不少具体一致性模型。固然最简单的最终一致性,是不须要关注中间变化的顺序,只须要保证在某个时间点一致便可。只是这个某个时间点须要根据不一样的系统,不一样业务再去衡量。再最终一致性完成以前,有可能返回任何的值,不会对这些值作任何顺序保证。

BASE理论中的E就是最终一致。

一致性模型有什么用

上面介绍了这么多一致性模型,咱们了解到越强的一致性他的约束条件就越多,若是咱们实现的话成本那么也就会越大。能够看见ZK若是想实现彻底的线性一致性,那么他就须要随时都调用sync()去进行同步数据。

再咱们真实的场景中咱们数据库的主从复制模型(经过binlog复制也是顺序一致性),从库的很大做用就是为了缓解主库的读压力,若是咱们想盲目的达到线性化一致性,那么就必须去访问主库,这样咱们的从库的意义就微乎其微了。

因此根据不一样的系统的模型,不一样的业务要求,咱们对于一致性的要求是不一样的,因此咱们了解这些一致性的模型是有不少必要的。

总结

这篇文章主要是介绍了什么是一致性,包括不少一致性模型,这里少讲了两个一致性事务一致性和应用一致性,有兴趣的能够阅读分布式事务。还有一个值得一提的是,谈到一致性其实就离不开共识,由于当数据副本有多个的时候,到底选择谁,如何选择才是正确的,这个有兴趣的同窗能够自行查阅一些资料,好比Raft,Paxos和Zab等。

最后也再给你们几个问题:

  1. ZK究竟是一致性模型?
  2. Mysql主从是什么一致性模型?
  3. Mysql主主是什么一致性模型?
  4. 你经常使用的一致性模型是什么?

最后这篇文章被我收录于JGrowing-CaseStudy篇,一个全面,优秀,由社区一块儿共建的Java学习路线,若是您想参与开源项目的维护,能够一块儿共建,github地址为:github.com/javagrowing… 麻烦给个小星星哟。

欢迎留言和我讨论,固然最好是关注个人公众号和我一块儿探讨哦,若是你们以为这篇文章对你有帮助,你的关注和转发是对我最大的支持,O(∩_∩)O:

参考文档:

  • 如何理解Zookeeper的顺序一致性:blog.csdn.net/cadem/artic…

  • How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs,Lamprot

  • Operational Characterization of Weak Memory Consistency Models:mp.weixin.qq.com/s/gg4q_53ei…

  • Designing Data-Intensive Applications, Martin Kleppman

相关文章
相关标签/搜索