分布式系统:Aurora和Frangipani

0.数据库事务

在这里插入图片描述
DB的服务端会从存储中读取需要操作的页,这些页会缓存到内存中。在处理事务时,首先会将对应操作记录在硬盘的log中,然后会操作内存中的页(注意只有累积一段时间的操作后才会覆盖掉硬盘中的页,而非马上覆盖),如果事务提交,会在log中添加对应标志。

崩溃时已提交的事务但还未写入硬盘的页,根据log进行redo。
为什么log中需要记录原始的值?
允许长事务对硬盘中的页的覆盖。如果崩溃时,再进行undo回滚。

1.Aurora

亚马逊的EC2服务是在每个电脑上有一个VMM(虚拟机管理),然后将虚拟机上可以运行各种操作系统,每个也有对应的存储空间,将这些租给用户。用户可以在这上面搭建自己的服务器或数据库。
在这里插入图片描述

为了支持数据库的容错,提出EBS,采用CR方式进行的容错处理。服务器收到读写请求后发生给EBS。但这些通常在一个数据中心,即论文中提到的AZ。

将redo processing放到多用户可扩展的存储服务

1.1 log redo

在这里插入图片描述

(1)为进一步增强容错能力,设计的系统如论文中Fig. 2和上图所示,副本只是重复主服务器的操作,主服务器需要发送修改的整个page,对应日志给副本,只有主/副本都将这些存储到EBS时,才会给客户端响应。
而每个page可能只修改了一小部分,每个page大小可能为8KB,这需要大量数据在网络上发送。

(2)6个replica分布在3个AZ中(每个AZ有2个replica)
Aurora的解决方案是:只发送log,而非整个page。尽管需要发送到6个replica,但发送的日志相比整个page还是小了很多。这样的设计丧失了通用性换来了性能。
(3)Avoiding quorum reads
若有4个响应了写请求,则可以认为提交了该日志条目。
SCN:系统提交事务log的Index
对于读请求,并不需要quorum read(本文中的3个响应),因为在运行过程中维护了每个服务器当前最高的LSN,读取时只需要去对应的节点读取即可。
(4)为什么还需要quorum read?
主要是为了恢复日志使用。
VCL:满足quorum write的最高LSN。也就是说在VCL之前的log都认为是提交的(可能事务未被提交)。故障恢复时,需要删掉VCL之后的log,之前的也需要删掉事务未提交的部分。
在这里插入图片描述

硬盘上存储的内容实际上是:最近修改过的页 + 该页对应的日志
只有需要读取该页时,将log中提交的事务执行redo再读取到内存中。
在这里插入图片描述

1.2 quorum

基于quorum的投票协议,共有 V V V个replica,必须满足两个规则:
(1)读操作必须知道最近的写操作,即要求 V r + V w > V V_r + V_w > V Vr+Vw>V
(2)写操作必须知道最近的写操作,即要求 2 ∗ V w > V 2*V_w > V 2Vw>V
(本质上是要求两次连续的写/读(或写写)操作至少有一个replica是重叠,从而保证能看到数据最新的版本)

1.3 增强处理读吞吐量

在这里插入图片描述
(1)对应论文Fig.3 对web应用,读请求往往更频繁,为增强读吞吐量,设置了额外若干个replicas instances。为了防止引入竞争,这些replicas instances只处理读请求,只有 primary instance才能处理写请求。
(2)横线表示:为了使这些replicas instances能够修改内存的page缓存, primary instance需要将log发送给replicas instances。(有可能replicas instances落后于primary ,但这对于web应用不明显)

1.4 快速恢复replica

在这里插入图片描述
当一台电脑的硬盘损坏时,会失去大量数据,可能为若干TB。如果复制整个硬盘的数据会延误很长时间。
将数据库的卷划分为小的固定大小的segment,每6个replica组成一个PG。存储可以看成有各个PG组成,各个PG最好存储的机器最好不重叠。
解决办法就是,损失的副本对应的PG的负责复制,这样相当于是并行复制。

2.Frangipani

分布式文件系统
构建在Petal顶层,Petal对上层提供了一个虚拟硬盘
不同机器上运行的程序都能看到相同的文件,文件一旦改变对所有机器都是可见的。
对文件内容的更改通过本地内核缓冲池暂存,并且不能保证写到硬盘在下一个适用的 fsync 或 sync 系统调用之前
元数据的更改会被记录下来

每个服务器redo log都放在Petal上的不同区域
可以通过让 Petal 服务器仅接受来自属于受信任的 Frangipani 服务器的网络地址列表的请求

Disk Layout

每个Frangipani server会分配一部分独享的bitmap space,只有写满时才会额外分配。只能为新文件分配其bitmap space对应的inode节点,但可以访问其他服务器文件的inode。
每个文件的前64KB存储在small blocks中,其余的存储在large block中。

