DDIA笔记——数据复制

Table of Contents generated with DocToc

此篇为《数据密集型应用系统设计》(DDIA)读书笔记,笔记可能存在遗漏,建议直接阅读原书。

第五章 数据复制

  • 主从复制
    • 复制滞后
    • 复制滞后带来的问题
  • 多主节点复制
    • 适用场景
    • 处理写冲突
    • 拓扑结构
  • 无主节点复制
    • 读修复和反熵
    • 读写quorum
    • 宽松的quorum和数据回传
    • 多数据中心操作
    • 检测并发写
    • 合并同时写入的值
    • 版本矢量

第五章 数据复制

主从复制

复制滞后

如果一个应用正好从一个异步的从节点读取数据,而该副本落后于主节点,则应用可能会读到过期的信息。由于并非所有的写入都反应到从副本上,如果同时对主节点和从节点发起相同的查询,可能会得到不同的结果。但如果停止写数据库,经过一段时间后,从节点最终会追上主节点并与主节点保持一致,这种效应也被称为最终一致性。

复制滞后带来的问题

三种复制滞后带来的问题场景:

1.读自己的写

许多应用会让用户提交一些数据,然后查看自己所提交的内容,对于异步复制会出现这样的问题:提交数据须发送到主节点,但是当用户读取数据时,数据可能来自从节点,然而这时新数据可能尚未到达从节点,导致用户以为提交的数据丢失。

针对这种情况,我们需要“读写一致性”,该机制保证如果用户重新加载页面,他们总能看到自己最近提交的更新,但对其他用户则没有任何保证(其他用户的更新可能无法及时看到)。

基于主从复制的系统实现读写一致性的可行方案:

  • 如果用户访问可能会被修改的内容,从主节点读取;否则,在从节点读取;所以在查询执行之前需要判断内容是否可能被修改;
  • 如果应用的大部分内容都必须经由主节点,这就丧失了读操作的扩展性。这就需要其他的方案,例如:跟踪最近的更新时间,如果更新后一分钟之内,则从主节点读取;同时监控从节点的复制滞后程度,避免从那些滞后时间过长的从节点读取;
  • 读请求+客户端记住最近更新时的时间戳,系统确保对该用户提供读服务时都应该至少包含了该时间戳的更新,否则可以交给另一个副本来处理或等待至副本接收到最近的更新;
  • 如果副本分布在多数据中心,必须先把请求路由到主节点所在的数据中心(?);

如果同一用户从多个设备访问数据时,还有一些需要考虑的问题:

  • 记住用户上次更新时间戳的方法实现起来会比较困难;
  • 如果副本分布在多数据中心,无法保证来自不同设备的连接经过路由之后都到达同一数据中心;

2.单调读

如图,用户1234发出一条写请求,主节点执行完毕之后,将请求转发给其他从节点,其中从节点1复制滞后较短,从节点2复制滞后较长。此时,用户2345发起了两次对reply_to=5555的读请求,第一次查询成功得到了用户数据,而第二次请求,由于滞后过长,导致返回为空。

在这里插入图片描述

单调读一致性保证:当读取数据时,如果某个用户依次进行多次读取,则他不会看到回滚现象,即在读取较新值之后又发生读旧值的情况。

实现单调读的一种方式是:确保每个用户总是从固定的同一副本执行读取(例如:基于用户ID的哈希算法来选择从节点);

3.前缀一致读

复制滞后带来的因果反常:

Mr. Pooms: How far into the future can you see, Mrs. Cake?

Mrs. Cake: About ten seconds usually, Mr. Pooms.

但是由于滞后程度不同其他人听到的可能是:

Mrs. Cake: About ten seconds usually, Mr. Pooms.

Mr. Pooms: How far into the future can you see, Mrs. Cake?

在这里插入图片描述

为了防止这种异常,引入了另一种保证:前缀一致读(对于一系列按照某个顺序发生的写请求,那么读取这些内容是也会按照当时写入的顺序)。

一种解决方案是确保任何具有因果顺序关系的写入都交给一个分区来完成,但该方案的真实效率会大打折扣。

