Zookeeper(四))持久化日志文件

持久化用途

存储两种文件

  • snapshot:内存快照
  • log:事务日志,类似MySQL的binlog,存储数据节点的操作日志

问题

  • 序列化的本质其实就是将原数据重新写入
  • roll中的BufferedOutputStream.flush和commit中的FileChannel.force()都是强制刷新:有什么区别

基本术语

  • FileTxnSnapLog,封装了TxnLog和SnapShot。 是操作数据文件和快照文件的对外API
  • TxnLog,接口类型,读取事务性日志的接口。
  • FileTxnLog,实现TxnLog接口,添加了访问该事务性日志的API。
  • Snapshot,接口类型,持久层快照接口。
  • FileSnap,实现Snapshot接口,负责存储、序列化、反序列化、访问快照。
  • Util,工具类,提供持久化所需的API。

实现

TxnLog
public interface TxnLog {
/**
* Setter for ServerStats to monitor fsync threshold exceed
* @param serverStats used to update fsyncThresholdExceedCount
*/
void setServerStats(ServerStats serverStats); // 滚动日志,从当前日志滚到下一个日志,不是回滚
void rollLog() throws IOException;
// 添加一个请求至事务性日志
boolean append(TxnHeader hdr, Record r) throws IOException; // 读取事务性日志
TxnIterator read(long zxid) throws IOException; // 事务性操作的最新zxid
long getLastLoggedZxid() throws IOException; // 清空zxid以后的日志
boolean truncate(long zxid) throws IOException; // 获取数据库的id
long getDbId() throws IOException; // 提交事务并进行确认
void commit() throws IOException; long getTxnLogSyncElapsedTime(); // 关闭事务性日志
void close() throws IOException;
}
FileTxnLog

实现TxnLog接口,提供操作事务日志的api

public class FileTxnLog implements TxnLog {
//最新的日志zxid
long lastZxidSeen;
//日志文件
volatile BufferedOutputStream logStream = null;
volatile OutputArchive oa;
//日志存储文件
File logFileWrite = null;
private FilePadding filePadding = new FilePadding();
private LinkedList<FileOutputStream> streamsToFlush =
new LinkedList<FileOutputStream>();
//重置日志文件
public synchronized void rollLog() throws IOException {
if (logStream != null) {
this.logStream.flush();
this.logStream = null;
oa = null;
}
}
//添加事务日志 hdr:事务标题 txn:事务本身
public synchronized boolean append(TxnHeader hdr, Record txn)
throws IOException {
if (hdr == null) {
return false;
}
//判断并更新最新的zxid
if (hdr.getZxid() <= lastZxidSeen) {
LOG.warn("Current zxid " + hdr.getZxid()
+ " is <= " + lastZxidSeen + " for " + hdr.getType());
} else {
lastZxidSeen = hdr.getZxid();
}
//构建事务日志文件
if (logStream==null) {
if(LOG.isInfoEnabled()){
LOG.info("Creating new log file: " + Util.makeLogName(hdr.getZxid()));
}
//1. 生成新的log文件
logFileWrite = new File(logDir, Util.makeLogName(hdr.getZxid()));
//2. 生成log文件输出流
fos = new FileOutputStream(logFileWrite);
//为写入给定的输出流而创建缓冲输出流
logStream=new BufferedOutputStream(fos);
//获取二进制序列化类 TODO
//BinaryOutputArchive内部维护一个DataOutput 根据值传递特性
//oa序列化写入时其实就是写入log文件
oa = BinaryOutputArchive.getArchive(logStream);
//3. 用TXNLOG_MAGIC VERSION dbId来生成事务日志文件头
FileHeader fhdr = new FileHeader(TXNLOG_MAGIC,VERSION, dbId);
//4. 将事务日志文件头序列化到文件上
fhdr.serialize(oa, "fileheader");
//确保文件扩展之前 魔数已被写入
logStream.flush();
filePadding.setCurrentSize(fos.getChannel().position());
streamsToFlush.add(fos);
}
//5. 剩余空间不足时 填充文件
filePadding.padFile(fos.getChannel());
//6. 将事务头部和本身序列化为字节数组
byte[] buf = Util.marshallTxnEntry(hdr, txn);
if (buf == null || buf.length == 0) {
throw new IOException("Faulty serialization for header " + "and txn");
}
//生成验证算法 校验数据流
Checksum crc = makeChecksumAlgorithm();
crc.update(buf, 0, buf.length);
oa.writeLong(crc.getValue(), "txnEntryCRC");
//6. 将当前序列化的事务记录写入到oa
Util.writeTxnBytes(oa, buf);
return true;
}
//从给定的zxid开始读取日志文件
public TxnIterator read(long zxid, boolean fastForward) throws IOException {
return new FileTxnIterator(logDir, zxid, fastForward);
}
//提交日志 保存到磁盘
public synchronized void commit() throws IOException {
//刷到磁盘
if (logStream != null) {
logStream.flush();
}
//强刷到磁盘
for (FileOutputStream log : streamsToFlush) {
log.flush();
if (forceSync) {
long startSyncNS = System.nanoTime();
FileChannel channel = log.getChannel();
//会强制将所有未写入磁盘的数据都强制写入磁盘 比如还在缓冲区中的数据
channel.force(false);
syncElapsedMS = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startSyncNS);
if (syncElapsedMS > fsyncWarningThresholdMS) {
if(serverStats != null) {
serverStats.incrementFsyncThresholdExceedCount();
}
LOG.warn("fsync-ing the write ahead log in "
+ Thread.currentThread().getName()
+ " took " + syncElapsedMS
+ "ms which will adversely effect operation latency. "
+ "File size is " + channel.size() + " bytes. "
+ "See the ZooKeeper troubleshooting guide");
}
ServerMetrics.FSYNC_TIME.add(syncElapsedMS);
}
}
//移除流并关闭
while (streamsToFlush.size() > 1) {
streamsToFlush.removeFirst().close();
}
//当日志文件大小超过限制 强刷到磁盘并重置
if(txnLogSizeLimit > 0) {
long logSize = getCurrentLogSize();
if (logSize > txnLogSizeLimit) {
LOG.debug("Log size limit reached: {}", logSize);
rollLog();
}
}
}
}

