一致性模型
一致性模型
模型
Store: 一个黑盒用以提供数据可用性, 以及数据持久性
A/B/C: 三个相互独立的Process, 对Store有读写操作
一致性
强一致性
当A写入到Store后, 之后的A,B,C的读操作都将返回最新值
弱一致性
当A写入到Store后, Store不能保证A,B,C的读操作都将返回最新值.
最终一致性
最终一致性是弱一致性的一种特例。
假如 A 首先 write 了一个值到存储系统,存储系统保证如果在 A,B,C 后续读取之前没有其它写操作更新同样的值的话,最终所有的读取操作都会读取到最 A 写入的最新值。
此种情况下,如果没有失败发生的话,“不一致窗口”的大小依赖于以下的几个因素:交互延迟,系统的负载,以及复制技术中 replica 的个数(这个可以理解为 master/salve 模式中,salve 的个数)。
视角
- 数据一致性
- 事务一致性
数据一致性
为了避免设备与网络的不可靠带来的影响,通常会存储多个数据副本。
逻辑上的一份数据同时存储在多个物理副本上,自然带来了数据一致性问题。
- 状态一致性是指,数据所处的客观、实际状态所体现的一致性.
- 操作一致性是指,外部用户通过协议约定的操作,能够读取到的数据一致性.
状态一致性
严格一致性
要求任何写操作都能立刻同步到其他所有进程,任何读操作都能读取到最新的修改。
要实现这一点,要求存在一个全局时钟,也就是说每台服务器的时间都完全一致,但在分布式场景下很难做到。
之所以需要全局时钟是为了定义顺序, 即数据同步和读操作的顺序, 在任意一个进程都是一致的, 而这种能力只能通过全局时钟来实现.
所以,严格一致性在实际生产环境中目前无法实现。
顺序一致性
所有的进程以相同的顺序看到所有的修改。
读操作未必能及时得到此前其他进程对同一数据的写更新,但是每个进程读到的该数据的不同值的顺序是一致的。
比起严格一致性, 顺序一致性的特点是: 只要能够保证在任意进程上的写操作是有顺序即满足.
因果一致性
因果一致性(Causal Consistency)是分布式系统中一种比较弱的一致性模型。
它不要求系统全局的严格一致性,而是确保如果一个操作在因果关系上先于另一个操作,那么所有的进程在更新数据时,都应该保证这种先后顺序。
换句话说,因果一致性能确保因果相关的操作的顺序,但对于因果无关的操作则不做此保证。
例如,在社交网络中,一个用户发表了一条状态更新(事件A),随后另一个用户对这条状态进行了评论(事件B)。因为评论是对状态更新的直接响应,所以事件B因果依赖于事件A。在因果一致性模型下,任何观察到事件B的进程,也必须能够观察到事件A。但是,如果有一个第三个事件C,它与A和B都不具有因果关系(比如另一个用户在不同的话题上发表了一个不相关的帖子),那么C的发生与A和B的先后顺序在因果一致性模型下是不受限制的。也就是说,不同用户可能会以不同的顺序看到这些不相关的事件。
这里是一个更加具体的例子:
- 用户1在论坛帖子中提出了一个问题(事件A)。
- 用户2看到了这个问题,并发表了自己的回答(事件B)。
- 用户3看到回答后,对该回答进行了点赞(事件C)。
在因果一致性模型中,事件C因果依赖于事件B(用户3必须看到回答才能点赞),而事件B因果依赖于事件A(用户2必须看到问题才能回答)。因此,任何观察到事件C的用户,必须先看到事件B,再看到事件A,以此来维持因果顺序。但是,如果另外有一个用户4同时在另外一个不相关的帖子上发表了一个评论(事件D),那么其他用户看到事件D的顺序就不受这个因果关系的约束,可能在A、B、C之前或之后。
操作一致性
单调读一致性
如果一个进程读取数据项 a 的值,那么该进程对 a 执行的任何后续读操作,总是得到第一次读取的那个值或更新的值。
这个比较容易理解,说白了,就是不能读到新数据后,再读到比这个数据还旧的数据;
如果没读到新数据,一直读的还是旧数据,单调读一致性并不关心这个问题。
单调写一致性
一个进程对数据项 a 执行的写操作,必须在该进程对 a 执行任何后续写操作前完成。
这个很容易满足,注意这里是一个进程,所有的写操作都是顺序的。(这也是为什么要有全局时钟)
即写操作是有顺序的, 先发生的写总是在后发生的写之前.
写后读一致性
一个进程对数据项 a 执行一次写操作的结果,总是会被该进程对 a 执行的后续读操作看见。
这个比较常见,比如数据库采用 Master-Slave 结构部署时,写完 Master 数据库,如果从 Slave 读取,有可能读不到,就不满足写后读一致性了。
读后写一致性
同一进程对数据项 a 执行的读操作之后的写操作,保证发生在与 a 读取值相同或比其更新的值上。
这个问题经常出现的场景是,如果数据存储了多个副本,因为没有及时同步,在第一个副本上读了数据,去第二个副本上写,出现不一致的情况。