zookeeper专题:zookeeper的节点类型,数据持久化机制


        Zookeeper 是一个分布式协调框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。主要有如下两个核心的概念:文件系统数据结构 + 监听通知机制

        ZooKeeper使用树形结构管理数据。以“/”作为树形结构的根节点。树形结构中的每一个节点都称为“znode”(目录节点)。文件系统中的目录可以存放/删除其他目录和文件,znode中可以存放/删除其他znode,也可以对应一个具体的值。znode和它对应的值之间是键值对的关系。

在这里插入图片描述

1. zookeeper的安装

①:下载并解压

wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
tar -zxvf apache-zookeeper-3.5.8-bin.tar.gz
cd  apache-zookeeper-3.5.8-bin

②:重命名配置文件 zoo_sample.cfg

cp zoo_sample.cfg  zoo.cfg 

③:启动zookeeper服务端

# 可以通过 bin/zkServer.sh  来查看都支持哪些参数 
bin/zkServer.sh start conf/zoo.cfg

④:客户端连接zookeeper服务端

//如果是单机,可省略 -server ip:port 
bin/zkCli.sh -server ip:port 


2. zookeeper的节点类型

  1. 持久化目录节点
    客户端与zookeeper断开连接后,该节点依旧存在,只要不手动删除该节点,他将永远存在
  2. 持久化顺序目录节点
    客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
  3. 临时目录节点
    客户端与zookeeper断开连接后,该节点被删除
  4. 临时顺序目录节点
    客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
  5. Container 节点
    zookeeper 3.5.3 版本新增的,容器节点主要用来容纳字节点,如果没有给其创建子节点,容器节点表现和持久化节点一样,如果给容器节点创建了子节点,后续又把子节点清空,容器节点也会被zookeeper删除。定时任务默认60s 检查一次
  6. TTL 节点
    默认禁用,只能通过系统配置 zookeeper.extendedTypesEnabled=true 开启,不稳定


3. zookeeper命令解析

①: 创建zookeeper 节点 (create)

create [-s] [-e] [-c] [-t ttl] path [data] [acl]

中括号为可选项,没有则默认创建持久化节点

  • -s: 顺序节点
  • -e: 临时节点
  • -c: 容器节点
  • -t: 可以给节点添加过期时间,默认禁用,需要通过系统参数启用
//系统参数如下:
-Dzookeeper.extendedTypesEnabled=true,  znode.container.checkIntervalMs : (Java system property only) New in 3.5.1: The time interval in milliseconds for each check of candidate container and ttl nodes. Default is "60000".
//创建持久化节点
create  /node data   	//创建带值目录节点 
create  /node  			//只创建目录节点

//创建持久化顺序节点
create -s /node data   	//创建带值顺序目录节点 
create -s /node  		//只创建顺序目录节点

//创建临时目录节点
create -e /ephemeral data

//创建临时顺序目录节点
create -e -s /ephemeral data

//创建容器节点
create -c /container

②:查看节点数据 (get)

get  /test-node

③:修改节点数据 (set)

set /test-node some-data-changed

④:查看节点目录(ls)

ls /test-node		//查看test-node下的节点
ls -R /test-node	//查看test-node下的所有节点,包括子节点下的节点

⑤:查看节点状态 (stat)

stat /test-node		    //只查看节点状态
get -s /test-node		//查看节点状态信息同时查看数据

在这里插入图片描述

节点状态详情如下:

  • cZxid:创建znode的事务ID(Zxid的值)
  • mZxid:最后修改znode的事务ID。
  • pZxid:最后添加或删除子节点的事务ID(子节点列表发生变化才会发生改变)。
  • ctime:znode创建时间。
  • mtime:znode最近修改时间。
  • dataVersion:znode的当前数据版本。
  • cversion:znode的子节点结果集版本(一个节点的子节点增加、删除都会影响这个版本)。
  • aclVersion:表示对此znode的acl版本。
  • ephemeralOwner:znode是临时znode时,表示znode所有者的 session ID。 如果znode不是临时znode,则该字段设置为零。
  • dataLength:znode数据字段的长度。
  • numChildren:znode的子znode的数量。