FileTxnIterator:用于读取事务日志

public static class FileTxnIterator implements TxnLog.TxnIterator {
public FileTxnIterator(File logDir, long zxid, boolean fastForward)
throws IOException {
this.logDir = logDir;
this.zxid = zxid;
init();
if (fastForward && hdr != null) {
while (hdr.getZxid() < zxid) {
if (!next())
break;
}
}
}
void init() throws IOException {
storedFiles = new ArrayList<File>();
//找出大于等于snapshot中最大的zxid的logfile及后续logfile并升序
List<File> files = Util.sortDataDir(FileTxnLog.getLogFiles(logDir.listFiles(), 0), LOG_FILE_PREFIX, false);
for (File f: files) {
if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) >= zxid) {
storedFiles.add(f);
}
// add the last logfile that is less than the zxid
else if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) < zxid) {
storedFiles.add(f);
break;
}
}
goToNextLog();
next();
}
}

Zookeeper(四))持久化日志文件的更多相关文章

  1. MySQL5.7 四种日志文件

    mysql 日志包括:错误日志,二进制日志,通用查询日志,慢日志等 一:通用查询日志: 记录建立的客户端连接和执行的语句 1)show variables like '%verision%'; 显示数 ...

  2. Zookeeper日志文件&事务日志&数据快照

    Zookeeper持久化两类数据,Transaction以及Snapshot,logDir存储transaction命令,dataDir存储snap快照,其下子目录名称以version-2命名,子目录 ...

  3. zookeeper清除日志文件工具

    zookeeper运行时间长了以后,日志会成为一个比较大的问题.比如作者压力测试hbase一周以后,zookeeper日志文件达到了10G的规模.由于zookeeper日志文件不能随意删除,因为一个长 ...

  4. 消费滚动滴log日志文件(flume监听,kafka消费,zookeeper协同)

    第一步:数据源 手写程序实现自动生成如下格式的日志文件: 15837312345,13737312345,2017-01-09 08:09:10,0360 打包放到服务器,使用如下命令执行,模拟持续不 ...

  5. 四步搞定Zabbix 日志文件监控

    Zabbix 日志文件监控 一.给运行Zabbix agent的用户授予要监控日志的读取权限. 1. 執行下面的命令,追加app的可讀權限: setfacl -m u:app:r-- /var/log ...

  6. nginx(四)初识nginx日志文件

    nginx 日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径.格式和缓存大小,通俗的理解就是先用log_format来定 ...

  7. 【lucene系列学习四】log4j日志文件实现多线程的测试

    参考资料:http://nudtgk2000.iteye.com/blog/1716379 首先,在http://www.apache.org/dyn/closer.cgi/logging/log4j ...

  8. ELK+Filebeat+Kafka+ZooKeeper 构建海量日志分析平台(elk5.2+filebeat2.11)

    ELK+Filebeat+Kafka+ZooKeeper 构建海量日志分析平台 参考:http://www.tuicool.com/articles/R77fieA 我在做ELK日志平台开始之初选择为 ...

  9. zookeeper(3) 持久化

    zookeeper为了防止,系统宕机或重启导致的数据丢失,会对数据进行定时持久化.有两种持久化方式: 1.为每次事务操作记录到日志文件,这样就可以通过执行这些日志文件来恢复数据. 2.为了加快ZooK ...

