数据模型node
ZK拥有一个命名空间就像一个精简的文件系统,不一样的是它的命名空间中的每一个节点拥有它本身或者它下面子节点相关联的数据。ZK中必须使用绝对路径也就是使用“/”开头。数据库
Znode:bash
ZK目录树中每一个节点对应一个Znode。每一个Znode维护这一个属性,当前版本、数据版本、创建时间和修改时间等,看下图:服务器
ZK就是使用这些属性来实现特殊功能的。当一个客户端要对某个节点进行修改时,必须提供该数据的版本号,当节点数据发生变化是其版本号就会增长。以下图:并发
Znode具备以下特性:分布式
Watches:客户端能够在节点上设置Watches(能够叫作监视器)。当节点状态发生变化时,就会触发监视器对应的操做,当监视器被触发时,ZK服务器会向客户端发送且只发送一个通知ide
数据访问:ZK上存储的数据须要被原子性的操做(要么修改为功要么回到原样),也是就读操做将会读取节点相关全部数据,写操做也会修改节点相关全部数据,,并且每一个节点都有本身的ACL。spa
节点类型:ZK中有几种节点类型,节点类型在节点建立的时候就被肯定且不可改变事务
临时节点(EPHEMERAL):临时建立的,会话结束节点自动被删除,也能够手动删除,临时节点不能拥有子节点ip
临时顺序节点(EPHEMERAL_SEQUENTIAL):具备临时节点特征,可是它会有序列号,分布式锁中会用到该类型节点
持久节点(PERSISTENT):建立后永久存在,除非主动删除。
持久顺序节点(PERSISTENT_SEQUENTIAL):该节点建立后持久存在,相对于持久节点它会在节点名称后面自动增长一个10位数字的序列号,这个计数对于此节点的父节点是惟一,若是这个序列号大于2^32-1就会溢出。
建立顺序节点
create -s /NODE_NAME DATA # -e参数为建立临时节点,若是不带参数则建立持久节点
ZK中的时间和版本号:
ZXID:ZK节点状态改变会致使该节点收到一个zxid格式的时间戳,这个时间戳是全局有序的,每次更新都会产生一个新的。若是zxid1的值小于zxid2,那么说明zxid2发生的改变在zxid1以后。zxid是一个惟一的事务ID,具备递增性,一个znode的创建或者更新都会产生一个新的zxid值,具体时间有3个cZxid(节点建立时间)、mZxid(该节点修改时间,与子节点无关)、pZxid(该节点的子节点的最后一次建立或者修改时间,孙子节点无关)
version:对节点的每次操做都会使节点的版本号增长,有三个版本号dataversion(数据版本号)、cversion(子节点版本号)、aclversion(节点所拥有的ACL版本号)
cZxid |
建立节点时的事务ID |
ctime |
建立节点时的时间 |
mZxid |
最后修改节点时的事务ID |
mtime |
最后修改节点时的时间 |
pZxid |
表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,可是修改子节点的数据内容则不影响该ID |
cversion |
子节点版本号,子节点每次修改版本号加1 |
dataversion |
数据版本号,数据每次修改该版本号加1 |
aclversion |
权限版本号,权限每次修改该版本号加1 |
dataLength |
该节点的数据长度 |
numChildren |
该节点拥有子节点的数量 |
版本号的做用
Zookeeper里面的版本号和咱们理解的版本号不一样,它表示的是对数据节点的内容、子节点列表或者ACL信息的修改次数。节点建立时dataversion、aclversion,cversion都为0,每次修改响应内容其对应的版本号加1。
这个版本号的用途就和分布式场景的一个锁概念有关。好比演出售票中的一个座位,显然每一个场次中的每一个座位都只有一个,不可能卖出2次。若是A下单的时候显示可售,他想买,那么为了保证他能够下单成功,此时别人就不能买。这时候就须要有一种机制来保证同一时刻只能有一我的去修改该座位的库存。这就用到了锁。锁有悲观锁和乐观锁。
悲观锁:它会假定全部不一样事务的处理必定会出现干扰,数据库中最严格的并发控制策略,若是一个事务A正在对数据处理,那么在整个事务过程当中,其余事务都没法对这个数据进行更新操做,直到A事务释放了这个锁。
乐观锁:它假定全部不一样事务的处理不必定会出现干扰,因此在大部分操做里不准加锁,可是既然是并发就有出现干扰的可能,如何解决冲突就是一个问题。在乐观锁中当你在提交更新请求以前,你要先去检查你读取这个数据以后该数据是否发生了变化,若是有那么你这次的提交就要放弃,若是没有就能够提交。
Zookeeper中的版本号就是乐观锁,你修改节点数据以前会读取这个数据并记录该数据版本号,当你须要更新时会携带这个版本号去提交,若是你此时携带的版本号(就是你上次读取出来的)和当前节点的版本号相同则说明该数据没有被修改过,那么你的提交就会成功,若是提交失败说明该数据在你读取以后和提交以前这段时间内被修改了。
这里经过set命令并携带版本号提交更新,版本号相同更新就会成功。
若是你再次更新并使用以前的版本号那么就会失败。