关于数据一致性你需要知道的那点事

写在前面:

这是我迄今为止看到的最好理解的关于数据一致性的文章,以前只知道CAP,读完这篇文章发现原来一致性跟性能还有着千丝万缕的关系,大家感兴趣的可以去看一下原文以及原作者的其他相关文章,在此奉上原文链接https://robertovitillo.medium.com/what-every-developer-should-know-about-database-consistency-cff3183913cb

 

想象一下, 你刚给一个变量赋了值, 然后接着读取它, 结果发现你的赋值压根就没有起效, 是不是很抓狂? 但是这种情况其实在没有强一致性保证的分布式数据库中是经常发生的。你可能会说,等一下, 数据库其中一项任务不就是为我提供数据一致性保证吗?其实这完全取决于数据库。

一些数据库舍弃掉数据一致性从而提供了更好的可用性和性能。另一些数据库则直接把这个选择权交给了用户, 让你在性能和一致性之间作出选择, 例如Azure’s Cosmos DB and Cassandra。因此,你必须了解该如何作出取舍。

 

剖析数据库请求

让我们看一下当你发送一个请求给数据库的时候到底会发生什么。在理论上, 你的请求会在瞬间执行完毕,就像下图一样:

但是我们生活在现实世界里,你的请求首先要到达数据库, 然后数据库执行你的请求,最后返回给你一个结果。所有这些动作都需要时间来完成, 所以真实的情况应该像下图这样。

数据库所能提供的最好的保证就是对数据的请求操作一定会在请求发起和结果返回之间的某一时间点完成。你可能觉得,这好像是废话, 尽管你在写单线程的应用时已经不知不觉的用到了这个保证, 你把1赋值给x,然后读取x, 你理所应当的认为读到的值肯定是1, 当然,这得是在没有其他线程对x进行写操作的前提下。但是,当你开始接触分布式数据库时,一切都变了。

假设我们有一个key-value型的分布式的数据库, 它由若干个节点组成。这些节点自己选举出主节点, 而且只有主节点才能执行写操作。当主节点收到写请求后, 它将这个请求通过异步广播的形式传递给其他备份节点。所有的备份节点都可以收到相同的更新请求,并且可以保证这些请求在到达备份节点的时候是有序的,但是,这些备份节点会在不同的时间执行这些请求。

这时候让你设计一个策略来处理读取请求, 你会怎么做? 读取操作可以由主节点或者备份节点中的任意一个节点来完成。如果所有的读取操作都由主节点来完成, 那吞吐量就受限于主节点的性能。相反的,如果把这些请求随机分配到任何一个节点,这明显会提高性能,但是两个客户端或者说观察者可能会看到不同的系统状态, 因为备份节点的状态更新是要晚于主节点的,而且备份节点之间的状态更新也是没有任何先后顺序保证的。

所以这里就有了一个关于一致性、系统性能和可用性之间的取舍。为了进一步了解这三者的关系,我们需要先明晰我们所说的一致性到底是什么。在这里我们将借助一致性模型,它准确定义了观察者所能观察到的几种系统状态。

 

强一致性

如果客户端只对主节点进行读写操作,那么很明显,每个操作都将会是在某一个时间点上的原子操作。不管此时有多少个备份节点,也不管备份节点的更新会滞后多长时间, 因为客户端只请求主节点, 所以在客户端看来,这个数据库就是个单节点的数据库。

因为数据请求需要时间,并且现在只有主节点在处理请求, 所以这就回到了我们上面提到的最佳保证, 对数据的请求操作一定会在请求发起和结果返回之间的某一时间点完成。换句话说,就是一旦请求完成了,那它所造成的结果就可以被其他观察者观察到了。

因为一个请求将会在其调用和完成之间对其他参与者变得可见, 这里就有了一个强制性的实时性保证, 这个保证被称作可线性化能力(Linearizability),或者叫强一致性。这是数据库对单对象请求所能提供的最高的一致性保证。

如果客户端发送一个读请求到主节点,但是当请求到达主节点的时候,这个主节点已经被隔离了,但是这个节点并不知情, 它仍然认为自己是主节点, 所以此时如果仍由此节点来完成此次请求, 那将无法保证强一致性。为了防止这种情况发生,主节点需要在处理请求之前与其他备份节点通讯,来确认自己仍然是主节点, 只有在得到多数备份节点的承认后,它才可以处理请求,向客户端返回数据。这明显增加了处理请求所需要的时间。

 

顺序一致性

目前为止, 我们讨论了将读取请求依次发送给主节点的情况。但是这样做会导致瓶颈,限制系统的吞吐量。除此之外, 主节点还需要在处理请求之前与备份节点通信来确定自己主节点的地位。为了提高读取性能, 我们可以允许备份节点参与到处理读取请求的任务中来。

尽管备份节点的状态会滞后于主节点, 但它仍然可以按顺序接收到与主节点一样的更新请求。如果客户端A只请求备份节点1,客户端B只请求备份节点2,那这两个客户端在相同时间可能会看到不同的系统状态,因为备份节点之间是没有同步机制的。

 

 

这种保证操作相对于观察者都是按相同顺序发生, 但是不提供任何实时的一致性保证的一致性模型被称作顺序一致性(sequential consistency)。缺少实时性保证是顺序一致性和强一致性的唯一差别。

这种模型最常见的应用就是生产者/消费者系统中通过队列来实现同步的机制, 生产者往队列中写,消费者来读, 生产者和消费者所看到的顺序是一样的,但是消费者要滞后于生产者。

最终一致性

尽管我们提高了读取吞吐量,但是我们还是必须把客户端和备份节点一一对应起来,如果这时这个备份节点故障了怎么办?我们可以通过允许客户端请求其他节点来提高整个系统的可用性。但是同时我们也要在一致性方面付出高昂的代价。现在假设我们有两个备份节点1和2,2的更新要滞后于1。现在有一个客户端先向节点1发送查询,然后再向节点2发送查询,它将会发现时光倒流了, 这将会使客户端非常困扰。这里我们唯一能对客户端作出的保证就是,如果外界不再向系统发送写请求, 一段时间之后,所有的备份节点最终的状态会一致。这种模型被称之为最终一致性模型。

使用只能保证最终一致性的数据库来进行应用开发是一件充满挑战的事情, 因为这种模式与我们开发单线程应用所用到的截然不同。你会遇到很多难以描述的bug, 并且它们很难调试,甚至很难复现。当然,也不是所有应用都会需要强一致性,你需要根据你的需求来对其作出取舍。如果你只是想记录访问你网站的用户数量,那最终一致性应该就足够了,因为就算你读出了过期的数据,也没什么大不了的。但是对于支付系统,你就必须考虑强一致性了。

 

PACELC原理

还有很多一致性模型我们在这篇文章里没有提及。但是它们的原理都是相通的:越强的一致性保证就会带来越高的延迟以及越弱的系统可用性, 这被称为PACELC原理。它大体是说, 一旦一个分布式系统出现了网络隔离的情况,我们只能在可用性和一致性之间作出选择, 即使没有网络隔离的情况发生,我们也需要在延迟和一致性之间作出选择。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值