在分布式系统中,有不少台node组成一个cluster,对于client 的一个写操做请求而言,在什么样的状况下,cluster告诉client这次写操做请求是成功的呢?html
假设有一个三节点的cluster,一个primary node,两个replica node以下图所示:
node
方案1,client有着良好的响应性,由于只须要primary持久化了,就给client响应;并发性更高,由于对于cluster而言,只须要primary持久化了,cluster 就算“暂时”成功处理了一个请求。缓存
可是对于client而言,读操做就不方便了。由于,client要想读到最新的数据,读primary node是没问题的,可是读 replic node就有可能读不到最新的数据,由于:primary 还将来得及将最新的数据 复制到 replica 时,client向replica 发起了读数据请求,以下图所示:
数据结构
这样,client就读不到它刚才明明已经写入成功的数据了。并发
方案2,client 读取任何一台node都能读取到最新写入的数据,可是client的响应性不佳。app
上面的讨论是从client角度、cluster角度来讨论的,还能够从数据的角度来讨论,就是数据是否被丢失。异步
一个cluster,通常是要接收大量的client的写请求的,若是来一个写请求,cluster就执行一次磁盘写操做(将数据持久化)那么性能应该是不佳的。所以,能够先将若干个写请求的数据都放到一个buffer中,而后等一段时间再批量将这些数据刷新到磁盘(sync)。elasticsearch
当引入了buffer,将写操做数据批量写磁盘 这种机制时,何时给client返回写操做成功的响应呢?是等若干个写操做数据批量同步到磁盘后,再给client返回写请求成功的响应、仍是数据只是存储到所谓的buffer里面,就给client返回写操做成功的响应?而buffer的引入,又对 primary node 将数据 复制到 replica node 产生何种影响?分布式
另外,就算真的不引入所谓的buffer,若是client的写操做很复杂、代价很大,难不成真的是要等数据持久化到磁盘才能给client响应成功吗?这种方式是否是有点与“并发控制 锁操做中的”悲观锁……高并发
这个真的就很差说了,可能不一样的产品有不一样的实现细节吧。好比ElasticSearch、Mongodb
而当引入了buffer以后,因为将client的写操做数据都批量缓存起来了,那万一机器挂了,那这些缓存的数据就全丢失了,而若是client发一个请求,就同步一次磁盘,那处理性能又受到了影响,这彷佛是一个两难的问题。
所以,为了解决这个问题,引入了一个叫“replication log”的概念。relication log有多种不一样的实现方式,好比 write ahead log(WAL),而在ElasticSearch里面也有一个相似的东西,叫作transaction log(不知道我理解的对不对)
replication log的思想就是:针对client的写操做,生成一条日志,该日志详细记录了写操做对数据进行何种操做。通常日志只支持append操做的,通常地,相比于写数据操做、写日志要轻量级得多。另外日志还有个好处是:若是node在写操做过程当中失败了,好比数据写到一半失败了,那及有可能形成数据的不一致性,那它还能够再读取日志,从日志中恢复出来。
下面来举个具体的例子,我的理解。
client 向 elasticsearch cluster 发起 index 操做。文档要分词,到Lucene底层要构造segment,生成 倒排索引(posting list)这种数据结构。这是一种“费时费力、代价很大的操做”,所以,不可能 针对 一篇文档一个index请求,就flush 一次segment。所以能够每隔一段时间、批量flush segment。而若是批量flush 的话,若是机器宕机了,那就会丢失不少数据。所以,es 引入了translog:
all the index/delete/update operations are written to the translog and the translog is fsynced after every index/delete/update operations to make sure the changes are persistent. the client receives acknowledgement for writes after the translog is fsynced on both primary and replic shards
写translog 应该要比 lucene segment flush 操做要轻量级得多。另外,primary shards 只须要将translog持久化、并同步给replica 后,就能够给client返回 写操做成功的响应了,这样可支持写操做的高并发。
本文从三个角度:client、cluster、数据是否丢失 阐述了分布式中的读写一致性及数据可靠性。
我的理解,可能有错。
原文:http://www.javashuo.com/article/p-mwqdybdg-my.html