在上一篇的server代码骨架中已提到,当处理proposal时,是由SyncRequestProcessor来处理的,下面就来对这其中的操作做更详细的分析。
日志和磁盘使用情况
server是使用事务日志来持久化事务的。在accept一个proposal请求之前,server(follower或者leader)把这个proposal以事务的形式持久化到事务日志,按照顺序进行append。server每隔一段时间,时不时的roll over,就是关掉当前的日志文件,并创建一些新的文件继续append。
因为写事务日志是写请求的一个关键步骤,所以ZK需要保证这个操作的高效。在硬件上append一个文件是很高效的,但是ZK玩了一些花招可以更快的操作,这就是group commit和padding。group commit在一次写磁盘操作中包括了多个append,这样可以只用一次磁盘寻道的代价持久化多个事务。
关于持久化事务日志还有一个重要的事情。现代的操作系统通常会缓存脏页并异步的写入磁盘。
然而我们需要在继续处理之前确保事务已被持久化,所以我们需要把事务刷新到磁盘上。刷新只是意味我们告诉操作系统把脏页写到磁盘上,当操作系统完成时则返回。因为我们在SyncRequestProcessor进行持久化,它的一个职责就是刷新。当需要刷新一个事务到磁盘时,我们实际上做了个优化,把所有进入队列的事务来进行group commit。如果只有一个事务进入队列,它仍然会执行刷新,并不会等待更多事务进入队列,这样可以优化延迟时间。可以参考SyncRequestProcessor.run()来了解细节。
磁盘写缓存
只有强制事务日志刷到磁盘后,server才能对proposal进行ack操作。说得更明白一点,server会调用ZKDatabase的commit方法,这最终会调用FileChannel.force方法。这样,server会在ack之前保证事务已被持久化到磁盘。关于此事还有一点要注意,现代磁盘有一个写缓存,可以保存要写到磁盘的数据。如果启用了写缓存,强行刷新不能保证返回的时候数据已落到磁盘,数据会落到写缓存中。为了保证在FileChannel.force()返回后数据落到磁盘,要禁用写磁盘缓存。操作系统有许多方式可以禁用。
Padding就是预分配(preallocate)的一个文件的磁盘块(disk block)。这样做是为了更新一个文件对应的文件系统块分配相关的元数据时不会显著的影响这个文件顺序写操作。如果事务以很快的速度append的时候,若这个文件的块没有预分配的话,每当写完一个块的时候,文件系统需要分配一个新的块。这会带来至少两次额外的磁盘寻道:一次是为了更新元数据,另一次是文件的末尾。
为了避免受到系统其他写操作的干扰,强烈建议在一个独立的设备写事务日志,使用另一个设备存放快照文件和系统其他文件。
快照
快照是ZK的data tree的一份拷贝。每一个server每隔一段时间会序列化data tree的所有数据并写入一个文件。server进行快照时不需要进行协调,也不用暂停处理请求。因为server在进行快照时还会处理请求,所以当快照完成时,data tree 可能会变化。我们称这样的快照是模糊的(fuzzy),因为它们不需要反映出(reflect)在任意给点的时间点data tree确切的状态。
举一个例子来说明一下。一颗data tree只有2个znode:/z和/z'。一开始,两个znode的数据都是1。现在有以下操作步骤:
1. 开始一个快照
2. 序列化并写入/z=1到快照
3. 设置/z的数据为2(事务T)
4. 设置/z'的数据为2(事务T')
5. 序列化并写入/z'=2到快照
这个快照包含了/z=1和/z'=2。然而在任意的时间点上,data tree的数据都不会跟快照一样。这不是问题,因为server会重放(replay)事务。每一个快照文件会被打上一个标记(tag),这个标记是快照开始的时候最后一个被commit的事务的时间戳,称之为TS。如果server最后加载快照,它会重放在TS之后的所有事务日志中的事务。在这个例子中,它们就是T和T'。在快照的基础上重放T和T'后,server包含/z=2和/z'=2,这是一个合理的状态。
还有一个重要的问题,就是说重放事务是否会带来问题,因为在开始快照之后这些事务已经被执行过一次了。其实不会有问题,因为事务是幂等的(idempotent),所以只要我们按照相同的顺序执行相同的事务,就会得到相同的结果,就算它们在生成快照前被执行过。
为了理解这个过程,假设执行一个事务,有一些要被重新执行的操作。有两种操作,一个操作设置某个znode的数据为一个特定的值,这个值跟其他东西不相关。另一种操作,无条件(unconditionly)的设置/z'的值(setData请求中的version number为-1),重新执行操作均成功,但最后我们得到了错误的version number,因为我们增加了它2次。下面这种方式会导致问题。假设有如下3个操作并成功执行:
setData /z', 2, -1
setData /z', 3, 2
setData /a, 0, -1
第一个setData操作跟我们描述的一样,但是我们加上了2个setData操作来展示在重放中第二个操作没有执行,因为一个不正确的version number。假设这3个操作在提交时被正确执行。又假设server加载最新的快照,快照已包含第一个setData操作。
server仍然会重放第一个setData操作,因为快照被一个更早的zxid标记。因为它重新执行了第一个setData操作。version并不匹配第二个setData操作期望的version,那么这个操作无法完成。第三个setData操作可以正常完成,因为它也是无条件的。
在加载完快照并重放日志后,server的状态是不正确的,因为它没有包括第二个setData请求。这个操作违反了持久性和正确性,请求的序列应该是没有缺口的(no gap)。
通过让leader来把事务转换成状态的delta来解决这个问题。当leader为一个请求产生事务时,作为事务生成的一部分,包括了一些在这个请求中znode或它的数据的变化的值(delta值),并指定一个特定的version number。最后重新执行一个事务就不会导致不一致的version number。
相关推荐
数据目录(dataDir)用于存储Zookeeper的状态信息,包括事务日志和快照。而端口号(clientPort)则是Zookeeper服务器监听客户端连接的端口。 启动Zookeeper服务,你可以使用`bin/zkServer.sh`脚本。在多节点集群...
7. **zkData** 目录:也需用户手动创建,用于存放Zookeeper的事务日志和快照,这是Zookeeper持久化数据的地方。 在实际使用中,用户需要根据自己的需求配置`zoo.cfg`,设置Zookeeper集群的配置,包括服务器ID、集群...
3. **日志与快照**:Zookeeper定期生成数据的快照和事务日志,以支持故障恢复和数据一致性。 总结,Zookeeper 3.4.6作为一款强大的分布式协调服务,对于搭建Dubbo这样的分布式应用至关重要。了解其安装、配置、集群...
- `src/main/java/org/apache/zookeeper/server/persistence`:数据持久化的实现,如快照和事务日志。 深入研究这些模块,可以让你对ZooKeeper的工作机制有更全面的理解,从而更好地利用它在大数据、分布式计算等...
日志种类 Zookeeper在运行期间主要输出三类文件:快照(snapshot)、事务日志(transaction log...当事务日志文件变得较大时,Zookeeper会将当前所有znode节点的最新状态生成快照snapshot存储到dataDir中,同时生成新的
Zookeeper通过内存数据树和磁盘中的快照、事务日志来确保数据的持久化。在集群启动时,系统会执行恢复流程,包括数据恢复、选举流程和数据同步流程。 4. 数据持久化 Zookeeper采用内存数据树和事务日志的双写模式来...
每个ZNode的变更都会形成一个事务日志(txnlog)和快照(snapshot)。3.4.8版本优化了数据恢复和快照机制,提高了系统的可用性和稳定性。 三、Zookeeper客户端连接与会话 客户端通过TCP连接与Zookeeper服务器建立...
1. `dataDir`: 指定Zookeeper保存数据的目录,例如"data",这个目录会在Zookeeper启动时创建,用于存储快照和事务日志。 2. `clientPort`: 设置Zookeeper服务器监听客户端连接的端口,默认为2181。 四、初始化数据...
本文将深入探讨Zookeeper在压力测试中的表现,包括磁盘IO瓶颈、CPU利用率、事务日志与快照策略以及连接管理等问题。 首先,我们关注Zookeeper在高并发环境下的性能表现。在800个连接并发创建80万个数据点时,每个...
- `dataDir`与`dataLogDir`:分别用于存储快照文件和事务日志。 - `clientPort`:客户端连接服务端口。 - `server.N`:定义集群中的服务器列表,其中N是服务器ID。 4. **分发配置**: - 使用`scp`命令将新版本...
- `dataDir`:用于存储Zookeeper的数据文件,包括事务日志和快照。 - `clientPort`:客户端连接Zookeeper服务器的端口,默认为2181。 - `server.x`:定义集群中的节点,x为1、2、3等,对应集群中的每个服务器。...
2. **事务日志和快照**:为了保证数据的一致性,ZooKeeper采用了事务日志和快照两种存储机制。事务日志记录所有更新操作,而快照则是某个时间点的数据备份。通过这两种方式,ZooKeeper能够快速恢复到最新状态。 3. ...
5. **data目录**:用于存储Zookeeper的数据,包括每个节点的事务日志(`zookeeper.log`)和快照文件(`myid`文件指定服务器ID)。 安装和使用Zookeeper通常涉及以下步骤: 1. **配置zoo.cfg**:修改`conf/zoo.cfg`...
你需要编辑这个文件,设置数据目录(dataDir),这是Zookeeper存储快照和事务日志的地方。例如,你可以将dataDir设置为C:\zookeeper\data。同时,你还需要创建一个名为myid的文件,里面写入此Zookeeper服务器的唯一...
- `dataLogDir`:存储事务日志的目录。 - `clientPort`:客户端连接 Zookeeper 服务的端口,每个实例应设置不同的端口。 - `server.x`:定义集群中的服务器节点,例如 `server.1=localhost:2187:2887`,其中 x ...
- dataDir:指定数据存储的目录,保存数据库快照和事务日志文件。 - clientPort:指定客户端连接到Zookeeper服务器的端口号。 - initLimit:指定Leader服务器和Follower服务器之间初始化连接时最长能忍受多少个心跳...
- `dataDir`:Zookeeper存储快照的目录,应指向`/zookeeper/zkdata`。 - `dataLogDir`:事务日志的存储目录,应指向`/zookeeper/zkdatalog`。 - `clientPort`:客户端连接Zookeeper的端口,这里设为2181。 - `server...
1. **数据存储路径**:在`zoo.cfg`中,`dataDir`参数定义了Zookeeper保存其事务日志和快照的目录。例如: ``` dataDir=/var/zookeeper/data ``` 这个路径下的`myid`文件则用来标识该节点在集群中的身份,内容是...
这是通过一系列的机制实现的,包括事务日志、快照和Leader选举等。 ##### 会话 (Sessions) Zookeeper中的客户端通过会话与服务端建立连接。会话具有时效性,一旦客户端长时间未与服务端交互,则会话将会过期。会话...
- `dataDir`: 指定Zookeeper保存数据的目录,用于存储快照和事务日志。 - `clientPort`: 每个Zookeeper实例对外提供服务的端口,默认为2181。 - `server.{id}`: 定义集群中的服务器,`{id}`是服务器标识,每个...