Zookeeper学习(三)----Zookeeper的读写流程

2020-04-20  本文已影响0人  彳亍口巴

ZooKeeper的读写流程分析

在了解了ZooKeeper的基本概念后,我们来详细看下ZooKeeper的读写流程,以及ZooKeeper在并发情况下的读写控制。以求对ZooKeeper有进一步的了解。

读流程分析

读流程如下图所示:

image

因为ZooKeeper集群中所有的server节点都拥有相同的数据,所以读的时候可以在任意一台server节点上,客户端连接到集群中某一节点,读请求,然后直接返回。当然因为ZooKeeper协议的原因(一半以上的server节点都成功写入了数据,这次写请求便算是成功),读数据的时候可能会读到数据不是最新的server节点,所以比较推荐使用watch机制,在数据改变时,及时感应到。

写流程分析

写流程如下图所示:

image

当一个客户端进行写数据请求时,会指定ZooKeeper集群中的一个server节点,如果该节点为Follower,则该节点会把写请求转发给Leader,Leader通过内部的协议进行原子广播,直到一半以上的server节点都成功写入了数据,这次写请求便算是成功,然后Leader便会通知相应Follower节点写请求成功,该节点向client返回写入成功响应。

ZooKeeper并发读写情况分析

我们已经知道ZooKeeper的数据模型是层次型,类似文件系统,不过ZooKeeper的设计目标定位是简单、高可靠、高吞吐、低延迟的内存型存储系统,因此它的value不像文件系统那样适合保存大的值,官方建议保存的value大小要小于1M,key为路径。

image

每个数据节点在ZooKeeper中叫做znode,并且其有一个唯一的路径标识,znode节点可以包含数据和子节点。

ZooKeeper的层次模型是通过ConcurrentHashMap实现的,key为path,value为DataNode,DataNode保存了znode中的value、children、 stat等信息。

ZooKeeper的层次模型是通过ConcurrentHashMap实现的,而ConcurrentHashMap是线程安全的Hash Table,它采用了锁分段技术来减少锁竞争,提高性能的同时又保证了并发安全,其结构如下图所示:

image

ConcurrentHashMap由两部分组成,Segment和HashEntry,锁的粒度是Segment,每个Segment 对象包含整个散列映射表的若干个桶,散列冲突时通过链表来解决。

因为插入键 / 值对操作只是在 Segment 包含的某个桶中完成,所以这里的加锁操作是针对(键的 hash 值对应的)某个具体的 Segment,不需要锁定整个ConcurrentHashMap,所以对于ConcurrentHashMap,可以进行并发的写操作,只要写入的Segment不同。而所有的读线程几乎不会因为写操作的加锁而阻塞(除非读线程刚好读到这个 Segment 中某个 HashEntry 的 value 域的值为 null,此时需要加锁后重新读取该值。这便是锁分段技术,保证并发安全的情况下又提高了性能。

对于ZooKeeper来讲,ZooKeeper的写请求由Leader处理,Leader能够保证并发写入的有序性,即同一时刻,只有一个写操作被批准,然后对该写操作进行全局编号,最后进行原子广播写入,所以ZooKeeper的并发写请求是顺序处理的,而底层又是用了ConcurrentHashMap,理所当然写请求是线程安全的。而对于并发读请求,同理,因为用了ConcurrentHashMap,当然也是线程安全的了。总结来说,ZooKeeper的并发读写是线程安全的。
但是对于ZooKeeper的客户端来讲,如果使用了watch机制,在进行了读请求但是watch znode前这段时间中,如果znode的数据变化了,客户端是无法感知到的,这段时间客户端的数据就有一定的滞后性了,只有当下次数据变化后,客户端才能感知到,所以对于客户端来说,数据是最终一致性。


引用(本文章只供本人学习以及学习的记录,如有侵权,请联系我删除)

ZooKeeper简介及其并发读写情况分析

上一篇 下一篇

猜你喜欢

热点阅读