服务器端一致性在服务器端 , 我们需要更深入地研究更新如何流经系统 , 以理解是什么驱动了使用系统的开发人员可以体验不同的模式 。在开始之前 , 让我们先建立一些定义:
N =存储数据副本的节点数
W =在更新完成之前需要确认已收到更新的副本的数量
R =通过读操作访问数据对象时所接触的副本数
如果W+R > N , 那么写集和读集总是重叠的 , 可以保证强一致性 。在实现同步复制的主备份RDBMS场景中 , N=2、W=2和R=1 。无论客户端从哪个副本读取数据 , 它都将得到一致的答案 。在启用了从备份读取数据的异步复制中 , N=2, W=1, R=1 。在这种情况下 , R+W=N , 一致性无法保证 。
这些配置是基本的quorum协议 , 它们的问题在于 , 当系统由于故障而无法写入W个节点时 , 写入操作必须失败 , 标志着系统不可用 。当N=3 W=3且只有两个节点可用时 , 系统将不得不失败写操作 。
在需要提供高性能和高可用性的分布式存储系统中 , 副本的数量通常大于两个 。只关注容错的系统通常使用N=3 (W=2和R=2配置) 。需要提供非常高读负载的系统经常复制超出容错要求的数据;N可以是数十个甚至数百个节点 , R配置为1 , 这样一次读取就会返回一个结果 。关注一致性的系统被设置为W=N进行更新 , 这可能会降低写入成功的可能性 。这些系统关注容错性但不具有一致性 , 它们的一种常见配置是使用W=1运行以获得最小的更新持久性 , 然后依赖一种惰性(流行)技术来更新其他副本 。
如何配置N、W和R取决于常见情况是什么 , 以及需要优化哪些性能路径 。在R=1和N=W的情况下 , 我们优化读的情况 , 而在W=1和R=N的情况下 , 我们优化写的非常快 。当然 , 在后一种情况下 , 在存在故障的情况下 , 持久性不能得到保证 , 如果W < (N+1)/2 , 那么当写集不重叠时 , 就有可能出现写冲突 。
当W+R <= N时出现弱/最终一致性 , 这意味着读写集有可能不重叠 。如果这是一个有意的配置 , 并且不是基于失败的情况 , 那么将R设为1以外的任何值都没有意义 。这种情况通常发生在两种情况中:第一种是前面提到的为了读扩展而进行的大规模复制;第二个问题是数据访问更加复杂 。在简单的键-值模型中 , 比较不同版本以确定写入系统的最新值很容易 , 但是在返回对象集的系统中 , 确定正确的最新值集就比较困难了 。在大多数写集小于副本集的系统中 , 会有一种机制以一种惰性的方式将更新应用到副本集的其余节点 。直到所有副本都被更新为止的时间段是前面讨论过的不一致窗口 。如果W+R <= N , 则系统容易从尚未接收到更新的节点读取数据 。
“读你的写”、会话和单调一致性是否可以实现 , 通常取决于客户机对执行它们的分布式协议的服务器的“粘性” 。如果每次都是相同的服务器 , 则相对容易保证“读己之所写”和单调的读取 。这使得管理负载平衡和容错稍微困难一些 , 但这是一个简单的解决方案 。使用会话 , 这是粘性的 , 使这一点显式 , 并提供了一个客户端可以推理的公开级别 。
有时客户端实现read-your-write和单调读取 。通过在写操作上添加版本 , 客户端将丢弃对版本在最后一个版本之前的值的读取 。
当系统中的一些节点无法到达其他节点时 , 就会发生分区 , 但两个节点集都可以被客户端组访问 。如果您使用传统的多数仲裁方法 , 那么具有W个副本集节点的分区可以继续进行更新 , 而另一个分区变得不可用 。对于读集也是如此 。给定这两个集重叠 , 根据定义 , 少数集变得不可用 。分区并不经常发生 , 但确实会发生在数据中心之间以及数据中心内部 。
在某些应用程序中 , 任何分区的不可用性都是不可接受的 , 重要的是能够到达该分区的客户机能够取得进展 。在这种情况下 , 双方分配一组新的存储节点来接收数据 , 并在分区愈合时执行合并操作 。例如 , 在Amazon中购物车使用这样的write-always系统;在分区的情况下 , 客户可以继续将商品放入购物车 , 即使原来的购物车存在于其他分区上 。一旦分区恢复 , cart应用程序将帮助存储系统合并购物车 。
推荐阅读
- 漫谈分层架构:为什么要进行架构分层?
- 图解Raft:应该是最容易理解的分布式一致性算法
- 探索3种顶级「集成框架」Apache、Spring和Mule
- 分布式系统核心问题简介
- 从 Spring Boot 程序启动深入理解 Netty 异步架构原理
- 什么是链路追踪?分布式系统如何实现链路追踪?
- 双活无共享数据库架构
- Android Jetpack 架构浅析
- 你的平台有这四大技术架构群吗?
- 分布式系统全局唯一ID的几种实现方式