通过版本号判断节点是否被修改过

/test-node 当前的数据版本是 1 , 这时客户端 用 set 命令修改数据的时候可以把版本号带上
在这里插入图片描述
如果在执行上面 set 命令前, 有人修改了数据,zookeeper 会递增版本号, 这个时候,如果再用以前的版本号去修改,将会导致修改失败,报如下错误
在这里插入图片描述


4.zookeeper的监听通知机制

  • 针对节点的监听 :一定事件触发,对应的注册立刻被移除,所以事件监听是一次性的
get  -w  /path   // 注册监听的同时获取数据
stat -w /path   // 对节点进行监听,且获取元数据信息

在这里插入图片描述

  • 针对目录的监听:如下图,目录的变化,会触发事件,且一旦触发,对应的监听也会被移除,后续对节点的创建没有触发监听事件
ls -w /path

在这里插入图片描述

  • 针对递归子目录的监听
ls -R -w /path // -R 区分大小写,一定用大写 

如下对/test 节点进行递归监听,但是每个目录下的目录监听也是一次性的。

如第一次在/test 目录下创建节点时,触发监听事件,第二次则没有,同样,因为时递归的目录监听,所以在/test/sub0下创建节点/test/sub0/subsub0时,触发事件,但是再次创建/test/sub0/subsub1节点时,没有触发事件。
在这里插入图片描述
Zookeeper事件类型

  • None: 连接建立事件
  • NodeCreated: 节点创建
  • NodeDeleted: 节点删除
  • NodeDataChanged:节点数据变化
  • NodeChildrenChanged:子节点列表变化
  • DataWatchRemoved:节点监听被移除
  • ChildWatchRemoved:子节点监听被移除

zookeeper的ACL授权机制,点击查看


5. zookeeper的常规配置

打开zookeeper的配置文件zoo.cfg
在这里插入图片描述

配置说明:

  1. tickTime=2000:zookeeper时间配置中的基本单位为2秒钟 (毫秒)

  2. initLimit=10:允许follower初始化连接到leader最大时长为20秒,它表示tickTime时间倍数 即:initLimit*tickTime = 20秒

  3. syncLimit=5: 允许follower与leader数据同步最大时长,它表示tickTime时间倍数

  4. dataDir=/tmp/zookeeper zookeper: 数据存储目录

  5. clientPort=2181 :对客户端提供的端口号

  6. maxClientCnxns=60: 单个客户端与zookeeper最大并发连接数

  7. autopurge.snapRetainCount=3: 保存的数据快照数量,之外的将会被清除

  8. autopurge.purgeInterval=1: 自动触发清除任务时间间隔,小时为单位。默认为0,表示不自动清除。


6. zookeeper的数据持久化

        Zookeeper数据的组织形式为一个类似文件系统的数据结构,而这些数据都是存储在内存中的,所以我们可以认为,Zookeeper是一个基于内存的小型数据库

内存中的数据:

public class DataTree {
    private final ConcurrentHashMap<String, DataNode> nodes =
        new ConcurrentHashMap<String, DataNode>();
        
    private final WatchManager dataWatches = new WatchManager();
    private final WatchManager childWatches = new WatchManager();
    

DataNodeZookeeper存储节点数据的最小单位

public class DataNode implements Record {
    byte data[];
    Long acl;
    public StatPersisted stat;
    private Set<String> children = null;

然而如果数据只存储在内存中的话,那么在zookeeper宕机或者断电的情况下,数据将会丢失,所以zookeeper也制定了一些数据持久化方式:

  • 事务日志log
  • 数据快照snapshot

        

6.1 事务日志log文件

        针对每一次客户端的事务操作(写),Zookeeper都会将他们记录到事务日志中,当然,Zookeeper也会将数据变更应用到内存数据库中。我们可以在zookeeper的主配置文件zoo.cfg 中配置内存中的数据持久化目录,也就是事务日志的存储路径 dataLogDir. 如果没有配置dataLogDir(非必填), 事务日志将存储到dataDir (必填项)目录。

dataDir=/tmp/zookeeper/snapshot //数据快照目录
dataLogDir=/tmp/zookeeper/log  //事务日志目录

ZK 启动后会基于上面两个路径继续创建 version-2 子路径,之后的文件都会在该子路径下创建

/tmp
└── zookeeper

