ZooKeeper事务日志记录器SyncRequestProcessor
SyncRequestProcessor作为一个ZooKeeper中的一个关键线程(ZooKeeperCriticalThread),是ZooKeeper请求处理链中的事务日志记录处理器,其主要用来将事务请求记录到事务日志文件中去,同时还会触发ZooKeeper进行数据快照。
数据结构
LinkedBlockingQueue<Request> queuedRequests:上一个RequestProcessor调用nextProcessor.processRequest(request)将request排入该队列中等待处理。Thread snapInProcess:负责快照线程,保证数据快照过程不影响ZooKeeper的主流程,需创建一个单独的异步线程来进行数据快照。LinkedList<Request> toFlush:在持久化过程中,使用组提交(Group Commits)来优化磁盘I/O操作。想象一个场景:当客户端有大量的事务请求,如果每次写请求都同步到磁盘,那么性能就会产生问题。所以设置该链表来暂存需要持久化到磁盘的Request。int snapCount:默认为100000,表示ZooKeeper每隔snapCount次事务日志记录后进行一个数据快照。
toFlush以及flush时机
toFlush队列可用于存储请求,可能是读也可能是写。
ZooKeeper专门使用线程SyncRequestProcessor来处理请求,所以这个线程必须合理的工作,否则会对整体的性能造成影响。如果客户端请求为读请求就没必要进行flush了,但如果是写请求,就必须把请求写入log,这个写入未必能保证真的同步到磁盘。所以合适的时机将缓存的事务日志刷入到磁盘是必须的。
从程序的设计应该能看到作者出于这个考虑选择了两个时机来做这件事情:
- 如果没有请求的时候(即较空闲的时候)
- 如果一直繁忙,则toFlush队列到达了一定数量(1000),就会批量同步
注意点
数据快照
每进行一次事务日志记录之后,ZooKeeper都会检测当前是否需要进行数据快照。理论上进行
snapCount次事务操作后就会开始数据快照,但是考虑到数据快照对于ZooKeeper所在机器的整体性能影响,需要尽量避免ZooKeeper集群中所有机器在同一时刻进行数据快照。因此ZooKeeper在具体的实现中,并不是严格按照这个策略执行,而是采取“过半随机”策略,即符合如下条件就进行数据快照:logCount > (snapCount / 2 + randRoll)
其中
logCount代表了当前已经记录的事务日志数量,randRoll为1 ~ snapCount/2之间的随机数,因此上面的条件就相当于:如果我们配置的snapCount为100000,那么ZooKeeper会在50000 ~ 100000次事务日志记录后进行一次数据快照。事务日志文件切换
当满足上述条件时,ZooKeeper就要开始进行数据快照了。首先是进行事务日志文件的切换。所谓的事务日志文件切换时指当前的事务日志已经“写满”,需要重新创建一个新的事务日志。即每当进行一次数据快照,重新创建一个事务日志文件。
源码
int logCount = 0;
int randRoll = r.nextInt(snapCount/2); // 产生0~snapCount/2之间的随机数
while (true) {
Request si = null;
if (toFlush.isEmpty()) {
si = queuedRequests.take(); // toFlush为空,不需要flush,没有数据则直接阻塞掉
} else {
si = queuedRequests.poll(); // 没有数据直接返回,有则拿出
if (si == null) { // 如果queuedRequests中没有数据,但toFlush不空,则表明ZooKeeper现在比较空闲,可以进行flush
flush(toFlush);
continue;
}
}
if (si == requestOfDeath) {
break;
}
if (si != null) {
if (zks.getZKDatabase().append(si)) { // 如果si是事务请求
logCount++;
if (logCount > (snapCount / 2 + randRoll)) { // 满足条件,需要进行数据快照
randRoll = r.nextInt(snapCount/2);
zks.getZKDatabase().rollLog(); // 切换事务日志文件
if (snapInProcess != null && snapInProcess.isAlive()) {
LOG.warn("Too busy to snap, skipping");
} else {
snapInProcess = new ZooKeeperThread("Snapshot Thread") { // 创建数据快照异步线程
public void run() {
try {
zks.takeSnapshot();
} catch(Exception e) {
LOG.warn("Unexpected exception", e);
}
}
};
snapInProcess.start(); // 保证数据快照过程不影响ZooKeeper的主流程,创建一个单独的异步线程来进行数据快照
}
logCount = 0;
}
} else if (toFlush.isEmpty()) { // 如果是非事务请求(读操作)且toFlush为空
// 说明近一段时间读多写少,直接响应,此处优化为了读比较频繁操作
// 为何读写不分开???
if (nextProcessor != null) {
nextProcessor.processRequest(si);
if (nextProcessor instanceof Flushable) {
((Flushable)nextProcessor).flush();
}
}
continue; // nextProcessor已经对该si处理过了,不用添加到toFlush中
}
toFlush.add(si);
if (toFlush.size() > 1000) { // 超过1000,直接flush
flush(toFlush);
}
}
}
参考
- zookeeper储存之实现分析
- 从Paxos到ZooKeeper分布式一致性原理与实践书籍
ZooKeeper事务日志记录器SyncRequestProcessor的更多相关文章
- zookeeper 事务日志
前面提到,在zookeeper server的配置文件zoo.cfg中可以通过dataLogDir来配置zookeeper的事务日志的输出目录,这个事务日志类似于下面这样的文件: 这个文件是一个二进制 ...
- zookeeper 事务日志与快照日志
zookeeper日志各类日志简介 zookeeper服务器会产生三类日志:事务日志.快照日志和log4j日志. 在zookeeper默认配置文件zoo.cfg(可以修改文件名)中有一个配置项data ...
- zookeeper 事务日志查看
在version下的日志是二进制文件,查看需要转换 创建/data/middleware/zookeeper-3.4.14/translog.sh 脚本 格式化命令: java -classpath ...
- Zookeeper日志文件&事务日志&数据快照
Zookeeper持久化两类数据,Transaction以及Snapshot,logDir存储transaction命令,dataDir存储snap快照,其下子目录名称以version-2命名,子目录 ...
- zookeeper读取事务日志、快照日志
zookeeper的事务日志的格式如 log.xxx, xxx表示顺序序号 我使用的zookeeper版本:3.5.5 事务日志 执行命令 java -cp .:/tmp/zookeeper-3.5. ...
- ZooKeeper系列(5):ZooKeeper的日志和快照
ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk ZooKeeper有两种日志.一种快照.日志分为事务日志和Zoo ...
- SharePoint 2010 数据库xxx的事务日志已满
接到领导安排,说客户有问题 请求协助解决,对方给我展示的错误日志,如下: 数据库'WSS_Content_xxxx'的事务日志已满.若要查明无法重用日志中的空间的原因,请参阅sy.databases中 ...
- SQL Server 事务以及事务日志综述
事务是一个非常重要的概念,特此在这里写一些文章来总结.整篇文章还在持续更新中. 在本系列文章中,你将看到以下内容: 数据库事务(Database Transaction)概述 事务操作(BEGIN/C ...
- SQL Server 事务日志传输
概述 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使辅助数据库与主数据库基本保持 ...
随机推荐
- centos 7.2 网卡配置文件 及 linux bridge的静态配置
在 centos 7.2 系统内, 网卡的配置文件在: /etc/sysconfig/network-scripts/ 下. 命名规则: ifcfg-xxxx. xxx为设备名称. 通过分析 ne ...
- windows下多字节和宽字节转换
先简单说下什么是多字节和宽字节. 多字节是指使用多个字节(1-3)表示一个字符.比如gbk使用英文占一个字节,中文占2个,这个就是多字节了.utf-8是使用1-3个字节表示字符.还有big5等等. 宽 ...
- HP_UX HBA 卡信息收集脚本
#/usr/bin/shname1=`hostname`_fcioscan -kfnNC fc |grep "/dev/" >/tmp/data/$name1.txtnum1 ...
- 获取B表数据添加到A表中作为一个下拉列表元素存在
1.ProductController类里toedit方法内添加: ProductModel product = ProductModel.dao.findById(id); //通过id查找服务类 ...
- JS原型和继承
//所有的函数都有一个prototype属性 function aa() { } console.info(aa.prototype); //这个prototype属性引用了一个对象,即原型,初始化时 ...
- python3.5-day5_迭代器_生成器_装饰器_模块
笔者QQ 360212316 迭代器&生成器 生成器: 一个函数调用返回一个迭代器,那这个函数叫做生成器,如果函数中包含yield语法,那么这个函数就会变成生成器 生成器的特点: 1.生成器必 ...
- CListBox多选情况处理方法
如ListBox的内容如下,蓝色代表选中的内容 列表 索引 删除时索引 item1 0 0 item2 1 0 item3 2 item4 3 1 删除所有选中列: vo ...
- range()和xrange()区别
版本:Python2.7 1.先看帮助说明 (1)range()返回一个递增或递减的数字列表,列表的元素由三个参数决定 start 表示列表开始的值,默认为0 stop 表示列 ...
- Android笔记之——线程
Java: package com.example.test_ui_thread01;import android.support.v7.app.AppCompatActivity;import an ...
- oracle sql改写
or可以改写成union 但是要注意,改写成union的时候一定要有一个唯一列参照,不然会少记录,因为union会去重. 可以用的唯一列:唯一索引列,主键列,rowid,rownum(视图里用这个)