多主节点复制

由主从复制模型中“一主多从”变为“多主多从”,复制流程类似:处理写的每个主节点都必须将该数据更改转发到所有其他节点 。此时,每个主节点还是其他主节点的从节点。

适用场景

1.多数据中心

在这里插入图片描述

数据中心内部采用主从复制方案,而在数据中心之间,由各个数据中心的主节点来负责同其他数据中心的主节点进行数据的交换和更新。

每个数据中心之间通过广域网连接。

缺点:不同的数据中心可能会同时修改相同的数据,因而必须解决潜在的写冲突。

2.离线客户端操作——网络断开后还要继续工作

3.协作编辑(通常不会将协作编辑等价于数据库复制问题,但二者有很多相似之处)

处理写冲突

1.避免冲突

确保特定用户的更新请求总是路由到特定的数据中心,并在该数据中心的主节点上进行读/写。从用户角度来看,这基本就等价于主从复制模型来。。。

2.收敛于一致状态

至少要保证数据在所有副本中最终状态是一致的。

  • 为每个写入分配一个特定的ID,并制定规则,挑选胜利者;
  • 将所有写入合并为一条,在应用层进行处理;

3.解决冲突最合适的方式可能还是依靠应用层

例如:保存导致冲突的所有可能,在读取数据时,由应用层处理。

拓扑结构

在这里插入图片描述

环形和星形拓扑存在的问题:如果某一节点发生故障,在修复之前,会影响其他节点之间复制日志的转发;

全链接拓扑存在的问题:存在某些网络链路比其他链路更快的情况,从而导致复制日志之间的覆盖;

冲突检测技术在许多多主节点复制系统中的实现还不够完善

无主节点复制

同时向所有副本发出写请求/读请求。

读修复和反熵

当一个失效节点重新上线后,如何赶上中间错过的那些写请求?两种机制:

  • 读修复

当客户端并行读取多个副本时,可以检测到过期值(根据版本号),然后将新值写入到该副本;这种方法主要适合那些被频繁读取到场景。

  • 反熵过程

一些数据存储有后台进程不断查找副本之间数据的差异,将任何缺少的数据从一个副本复制到另一个副本。与基于主节点复制分复制日志不同的是,反熵过程并不保证以特定的顺序复制写入,并且会引入明显的同步滞后。

读写quorum

n个副本,写入需要w个节点确认,读取必须至少查询r个节点,则只要w + r > n,读取节点就一定包含最新值,然后根据版本号就可以确定过期值。不要把wr当作绝对的保证,而是一种灵活可调的读取新值的概率,因为现实情况往往更加复杂

宽松的quorum和数据回传

配置适当的quorum的数据库系统可以容忍某些节点故障,也不需要进行故障切换,同时还可以容忍某些节点变慢(不需要等待所有副本响应)。

在一个大规模集群中(节点数远大于n),客户可能在网络中断期间还能连接到某些数据库节点,但这些节点又不能够满足数据仲裁的那些节点,此时你可能面临一个选择:

  • 如果无法达到wr,将错误明确返回给客户端;
  • 只是将它们暂时写入到一些可以访问到的节点(这些节点并不在n个节点集合中);

第二种方案称为“宽松的仲裁”(sloppy quorum):写入和读取仍然需要wr个成功的响应,但包含了那些并不在先前指定的n个节点。一旦网络问题解决,临时节点需要把接收到的写入全部发送到原始节点上,这就是所谓的“数据回传”。

可以看出,sloppy quorum对于提高写入可用性特别有用,只要有任何w个节点可用,数据库就可以接收新的写入。同时,这也意味着,即使满足w + r > n,也不能保证着读取r个副本时一定能读取到新值,因为新值可能被临时写入n之外的某些节点且尚未回传。所以,sloppy quorum更像是为了数据持久性而设计的一个保证措施。

多数据中心操作

在一些数据库系统中(Cassandra和Voldemort):副本的数量n是所有数据中心的节点总数,每个客户端的写入都会发送到所有副本,但客户端通常只会等待来自本地数据中心内的quorum节点数的确认。