	# 数据快照
    ├── snapshot
            └── version-2
                    └── ...
	# 事务日志
    └── log
            └── version-2
                    └── ...

事务日志 log 文件名的格式是这样 log.{zxid} ,其中zxid 对应当时创建该文件时的最大 zxid,假设现在创建时 zxid 为 0,那目录结构会是这样:

/tmp
└── zookeeper
    └── log
            └── version-2
                    └── log.0

        Zookeeper进行事务日志文件操作的时候会频繁进行磁盘IO操作,事务日志的不断追加写操作会触发底层磁盘IO为文件开辟新的磁盘块,即磁盘Seek

        因此,为了提升磁盘IO的效率,Zookeeper在创建事务日志文件的时候就进行文件空间的预分配- 即在创建文件的时候,就向操作系统申请一块大一点的磁盘块。这个预分配的磁盘大小可以通过系统参数 zookeeper.preAllocSize 进行配置。

        zookeeper.txnLogSizeLimitInKb 这个环境变量配置,默认是 -1,这个配置限制了 log 单个文件大小(单位是 KB),如果用户手动配置了该参数,就会检查当前 log 文件大小是否超过了该参数大小,如果超过了就会进行 rollLog,相当于下一次的写请求会创建一个新的 log 文件。除此之外, 每次快照的时候会强制执行一次 rollLog

        
        

6.2 数据快照snapshot

        数据快照用于记录Zookeeper服务器上某一时刻的全量数据,并将其写入到指定的磁盘文件中。数据存储在dataDir 指定的目录中,snapshot 文件名的格式是 snapshot.{zxid},那么 zxid 对应当是创建该文件时的最大 zxid,假设现在创建是最大 zxid0,那目录结构会是这样:

/tmp
└── zookeeper
    └── snapshot
            └── version-2
                    └── snapshot.0

首先有两个配置

  • zookeeper.snapCount (默认 100000
  • zookeeper.snapSizeLimitInKb(默认 4194304 单位是KB,相当于 4 GB)

在启动后会基于这两个配置分别生成两个随机数,假设上述的配置是按照默认的设置,这两个随机数的范围就是:

randRoll = [0, 50000]
randSize = [0, 4194304 * 1024 / 2]

        可以简单的认为就是上述两个配置的一半之内的随机数,至于 randSize 为什么要乘以 1024 因为最终文件计算大小是以 byte 作为单位的。

而是否快照就是取决于上面两个随机数,有两个条件:

  • 当前写请求的数量达到了 zookeeper.snapCount 的一半并加上 randRoll 的数量
  • 当前 log 文件的大小达到了 zookeeper.snapSizeLimitInKb 的一半并加上 randSize 的大小

上述条件满足任意一个条件后就会重置上面的两个随机数,并开始生成快照,生成快照这个过程是启动一个子线程去创建的。

        
        

6.3 zookeeper数据恢复过程

上述两种持久化文件另一个重要用途就是帮助 ZK 恢复服务端的信息。

在 ZK 启动的时候就会尝试读取 dataDirdataLogDir 这两个目录下的文件,假设在这两个路径下的文件是:

/tmp
└── zookeeper
    ├── snapshot
            └── version-2
                    └── snapshot.5
                    └── snapshot.37
                    └── snapshot.100
    └── log
            └── version-2
                    └── log.0
                    └── log.6
                    └── log.38
                    └── log.90
                    └── log.108

  • log 负责记录每一个写请求
  • snapshot 负责对当前整个内存数据进行快照
  • 恢复数据的时候,会先读取最新的 snapshot 文件
  • 然后在根据 snapshot 最大的 zxid 去搜索符合条件的 log 文件,再通过逐条读取写请求来恢复剩余的数据
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值