introduction
为什么需要分布式计算?
并行,容错,物理因素(地理位置),安全
分布式计算面临的挑战?
并发性,部分错误,性能问题
-
基础设施
存储,通信,计算————目标是抽象成从外部看上去就是一个中心化的系统一样
-
实现
RPC,线程,并发(锁)
-
性能
理想是得到可扩展性的提升(XX倍的计算资源可以带XX倍的性能),但是这个非常难
-
容错
机器出现故障在一台机器上是罕见的,但是在分布式计算机集群中是很常见的一个问题。
可用性,可恢复性(利用非易失性存储,复制实现)
-
一致性
KV数据库(放入,取出),数据库取出的数据一定是最新的(强一致性),如果不能一定保证的是弱一致性,强一致性相比弱一致性的开销更大,因为可能需要花费更多的通信开销
MapReduce
Map:对输入进行分类处理
Reduce:对分类的结果进行归类
多线程
为什么需要多线程:
I/O并发,并行化,易用性(周期性的检查)
多线程面临的挑战:
线程竞争共享资源(互斥),线程协作(同步),死锁
GFS
-
大型存储为什么这么难?
性能问题 ->分片存储
机器出错->容错->复制->不一致
一致性->降低性能
性能和一致性难以达到平衡
-
强一致性
同一时刻只处理同一条请求
-
GFS设计的目标
容量大,速度快,全局化,自动恢复,单数据中心,内部使用(不会面向用户),针对大型文件的顺序访问(大的吞吐量)
-
master存储的数据
filename到chunk handle(NV:备份至磁盘)的映射(从文件名到chunk标识的映射)
handle到chunk server列表(V:不用备份至磁盘,因为master启动后会轮询所有的chunk服务器)的映射(chunk标识到具体存储位置的映射,因为一个chunk会存在多个server,所以返回的是列表)
每个chunk的版本号(NV,便于master找到哪个是最新的chunk,版本号只在变更primary时修改),主要chunk的标号(V),担任primary的轮换时间(V)
日志,检查点都需要写入磁盘进行容错。出错以后重启master,master只需要从最新的检查点开始重演日志然后恢复至出错前的最新状态。
-
读数据
1.client发送文件名和文件偏移量给master
2.master发送chunk handle和chunk server列表给client,缓存这部分信息便于以后访问
3.client向chunk server发送chunk handle和偏移量读取数据
-
写数据
-
master没有primary chunk
找到最新的chunk副本(通过master版本号和chunk版本号确认)
挑选其中一个最新的chunk server作为primary chunk,并且设置任期,只有这个期限该节点是primary
master递增版本号,通知primary和其余备份的secondary节点保存最新的版本号至各自的磁盘
master将最新版本号写入磁盘进行备份
client收到所有相关文件的chunk server地址后,发送需要修改的数据给primary和secondary节点,client可以通过发送给就近的chunk server,然后这些收到消息的chunk server再通知其它server
primary首先按照client发来的请求进行写入操作,然后通知其它secondary节点按照primary同样的顺序执行
secondary节点成功写入数据后,会向primary节点回复yes,如果primary没有收到或者收到了错误的回复,则会发送没有成功的消息告诉client,client可以重新执行请求。如果收到成功的回复,则说明所有备份节点都成功写入,如果失败,则有部分(可能也没有)节点成功写入。
即使master不能联系上primary,也需要等待任期结束,因为可能是由于网络原因导致没有联系上,如果直接任期一个新的primary,可能会导致脑裂
“脑裂” 如果同时出现两个primary导致的出现不同的副本的情况就是脑裂,可能由于网络分区造成的。
创建新文件的过程与写入类似,master发现没有写入文件的chunk handle后会随机找到新的primary和secondary节点,并且生成全新的版本号
-
主从备份与复制
-
复制实现容错,这个错误智能解决fail-stop问题
fail-stop:由于单机出现物理故障导致的错误,不包括软件bug或硬件设计的缺陷
-
状态转移(存储)
primary将所有最新内容发送给backup进行备份(每次只发送修改的部分可提高效率)
-
复制状态机(外部输入)
primary将来自外部的输入或事件发送个backup,由于没有没有外界输入时primary和backup的操作一致,所以在保证有同样输入后backup和primary仍可以保证一致
-
如何实现replicated state machine(复制状态机)
状态是什么:主从同步,切换主机,匿名切换,新的备份
一般都是只复制应用程序级别的数据(如GFS只复制应用程序可见的相同的chunk),这种方法虽然高效,但是必须要和应用程序绑定,内置在应用程序中实现,机器不知道复制的内容的具体含义
还有一种就是基于系统底层进行的将数据进行全部复制(如VMware的容错机制),这种方法效率较低,但好处就是可以不用考虑在其上面的应用程序,具有很强的适配性
primary和backup之间会通过日志(log entry)进行同步,这个是周期持续的,如果backup有段时间没有从log channel中收到primary发送的日志文件,就会根据容错机制,backup就可以开始自由执行而不需要再等待primary的输入,成为新的primary
如果有些指令(例如随机数)在不同电脑上执行结果不一样,包含这种操作的网络数据包中的各种操作交给primary和backup后,只有primary会执行所有的操作,而backup不会作其中的指令,只会等待从log channel中获取最终结果。其余的可以一样的指令则是primary和back up共同执行
-
非确定性事件
外界输入(输入包和中断信号):可能不是同一时刻到达,到达时primary和backup的状态不一样
特殊指令:指令在不同的电脑上可能结果不一样,如生成随机数,获取当前时间
(多核处理):服务指令在多核上交错执行,执行顺序是不可预测的,导致结果可能会不一致,这个被VMware FT的论文屏蔽了
上述问题通过backup利用log entry去同步执行结果,而自己不去执行的方式来保存同步
这些问题需要通过日志事件(log entry)进行处理,log entry中可能包含有,指令的序号,指令类型,数据。
backup中有一个缓存区存有和primary同步的操作,backup只有当缓存有操作才会执行指令,所以backup肯定会比primary慢一两个事件
-
数据到达
数据到达后,由于如果直接放入数据会导致primary(backup)不确定最终在哪个时间点看到数据包,所以数据会先存在VMware的私有内存中,然后先把primary(back up)挂起,然后在primary(backup)没有看到的情况下将数据复制到内存中,然后VMware在发送模拟NIC发送中断给primary(backup)
数据包大部分都是传递的数据,只有极少一部分是非确定性的指令
-
输出规则
只有当所有的backup都收到了log record的后primary才能够向外发送输出,所以如果client能看到的最终输出一定是其他的backup也都拿到这个结果了
这种方法会造成primary的停顿,因此会损失部分性能,为减少这部分损失,可以只针对高级操作(如写)进行停顿,而一些普通的操作(如读),就不需要按照这种方式同步
如果由于网络原因,primary和backup互相无法通信,即双方都认为对方宕机了,那么如果直接上线新的主机代替主机就会导致两种新的运行方式,即“脑裂”。为了避免这种情况,需要引入外部帮助,通过设置test-and-set,primary和backup同时向其发起请求,只有一个能够单独申请成功,然后两个只有一个能够最好申请新的主机。
Go,Threads and Raft
-
Go
使用闭包时注意调用外部参数是否需要为当前的值,如果需要则要在参数传入改值,因为可能闭包调用外部参数时这个值已经发生了变化。
互斥变量mutex主要是用于保护互斥变量,或者有些中间会变化但总体不变的值,总之就是为了让一部分的代码能够具有原子性。
条件变量c