Znode简介
在ZooKeeper中,节点也称为znode。由于对于程序员来说,对zk的操作主要是对znode的操作。
ZooKeeper采用了类似文件系统的的数据模型,其节点构成了一个具有层级关系的树状结构。下图展示了zk节点的层级树状结构。
上图中,根节点 / 包含了两个字节点 /module1,/module2,而节点 /module1 又包含了三个字节点 /module1/app1,/module1/app2,/module1/app3。在zk中,节点以绝对路径表示,不存在相对路径,且路径最后不能以 / 结尾(根节点除外)。
Znode类型
根据节点的存活时间,可以对节点划分为持久节点和临时节点。顺序节点为节点的一种特性,这样一来,znode一共有4种类型:持久的、临时的,持久顺序的,临时顺序的。
节点的类型在创建时就被确定下来,并且不能改变。
持久节点
使用命令create可以创建一个持久节点。
持久节点的存活时间不依赖于客户端会话,只有客户端在显式执行删除节点操作时,节点才消失。
临时节点
使用create命令,并加上-e参数,可以创建一个临时节点。
临时节点的存活时间依赖于客户端会话,当会话结束,临时节点将会被自动删除(当然也可以手动删除临时节点)。利用临时节点的这一特性,我们可以使用临时节点来进行集群管理,包括发现服务的上下线等。
顺序节点
使用命令create加上-s参数,可以创建顺序节点。
每次创建顺序节点时,zk都会在路径后面自动添加上10位的数字(计数器),例如 < path >0000000001,< path >0000000002,……这个计数器可以保证在同一个父节点下是唯一的。在zk内部使用了4个字节的有符号整形来表示这个计数器,也就是说当计数器的大小超过2147483647时,将会发生溢出。
Znode数据
在创建节点时,可以指定节点中存储的数据。ZooKeeper保证读和写都是原子操作,且每次读写操作都是对数据的完整读取或完整写入,并不提供对数据进行部分读取或者写入的操作。
以下命令创建一个节点/module1/app2,且其存储的数据为app2。
create /module1/app2 app2
ZooKeeper虽然提供了在节点存储数据的功能,但它并不将自己定位为一个通用的数据库,也就是说,你不应该在节点存储过多的数据。Zk规定节点的数据大小不能超过1M,但实际上我们在znode的数据量应该尽可能小,因为数据过大会导致zk的性能明显下降。如果确实需要存储大量的数据,一般解决方法是在另外的分布式数据库(例如redis)中保存这部分数据,然后在znode中我们只保留这个数据库中保存位置的索引即可。
节点的属性
每个znode都包含了一系列的属性,通过命令get,我们可以获得节点的属性。下面主要介绍版本号,事务ID和时间戳三个属性。
get /module1/app2
app2
cZxid = 0x20000000e
ctime = Thu Jun 30 20:41:55 HKT 2016
mZxid = 0x20000000e
mtime = Thu Jun 30 20:41:55 HKT 2016
pZxid = 0x20000000e
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
版本号
对于每个znode来说,均存在三个版本号:
- dataVersion
数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据)。 - cversion
子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。 - aclVersion
ACL的版本号,关于znode的ACL(Access Control List,访问控制),可以参考 参考资料1 有关ACL的描述。
以数据版本号来说明zk中版本号的作用。每一个znode都有一个数据版本号,它随着每次数据变化而自增。ZooKeeper提供的一些API例如setData和delete根据版本号有条件地执行。多个客户端对同一个znode进行操作时,版本号的使用就会显得尤为重要。例如,假设客户端C1对znode /config写入一些配置信息,如果另一个客户端C2同时更新了这个znode,此时C1的版本号已经过期,C1调用setData一定不会成功。这正是版本机制有效避免了数据更新时出现的先后顺序问题。在这个例子中,C1在写入数据时使用的版本号无法匹配,使得操作失败。下图描述了这个情况。
事务ID
对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id)。通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生。
需要指出的是,zxid对于整个zk都是唯一的,即使操作的是不同的znode。
-
cZxid
Znode创建的事务id。 -
mZxid
Znode被修改的事务id,即每次对znode的修改都会更新mZxid。
在集群模式下,客户端有多个服务器可以连接,当尝试连接到一个不同的服务器时,这个服务器的状态要与最后连接的服务器的状态要保持一致。Zk正是使用zxid来标识这个状态,下图描述了客户端在重连情况下zxid的作用。当客户端因超时与S1断开连接后,客户端开始尝试连接S2,但S2延迟于客户端所识别的状态。然而,S3的状态与客户端所识别的状态一致,所以客户端可以安全连接上S3。
时间戳
包括znode的创建时间和修改时间,创建时间ctime是znode创建时的时间,创建后就不会改变;修改时间mtime在每次更新znode时都会发生变化。