mysql的预写日志_分布式系统设计模式 - 预写日志(Write Ahead Log)

预写日志(WAL,Write-Ahead Log)是一种用于提高数据持久性和系统性能的技术,常用于数据库和分布式系统。本文详细介绍了WAL的工作原理,包括其背景、解决方案和实现考虑,并以MySQL的InnoDB Redo Log和RocketMQ为例阐述了其实现。WAL通过顺序写入日志,确保即使在系统崩溃后也能恢复数据,从而提供数据一致性。
摘要由CSDN通过智能技术生成

Write-Ahead log 预写日志

预写日志(WAL,Write-Ahead Log)将每次状态更新抽象为一个命令并追加写入一个日志中,这个日志只追加写入,也就是顺序写入,所以 IO 会很快。相比于更新存储的数据结构并且更新落盘这个随机 IO 操作,写入速度更快了,并且也提供了一定的持久性,也就是数据不会丢失,可以根据这个日志恢复数据。

背景介绍

如果遇到了服务器存储数据失败,例如已经确认客户端的请求,但是存储过程中,重启进程导致真正存储的数据没有落盘,在重启后,也需要保证已经答应客户端的请求数据更新真正落盘成功。

解决方案

83051ddd761a

image

将每一个更新,抽象为一个指令,并将这些指令存储在一个文件中。每个进程顺序追加写各自独立的一个文件,简化了重启后日志的处理,以及后续的在线更新操作。每个日志记录有一个独立 id,这个 id 可以用来实现分段日志(Segmented Log)或者最低水位线(Low-Water Mark)清理老的日志。日志更新可以使用单一更新队列(Singular Update Queue)这种设计模式。

日志记录的结构类似于:

class WALEntry {

//日志id

private final Long entryId;

//日志内容

private final byte[] data;

//类型

private final EntryType entryType;

//时间

private long timeStamp;

}

在每次重新启动时读取日志文件,回放所有日志条目来恢复当前数据状态。

假设有一内存键值对数据库:

class KVStore {

private Map kv = new HashMap<>();

public String get(String key) {

return kv.get(key);

}

public void put(String key, String value) {

appendLog(key, value);

kv.put(key, value);

}

private Long appendLog(String key, String value) {

return wal.writeEntry(new SetValueCommand(key, value).serialize());

}

}

put 操作被抽象为 SetValueCommand,在更新内存 hashmap 之前将其序列化并存储在日志中。SetValueCommand 可以序列化和反序列化。

class SetValueCommand {

final String key;

final String value;

public SetValueCommand(String key, String value) {

this.key = key;

this.value = value;

}

@Override

public byte[] serialize() {

try {

//序列化

var baos = new ByteArrayOutputStream();

var dataInputStream = new DataOutputStream(baos);

dataInputStream.writeInt(Command.SetValueType);

dataInputStream.writeUTF(key);

dataInputStream.writeUTF(value);

return baos.toByteArray();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

public static SetValueCommand deserialize(InputStream is) {

try {

//反序列化

DataInputStream dataInputStream = new DataInputStream(is);

return new SetValueCommand(dataInputStream.readUTF(), dataInputStream.readUTF());

} catch (IOException e) {

throw new RuntimeException(e);

}

}

}

这可以确保即使进程重启,这个 hashmap 也可以通过在启动时读取日志文件来恢复。

class KVStore {

public KVStore(Config config) {

this.config = config;

this.wal = WriteAheadLog.openWAL(config);

this.applyLog();

}

public void applyLog() {

List walEntries = wal.readAll();

applyEntries(walEntries);

}

private void applyEntries(List walEntries) {

for (WALEntry walEntry : walEntries) {

Command command = deserialize(walEntry);

if (command instanceof SetValueCommand) {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值