Zookeeper 数据结构详解

Zookeeper

https://zookeeper.apache.org/doc/current/zookeeperOver.html

ZooKeeper is a distributed, open-source coordination service for distributed applications.
It exposes a simple set of primitives that distributed applications can build upon to implement higher level services for synchronization, configuration maintenance, and groups and naming.

It is designed to be easy to program to, and uses a data model styled after the familiar directory tree structure of file systems. 

Zookeeper是高性能,高可用,严格有序的分布式协调服务,提供了统一配置(configuration),命名(naming),同步(synchronization),以及分组服务(group service)。

同时,Zookeeper本身支持复制集群,实例间是两两连接的,维护内存中的数据状态,并持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper服务将可用。
在“读为主”的工作负载中,它特别快。ZooKeeper应用程序可在数千台计算机上运行,并且在读取比写入更为常见的情况下,其性能最佳,比率约为10:1。

Zookeeper 保证的特性

  • 顺序一致性(Sequential Consistency):来自客户端的更新将按照其发送顺序进行执行。
  • 原子性(Atomicity):更新成功或失败。没有中间状态的结果。
  • 统一视图(Single System Image):无论客户端连接到哪个服务器,客户端都将看到相同服务的数据视图。
  • 可靠性(Reliability): 一旦数据更新被执行了,它将从那时起持续到客户端覆盖更新。
  • 及时性(Timeliness):确保系统的客户视图在特定时间范围内是最新的。

Zookeeper的数据结构

ZooKeeper提供的名称空间与标准文件系统的名称空间非常相似。

Zookeeper 数据结构详解_第1张图片

名称是由斜杠(/)分隔的一系列路径元素。ZooKeeper命名空间中的每个节点都由路径进行唯一标识。

ZooKeeper的层次命名空间

与标准文件系统不同,ZooKeeper命名空间中的每个节点都可以具有与其关联的数据以及子节点。就像拥有一个文件系统一样,该文件系统也允许文件成为目录。
每一个节点都可以存储数据,只是需要注意的是存储的容量是有限,一般不能超过 1MiB。

Znode 类型
  • Znode的类型分为三类:

    • 持久节点(persistent node)节点会被持久
    • 临时节点(ephemeral node),客户端断开连接后,ZooKeeper会自动删除临时节点
    • 顺序节点(sequential node),每次创建顺序节点时,ZooKeeper都会在路径后面自动添加上10位的数字,从1开始,最大是2147483647 (2^32-1)
      每个顺序节点都有一个单独的计数器,并且单调递增的,由Zookeeper的leader实例维护。
  • Znode实际上有四种形式,默认是persistent

    • PERSISTENT 持久节点: 如 create /test/a "hello" ,通过 create 参数指定为持久节点
    • PERSISTENT_SEQUENTIAL(持久顺序节点/s0000000001) ,通过 create -s 参数指定为顺序节点
    • EPHEMERAL 临时节点,通过 create -e 参数指定为顺序节点
    • EPHEMERAL_SEQUENTIAL(临时顺序节点/s0000000001) ,通过 create -s -e 参数指定为临时及顺序节点

Zookeeper 数据结构详解_第2张图片

  • 下面是一些实例:

(1) 创建顺序节点

[zk: 127.0.0.1:2281(CONNECTED) 0] create /seq_test/ ""
Created /seq_test
[zk: 127.0.0.1:2281(CONNECTED) 1] create -s /seq_test/s "hello"
Created /seq_test/s0000000001
[zk: 127.0.0.1:2281(CONNECTED) 2] create -s /seq_test/s "hello"
Created /seq_test/s0000000002
[zk: 127.0.0.1:2281(CONNECTED) 3] ls /seq_test
[s0000000001, s0000000002]

(2) 创建临时节点

[zk: 127.0.0.1:2281(CONNECTED) 0] create /ephe_test/ ""
Created /ephe_test
[zk: 127.0.0.1:2281(CONNECTED) 1] create -e /ephe_test/e "hello"
Created /ephe_test/e
[zk: 127.0.0.1:2281(CONNECTED) 2] ls /ephe_test
[e]

断开重新连接

[zk: 127.0.0.1:2281(CONNECTED) 0] ls /ephe_test
[]

(3) 创建临时顺序节点