而有些数据库系统(如:Riak)则将客户端与数据库节点之间的通信限制在一个数据中心内,因此n描述的是一个数据中心内的副本数量。集群之间跨数据中心的复制则在后台异步运行,类似于多主节点复制风格。

检测并发写

处理写冲突

  • 最后写入者获胜:
    强制对写请求排序,最后只有一个写入值存活下来。

如何判断两个操作是否并发?

  • Happens-before关系和并发

如果B知道A,或者依赖于A,或者以某种方式在A基础上构建,则称操作A中操作B之前发生。所以,如果两个操作都不在另一个之前发生,那么操作是并发的。(呃。。。)

如果一个操作发生在另一个操作之前,则后面的操作可以覆盖较早的操作;如果属于并发,就需要解决潜在的冲突问题。

如何让确定操作并发性,即两个操作究竟属于并发还是一个发生在另一个之前(依赖)?

  • 服务器为每个主键维护一个版本号,每当主键新值写入时递增版本号,并将新版本号于写入的值一起保存;
  • 当客户端读取主键时,服务器将返回所有当前值以及最新的版本号。且要求写之前,必须先发生读请求;
  • 客户端写请求必须包含:之前读带的版本号、读到的值和新值三者合并后的集合。写请求的响应和读操作一样;
  • 当服务器收到带有特定版本号的写入时,覆盖该版本号或更低版本的所有值;如果一个写请求没有包含版本号,它将与所有其他写入同时进行,不会覆盖任何已有值,其传入的值将包含在后续读请求的返回值列表中;

合并同时写入的值

不舍弃并发写入的值和旧值,而是将旧值和新值进行合并。但是对于删除操作,项目在删除时不能简单地从数据库中删除,系统必须保留一个对应的版本号以恰当的标记该项目需要在合并时删除(结合购物车案例)。

版本矢量

对每个副本的每个主键均定义一个版本号,每个副本在处理写入时增加自己的版本号,并且跟踪从其他副本看到的版本号,通过这些信息来指示要覆盖哪些值、该保留哪些值。所有副本的版本号集合称为版本矢量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309620.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 abp vNext 和 .NET Core 开发博客项目 - 集成Hangfire实现定时任务处理

上一篇文章成功使用了Redis缓存数据,大大提高博客的响应性能。接下来,将完成一个任务调度中心,关于定时任务有多种处理方式,如果你的需求比较简单,比如就是单纯的过多少时间循环执行某个操作,可以直接使用.…

Docker基本组成 和 基本命令

此篇为Docker笔记,文章可能存在疏忽,建议直接观看原视频。 视频地址:https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from333.999.0.0 Docker基本组成 和 基本命令 镜像 image:就好比一个模板,可以通过这个模板…

Docker镜像讲解

此篇为Docker笔记,文章可能存在疏忽,建议直接观看原视频。 视频地址:https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from333.999.0.0 参考:https://blog.csdn.net/11b202/article/details/21389067 Docker镜像讲解 镜像是…

Making the Grade POJ - 3666(离散化+dp)

题意: 给你n个山的高度,单独的一个数可以任意加减,让经过对每座山峰任意加减高度后变成递增或递减的序列时,求对每个数的相加或相减的数目的最小和。 题目: A straight dirt road connects two fields on FJ’s far…

Kubernetes的安全性怎么解?从4个方面为你列出方案清单

导语Kubernetes中的安全性是一个多维问题,必须从各个不同的角度来解决才算完善,这篇文章将从4个方面为读者列出安全清单。正文Kubernetes,经过更快的采用和社区的更多贡献,正日益攀登到新的高度。不过,安全性仍然是Kub…

DDIA笔记—第六章 数据分区

第六章 数据分区 数据分区与数据复制 分区通常与复制结合使用,即每个分区在多个节点都存在副本,这就意味着某条记录属于特定的分区,而同样的内容会保存在不同的节点上以提高系统的容错性。 每个节点同时充当某些分区的主副本和其他分区的从…

Magicodes.IE 2.2发布

