Zookeeper 数据结构详解

Zookeeper

https://zookeeper.apache.org/...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)。node

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

Zookeeper 保证的特性

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

Zookeeper的数据结构

ZooKeeper提供的名称空间与标准文件系统的名称空间很是类似。apache

file

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

ZooKeeper的层次命名空间服务器

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

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

    • 持久节点(persistent node)节点会被持久
    • 临时节点(ephemeral node),客户端断开链接后,ZooKeeper会自动删除临时节点
    • 顺序节点(sequential node),每次建立顺序节点时,ZooKeeper都会在路径后面自动添加上10位的数字,从1开始,最大是2147483647 (2^32-1)

每一个顺序节点都有一个单独的计数器,而且单调递增的,由Zookeeper的leader实例维护。数据结构

  • Znode实际上有四种形式,默认是persistentapp

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

file

  • 下面是一些实例:

(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

file

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

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

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

Znode 属性

file

  • 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/)
更多请关注微信公众号【编程不离宗】,专一于分享服务器开发与编程相关的技术干货:
相关文章
相关标签/搜索