zxid 就是我们之前提到的事务编号,是一个 8 字节的整型数字,但是 ZK 设计的时候把这一个数字拆成了两部分使用,一鱼两吃!
8 个字节的整数一共有 64 位长度,前 32 位用来记录 epoch,后 32 位就是用来计数。你可能要问了?epoch?是啥?
zxid 初始化是 0,也就是这样
- 00000000000000000000000000000000 00000000000000000000000000000000
每一次写请求都会增加后 32 位,假设现在进行了 10 次写请求(无论该请求有没有真的修改到数据),zxid 就会变成这样
- 00000000000000000000000000000000 00000000000000000000000000001010
当进行一次选举的时候,前 32 位就会增加 1,并且清零后 32 位
- 00000000000000000000000000000001 00000000000000000000000000000000
除了选举以外,当后 32 位彻底用完(变成全 1,也就是 ZK 正常执行了 2^32 – 1 次写请求都没进行过一次选举,牛逼!)也会让前 32 位增加 1,相当于进位
- # 进位前
- 00000000000000000000000000000000 11111111111111111111111111111111
- # 进位后
- 00000000000000000000000000000001 00000000000000000000000000000000
到这里我就可以回答大家前面的问题了,epoch 就是 zxid 前 32 位的这个数字,epoch 本身的翻译是“纪元,时代”的意思,意味着更新换代,而 zxid 的后 32 位数字仅仅是写请求的计数罢了
1.2 myid
在之前的小故事里,我给 ZK 的集群中的各个节点都起了一个好记的名字(神特么好记!)。但是 ZK 官方自己是如何给每一个集群中的节点起名字的呢?用的就是 myid!
ZK 的启动配置 zoo.cfg 中有一项 dataDir 指定了数据存放的路径(默认是 /tmp/zookeeper),在此路径下新建一个文本文件,命名为 myid, 文本内容就是一个数字,这个数字就是当前节点的 myid
- /tmp
- └── zookeeper
- ├── myid
- └── …
然后在 zoo.cfg 是这样配置集群信息
- server.1=zoo1:2888:3888
- server.2=zoo2:2888:3888
- server.3=zoo3:2888:3888
这个 server. 之后的数字就是 myid,这个 myid 在整个集群中,各个节点之间是不能重复的。我忘记之前在哪儿看到的了,说是 myid 只能是 1 到 255 的数字,我一直信以为真,直到这次,我本着严谨的态度去做了实践,一切以事实为主,并且我的实验覆盖了 3.4、3.5、3.6 三大版本(都是三台机器的简单集群),结论是:myid 只要是不等于 -1 就行(-1 是一个固定的值会导致当前节点启动报错),不能大于 Long.MAX_VALUE 或者小于 Long.MIN_VALUE,但是如果在当前的节点中配置了 zookeeper.extendedTypesEnabled=true 那当前节点的最大 myid 是 254(负数不影响,我也不知道这个 254 的用意,但是代码中的确有判断) 是不是奇怪的知识又增加了呢~