Magicodes.IE 2.2发布导入导出通用库,支持DTO导入导出以及动态导出,支持Excel、Word、PDF、CSV和HTML。已加入ncc开源组织.Magicodes.IE2.0发布Magicodes.IE2.1发布如何做好一个开源项目(一)GitHub:https://github.com/dotnetcore/Magicodes.…

C++ 基类,子对象,派生类构造函数调用顺序

#include <iostream> using namespace std;class A {public:A( ) {cout << "A Constructor………" << endl;}~A( ) {cout << "A Destructor………" << endl;} };class B: public A {public:B( ) {cout << "B …

C++ 虚析构函数

代码如下: #include <iostream> using namespace std;class Base {public:Base() {cout << "Base" << endl;}~Base() {cout << "Base destructor" << endl;} };class Derived : public Base {public:Derived() {cout <&…

I - Interesting Permutation Gym - 102394I(排列组合)

题意&#xff1a; 纯数题 1≤i≤n, fimax{a1,a2,…,ai}; 1≤i≤n, gimin{a1,a2,…,ai}; 1≤i≤n, hifi−gi. 数列a是一个排列&#xff0c;问多少种排列方式满足h数列。 题目&#xff1a; DreamGrid has an interesting permutation of 1,2,…,n denoted by a1,a2,…,an. He …

Magicodes.SwaggerUI 已支持.NET Core 3.1

Magicodes.SwaggerUI 通过配置文件简单配置即可快速完成SwaggerUI的配置&#xff0c;包括&#xff1a;SwaggerUI的文档信息API分组API隐藏API JSON生成&#xff08;枚举、API架构Id&#xff09;验证自定义页面支持.NET Core 2.2和3.1。版本日志和使用教程见下文。注意&#xff…

[推荐]大量 Blazor 学习资源(二)

继上一篇《[推荐]大量 Blazor 学习资源&#xff08;一&#xff09;》之后&#xff0c;社区反应不错&#xff0c;但因个人原因导致这篇文章姗姗来迟&#xff0c;不过最终还是来了&#xff01;这篇文章主要收集一些常用组件、书籍和电子书。资料来源&#xff1a;https://github.c…

Sql Server之旅——第八站 看公司这些DBA们设计的这些复合索引

这一篇再说下索引的最后一个主题&#xff0c;索引覆盖&#xff0c;当然学习比较好的捷径是看看那些大师们设计的索引&#xff0c;看从中能提取些什么营养的东西&#xff0c;下面我们看看数据库中一个核心的Orders表。一&#xff1a;查看表的架构1. 先查看这个表的大概架构信息-…

C++ setprecision()用法

io 流控制头文件, 主要是一些操纵用法如setw(int n),setprecision(int n) #include < iomanip > setw(n)用法&#xff1a; 通俗地讲就是预设宽度 #include<iostream> #include <iomanip> using namespace std;int main() {cout << setw(5) <<…

备战ccpc分站赛:秦皇岛和威海站(数论模块和dp模块)

挑战程序设计竞赛&#xff08;第2版&#xff09;练习题 tips&#xff1a;难度&#xff08;个人主观判断&#xff09;&#xff1a; 简单* 简单但卡思维 ** 中 *** 中稍加思考 **** 难 ***** 1 . 记录结果再利用的“动态规划” &#xff08;1&#xff09;基础的动态规划算法&am…

15分钟从零开始搭建支持10w+用户的生产环境(四)

上一篇文章&#xff0c;介绍了这个架构中&#xff0c;WebServer的选择&#xff0c;以及整个架构中扩展时的思路。原文地址&#xff1a;15分钟从零开始搭建支持10w用户的生产环境(三)五、架构实践前边用了三篇文章&#xff0c;详细介绍了这个架构的各个部分的选择以及安装。这篇…

[Java基础]体验Stream流

代码如下: package StreamTest;import java.lang.reflect.Array; import java.util.ArrayList;public class StreamDemo {public static void main(String[] args){ArrayList<String> list new ArrayList<String>();list.add("Tom");list.add("ja…