[zk: 127.0.0.1:2281(CONNECTED) 0] create /ephe_seq_test/ ""
Created /ephe_seq_test
[zk: 127.0.0.1:2281(CONNECTED) 1] create -e -s /ephe_seq_test/s "hello"
Created /ephe_seq_test/s0000000001
[zk: 127.0.0.1:2281(CONNECTED) 2] ls /ephe_seq_test
[s0000000001]
Zxid(ZooKeeper Transaction Id

Zookeeper 数据结构详解_第3张图片

每次的变化都会产生一个集群全局的唯一的事务id, Zxid(ZooKeeper Transaction Id),由Zookeeper的leader实例维护。
这里的变化包括:

  • 任何的客户端连接到Server
  • 任何的客户端断开与Server连接
  • 任何的Znode节点被创建create、修改set、删除deletermr

Zxid是一个64位的数字,高32位表示纪元,从1开始,每次选举出一个新的leader,就会递增1;低32位是当前纪元维护的单调递增的数字,同样从1开始。

Znode 属性

Zookeeper 数据结构详解_第4张图片

  • cZxid :创建的事务标识。
  • ctime:创建的时间戳
  • mZxid:修改的事务标识,每次修改操作(set)后都会更新mZxidmtime
  • mtime:修改的时间戳
  • pZxid:直接子节点最后更新的事务标识,子节点有变化(创建create、修改set、删除deletermr)时,都会更新pZxid
  • cversion :直接子节点的版本号。当子节点有变化(创建create、修改set、删除deletermr)时,cversion 的值就会增加1。
  • dataVersion :节点数据的版本号,每次对节点进行修改操作(set)后,dataVersion的值都会增加1(即使设置的是相同的数据)。
  • aclVersion :节点ACL的版本号,每次节点的ACL进行变化时,aclVersion 的值就会增加1。
  • ephemeralOwner:当前节点是临时节点(ephemeral node )时,这个ephemeralOwner的值是客户端持有的session id。
  • dataLength:节点存储的数据长度,单位为 B (字节)。
  • numChildren:直接子节点的个数。
➜ zkCli.sh -server 127.0.0.1:2281
[zk: 127.0.0.1:2281(CONNECTED) 0] get /

cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1

默认根节点 / 和 /zookeeper 是存在,因此 cZxid 是 0x0。

[zk: 127.0.0.1:2281(CONNECTED) 1] ls /
[zookeeper]
[zk: 127.0.0.1:2281(CONNECTED) 2] create /test "hello"
Created /test
[zk: 127.0.0.1:2281(CONNECTED) 3] ls /
[zookeeper, test]

当创建了一个 /test 节点后,根节点的子节点就多出来了。

[zk: 127.0.0.1:2281(CONNECTED) 4] get /test
hello
cZxid = 0x100000002
ctime = Sat May 23 15:43:10 CST 2020
mZxid = 0x100000002
mtime = Sat May 23 15:43:10 CST 2020
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

为什么cZxid 高32位是 0x1 表明了当前纪元为第一代,低32位是00000002 表明了当前纪元第2笔事务操作创建了 /test 节点。
为什么是2呢?因为我们是通过 zkCli.sh 客户端连接到Server的,这里会消耗一次 Zxid。

[zk: 127.0.0.1:2281(CONNECTED) 5] set /test "hello world"
cZxid = 0x100000002
ctime = Sat May 23 15:43:10 CST 2020
mZxid = 0x100000003
mtime = Sat May 23 15:43:41 CST 2020
pZxid = 0x100000002
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0

修改 /test 节点数据,mZxidmtimedataVersion发生了变化, dataLength 变为了( "hello world"占用了11个字节)。

[zk: 127.0.0.1:2281(CONNECTED) 6] create /test/a ""   
Created /test/a
[zk: 127.0.0.1:2281(CONNECTED) 7] get /test              
hello world
cZxid = 0x100000002
ctime = Sat May 23 15:43:10 CST 2020
mZxid = 0x100000003
mtime = Sat May 23 15:43:41 CST 2020
pZxid = 0x100000004
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1

创建 子节点 /test/a,我们查看 /test 节点属性,发现 pZxidcversion, numChildren 都发生了变化。

[zk: 127.0.0.1:2281(CONNECTED) 8] get /test/a

cZxid = 0x100000004
ctime = Sat May 23 15:44:12 CST 2020
mZxid = 0x100000004
mtime = Sat May 23 15:44:12 CST 2020
pZxid = 0x100000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0

查看 /test/a 节点属性,发现 cZxid 与 父节点的 pZxid 是一致的,证实了 pZxid是直接子节点最后更新的事务标识。

Znode的监视(Watch)

ZooKeeper支持 Watch。客户端可以在znode上设置 Watch。

znode更改时,将触发并删除监视。触发监视后,客户端会收到一个数据包,说明znode已更改。

如果客户端与其中一个ZooKeeper服务器之间的连接断开,则客户端将收到本地通知。

3.6.0中的新增功能:

客户端还可以在znode上设置永久性的递归监视,这些监视在触发时不会删除,并且会以递归方式触发注册znode以及所有子znode的更改。

支持 Watch的 客户端命令:

  • stat path [watch]

  • ls path [watch]

  • ls2 path [watch]

  • get path [watch]

[zk: 127.0.0.1:2281(CONNECTED) 3] get /test/d watch

cZxid = 0x100000013
ctime = Sat May 23 16:47:41 CST 2020
mZxid = 0x100000013
mtime = Sat May 23 16:47:41 CST 2020
pZxid = 0x100000013
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 0

此时使用另外一个客户端去更改 /test/d 节点的数据,我们就可以看到原来的客户端自动收到了一个WATCHER 通知。

[zk: 127.0.0.1:2281(CONNECTED) 4] 
WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/test/d

@SvenAugustus(https://www.flysium.xyz/)
更多请关注微信公众号【编程不离宗】,专注于分享服务器开发与编程相关的技术干货:
Zookeeper 数据结构详解_第5张图片

你可能感兴趣的:(Zookeeper 数据结构详解)