开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情
editslog 的刷盘机制
我们已经知道了 editslog 是分布式存储系统中的一种操作日志,它是负责将 NameNode 中的元数据持久化的;那么今天我们就一起来学习一下 editslog 的刷盘机制。
是直接刷盘的吗?
editslog 是直接刷盘的吗?一生成 editslog 就直接刷盘??
其实不然!
我们都知道磁盘IO是很慢的,如果 editslog 是直接刷盘的话,那性能也太差了。
editslog 双缓冲机制
为了避免生成 editslog 后直接刷盘,所以提出了 editslog 双缓冲机制。
双缓冲的机制:设计两块缓存,不停的往其中一块缓冲区里去写,一旦写满了之后,就由一个线程把这个缓冲区的数据刷入磁盘;在刷磁盘之前,会做一个缓冲区的交换,把两块缓冲区交换一下,让后续的editslog写入另外一块空置的缓冲区里去,之前写满的一块缓冲区就可以刷入磁盘中了。
双缓冲的结构定义:
editslog 生成的数据都往 currentBuffer 写,每次都拿 syncBuffer 这个刷盘;其实它们的作用有点像是指针,后面的交互缓冲区,其实就是交换这两个指针的指向。
@Slf4j
public class DoubleBuffer {
// 以下定义了两块缓冲区
// 当前写缓冲区,editslog 生成的数据都往这写
private EditLogBuffer currentBuffer;
// 刷盘缓冲区,每次都拿这个刷盘
private EditLogBuffer syncBuffer;
......
}
写入一条editlog:
向 currentBuffer 缓冲区中写入
/**
* 写入一条editlog
*/
public void write(EditLogWrapper editLog) throws IOException {
currentBuffer.write(editLog);
}
交换两块缓冲区:
交换 currentBuffer 和 syncBuffer 的指向
/**
* 交换两块缓冲区
*/
public void setReadyToSync() {
EditLogBuffer temp = currentBuffer;
currentBuffer = syncBuffer;
syncBuffer = temp;
}
把缓冲区的 editlog 数据刷新到磁盘:
EditslogInfo 是刷盘前 editslog 对应的磁盘文件的信息
/**
* 把缓冲区的 editlog 数据刷新到磁盘
*/
public EditslogInfo flush() throws IOException {
// 刷盘
EditslogInfo editslogInfo = syncBuffer.flush();
// 如果 editslogInfo 不为空,说明已经有数据成功刷盘了
// 这时候可以清楚缓冲区中的数据了
if (editslogInfo != null) {
syncBuffer.clear();
}
// 返回 editslog 文件信息
return editslogInfo;
}
刷盘的时机
这里的刷盘,我们又分为两种:一种是异步刷盘,另一种是强制刷盘。
异步刷盘的刷盘时机: 当缓冲区中的数据大小大于配置值的时候,触发异步刷盘。(每次往缓冲区中写入 editslog 的时候都会检查一下是否需要异步刷盘)
// 缓冲区中数据大小达到指定值的时候,才放行,执行异步刷盘
if (!editLogBuffer.shouldForceSync()) {
return;
}
强制刷盘的刷盘时机: 关机的时候需要强制刷盘!
/**
* 优雅停机
* 强制把内存里的edits log刷入磁盘中
*/
public void shutdown() {
log.info("Shutdown DiskNameSystem.");
// 强制刷盘
this.editLog.flush();
}