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. leecode刷题(27)-- 合并k个排序链表

    leecode刷题(27)-- 合并k个排序链表 合并k个排序链表 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1-> ...

  2. html/form表单常用属性认识

    1.form表单常用属性练习 <style> .form1 { margin: auto; height: 900px; width: 500px; text-align: center; ...

  3. exits 和no exits

    exists : 强调的是是否返回结果集,不要求知道返回什么, 比如:  select name from student where sex = 'm' and mark exists(select ...

  4. 实体类与数据库字段不匹配问题,java.sql.SQLSyntaxErrorException: Unknown column 'xxx' in 'field list'

    控制台报错 ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'user_nam ...

  5. django优化--ORM优缺点

    谈Django绕不开ORM ORM : ORM概念,ORM特点,ORM 的优点,ORM 的缺点 orm : 对象关系映射 (Object Relational Mapping) ,用于实现面向对象编程 ...

  6. 17种常用的JS正则表达式 非负浮点数 非负正数

    <input type='text' id='SYS_PAGE_JumpPage' name='SYS_PAGE_JumpPage' size='3' maxlength='5' onkeyup ...

  7. nfs服务的配置

    nfs服务 nfs简介 Network file system 网络文件系统.NFS server可以看作是一个 file server.它可以让你的pc通过网络将远端的nfs server共享出来的 ...

  8. java8学习之groupingByConcurrent与partioningBy源码分析

    在上一次[http://www.cnblogs.com/webor2006/p/8387656.html]中对于Collectors.groupingBy()方法进行了完整的分析之后,接着继续来分析一 ...

  9. java8学习之收集器用法详解与多级分组和分区

    收集器用法详解: 在上次已经系统的阅读了Collector收集器的Javadoc对它已经有一个比较详细的认知了,但是!!!它毕境是只是一个接口,要使用的话还得用它的实现类,所以在Java8中有它进行了 ...

  10. Tableau 分群

    对数据的特征进行分析,分群. 数据选用的是Iris data 下载地址:http://archive.ics.uci.edu/ml/machine-learning-databases/iris/ 1 ...