预写Log仅仅记录元数据的更新。这些记录会周期性的写入到Petal中。只有其写入到Petal中时,服务器才会修改实际的元数据。

文件系统的逻辑位于客户端。

2.1 Cache Coherence

Frangipani将文件层在Frangipani server上实现,为了使用户方便的更改文件,使用了缓存技术。
目的:可以让其他用户看到最近的修改,即保证缓存中的数据能及时被写回到Petal。
方法:实际上是通过锁来实现

(1)有一个额外的锁服务器,每个工作站中都有自己的锁表。当锁的状态变为IDLE时不会立即释放锁,而是等其他服务器对相同文件有请求时,才会进行释放(这样的目的是,防止重复申请锁,即先创建文件,之后很可能会向该文件写入一些内容)
在这里插入图片描述
(2)整体流程:

  1. 首先向锁服务器申请锁 ------------- request
  2. 如果文件上没有锁,锁服务器授予锁 ----------- grant
  3. 如果文件上有锁,那么锁服务器会向持有锁的WS发送撤销请求 --------- revoke
  4. 如果锁的状态为IDLE,则持有锁的WS释放锁,重复第2步。如果为BUSY,则等待其变为IDLE。 ------------- release

(3)遵守的规则:
必须持有锁,才会缓存
释放锁时,如果文件内容被改变,则需要写回到Petal
(4)注意的点
为防止崩溃时丢失太多数据,每隔一段固定时期,就会将脏数据写回Petal
为防止client失败,每个锁提供了一个lease
上述锁其实并不是在文件上加锁(以文件表达便于理解),原文是直接在Block块上提供锁。

2.2 atomicity

此处是指:事务的原子性,即创建文件的流程可能包含一系列操作,包括分配Inode节点、分配bitmap等,保证这些操作要么都执行,要么都不执行。

此处也是通过锁来实现,在操作开始,获取所有需要的锁。操作完成后才将锁释放。

2.3 Crash Recovery

预写日志
每个服务器都有自己的日志,方便崩溃后有其他服务器接管。
只有具备完整性的日志(checksum检查),才可能会进行redo。
为了防止重复执行做过的操作,日志中包含版本号,如果当前数据的版本号 >= 日志中的(说明服务器之前释放了锁,由其他服务器进行了操作),则不进行操作。

问题:服务器崩溃后进行恢复时,可能有些数据的锁被其他服务器持有,如何解决这种情况?
如果这些数据的锁被其他服务器持有,那么说明之前崩溃的服务器将该锁释放了,那么这些数据的版本号肯定满足 >= 日志中的版本号,因此可以不用管这部分的日志。

Lab3

  1. 第一个需要注意的地方是KVServer中的me,与Clerk中的序号不同一样。假设按照如下方式调用:
ck.servers[0].Call()   // 那么对应的KVServer中的kv.me可能为3

因此需要建立一个ck.servers与KVServer中me的映射关系,在Raft中可以通过AppendEntries获取当前Leader的id,如果没有这个映射关系,无法直接使用rf.Leader_Id

  1. 判断重复请求:

(1)如何区分客户端和该客户端的请求号?
客户端可以设置一个单调递增的请求号,在每个请求上附加上这个请求号即可。客户端的区分则是个问题,当前的策略是,创建客户端时使用一个足够大范围的随机数标识客户端(int64的随机数,从这个范围随机选几个出现重复的概率几乎为0)

(2)在哪里判断重复请求?
在KVServer处,维护一个各客户端对应的已提交请求的最高请求号的映射(在处理提交的log处维护)。如果到来的请求号比这个号低的话,那么就忽略这个请求。

(3)为什么不是已经调用Start的最高请求号,而是已提交的最高请求号?
这是为了防止客户端对不同服务器的重复请求。比方说(c1,4)请求到服务器s1上,如果该日志提交,但给c1响应丢失时,那么c1可能会重新请求到服务器s2上,s2维护的映射中之前并未见过这个请求,因此会重新调用Start。造成这个问题出现的原因是:如果在处理请求处维护这个状态,那么各服务器之间没有一个统一集中的状态。而在处理提交的log处维护这个状态,是一个统一的状态,其他的服务器已能看到不同客户端请求提交的结果,如果该请求被提交,那么一定会被新Leader看到,因此就能处理这种情况。

  1. 无限等待问题:

client发送请求到当前Leader,但调用Start后,Leader失去了其领导权(使用Start返回值判断是无效的),那么此时如果在applyCh上等待的话,就会造成无限等待的问题。因为其不是Leader,不会将该操作日志复制到其他服务器,因此该日志就不会提交。
也可能是由于发生网络分区错误,旧Leader位于少数服务器区,因此无法提交日志造成的无限等待问题。
解决方法是:设置一个超时机制,如果超过一段时间未给出响应,那么client就尝试切换服务器重新请求。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值