随机推荐

  1. [Next] 服务端渲染知识补充

    渲染 渲染:就是将数据和模版组装成 html 客户端渲染 客户端渲染模式下,服务端把渲染的静态文件给到客户端,客户端拿到服务端发送过来的文件自己跑一遍 js,根据 JS 运行结果,生成相应 DOM,然 ...

  2. 常见Http访问错误小结

    4xx 客户端错误# 400 bad request 错误的请求 # 401 未携带身份信息 # 403 forbidden 权限不够 # 404 Not Found# 405 请求方式不允许 5xx ...

  3. 帝国cms所有一级栏目遍历,如果有子栏目的话,遍历出来

    所有一级栏目遍历,如果有子栏目的话,遍历出来. 注意下方的bclassid是可以改变的.可以改成自己想要设置的父栏目id. 遍历所有栏目,如果有二级栏目的话显示 [e:loop={"sele ...

  4. feign 多参数问题

    参考: https://stackoverflow.com/questions/43604734/springboot-feignclient-method-has-too-many-paramter ...

  5. Clang教程之实现源源变化

    clang教程之实现源源变化 声明:本教程来自于Eli Bendersky's website 原文地址:http://eli.thegreenplace.net/2014/05/01/modern- ...

  6. Halide安装指南release版本

    Halide安装指南 本版本针对Halide release版本 by jourluohua 使用的是Ubuntu 16.04 64位系统默认Gcc 4.6 由于halide中需要C++ 11中的特性 ...

  7. 转载:ubuntu 下添加简单的开机自启动脚本

    转自:https://www.cnblogs.com/downey-blog/p/10473939.html linux下添加简单的开机自启动脚本 在linux的使用过程中,我们经常会碰到需要将某个自 ...

  8. vue配置路由时报错 Error in render: "RangeError: Maximum call stack size exceeded"

    虽然标题写的是配置路由报错,最终也是通过修改路由解决的,但是导致报错的还有一个主要因素,是因为我增加了一个功能“页面刷新时,根据url高亮左侧导航”,如下图: 1.页面刷新,根据url高亮左侧导航代码 ...

  9. 001-CentOS 7系统搭建Rsyslog+LogAnalyzer解决交换机日志收

    日志功能对于操作系统是相当重要的,在使用中,无论是系统还是应用等等,出了任何问题,我们首先想到的便是分析日志,查找问题原因.自 CentOS 7 开始,我们的 CentOS 便开始使用 rsyslog ...

  10. Python数据驱动DDT的应用

    在开始之前,我们先来明确一下什么是数据驱动,在百度百科中数据驱动的解释是:数据驱动测试,即黑盒测试(Black-box Testing),又称为功能测试,是把测试对象看作一个黑盒子.利用黑盒测试法进行 ...