仿Kafka轻量WAL框架

205 阅读4分钟

概述

什么是wal

WAL,write ahead log,预写日志。大家最耳熟能详的wal框架应该是mysql中的redolog、undolog和binlog。其中redolog和binlog就是wal,redolog侧重于保留写操作的记录,binlog侧重于主从同步。

下面deepseek列举一些常见组件中的wal,可以从不同角度来看,日志类型有直接装数据的和装操作指令的、清理策略是滚动的还是归档的,等等要去思考。

image.png

kafka与wal

WAL和Kafka在持久化、顺序写入方面有相似之处。Kafka的核心特点包括分区、副本、顺序写、高吞吐,还有日志分段管理。这些点应该都能应用到WAL的设计中。

比如,顺序写可以提高磁盘IO效率,这在WAL里也很重要。然后分区机制可能对应到不同的数据分片,副本机制保证高可用性。可能还需要支持多消费者或者多线程读取,类似Kafka的消费者组概念。

下面这段deepseek的思考真的很厉害,真的把下面这个实际开发项目的方方面面涵盖到了:

由此可以看到,AI的设计能力真的不是一般的强,还是在瞬间分析出来。今年上半年主攻大数据,下半年AI就要用起来,做起来,学起来。工作今年真的不重要了。前面这句话也不对,反正白天该做工作还是好好做,该表现就表现,展示出自己要在这里干到天荒地老。

考虑具体的设计模块。

首先是日志分段,像Kafka那样将日志分成多个段文件,避免单个文件过大,方便管理和清理。这里可能需要滚动策略,比如按大小或时间切分文件。(项目中最重要的一个实体类就是Log,一起的还有Segment等等)

写入优化,使用顺序追加写入,内存映射或者文件通道来提升速度。还要有刷盘策略,同步或异步,根据场景选择。(对,就是顺序写,经常有异常字符写进来就把整个文件堵住了)

高可用方面,是否需要副本?如果是单机WAL,可能不需要,但如果是分布式系统,可能需要主从复制,类似Kafka的副本机制。(副本在下面的项目中应该是没有用到)

另外,索引机制也很重要,Kafka有偏移量索引,WAL可能需要自己的索引方式,比如记录位置或者时间戳,加速读取。(索引机制也是和kafka一样的有Commit类,commit文件,deepseek说的太到点子上了)

数据清理方面,Kafka有基于时间或大小的保留策略,WAL可能需要类似的机制,比如定时删除旧日志,或者根据检查点来清理已提交的数据。(这一点也说得太对了,大量PB级的数据打过来,数据是肯定要实时清理的,不然磁盘哪装的下)

恢复机制是WAL的关键,系统启动时需要读取日志回放数据。这时候要考虑如何快速定位未提交的数据,可能需要检查点文件记录最后一次提交的位置。(说的太对了,commit的点很重要。前几天修改错了log文件,大哥是直接修改commit文件的值,让read线程重新定位读取)

最后是API设计,提供简单的写入和读取接口,可能支持同步或异步回调,方便集成到不同的系统中。

还要考虑扩展性,比如未来支持加密、压缩,或者监控指标。这些功能可以后续添加,但架构上要预留空间。

仿kafka项目

项目结构

我们先从最终文件的角度来看这个项目:

/home/xxx/wal
-- default
    -- partition-0
-- xxx(最大的流量来源单独拎出来)
    -- partition-0
    -- partition-1
        -rw-rw-r-- 1 service service    5242880 Mar 30 10:30 00000000000699308552.index
        -rw-rw-r-- 1 service service  100893115 Mar 30 10:30 00000000000699308552.log
        -rw-rw-r-- 1 service service    5242880 Mar 30 10:30 00000000000699308552.timeindex
        (下面commit文件每一行的值,就是上面的00000000000699308552这些)
        -rw-rw-r-- 1 service service    3540558 Mar 30 10:30 default-wal-partition-1.commit
        -rw-rw-r-- 1 service service    5242860 Mar 21 13:38 default-wal-partition-1.commit-deleted
    -- partition-2

再看下项目结构

my-service
-- 引入my-wal jar包
-- wal 目录
    -- WALManager.java 初始化,再加一个write方法
    -- DefaultReader.java 读wal磁盘线程

my-wal
-- manager 目录,有点提纲挈领的意思,用下面的实体类
    -- Groups.java
    -- Topics.java
    -- Partitions.java
    -- CommitFile.java
-- log 目录,最重要的实体类群了
    -- Log.java
    -- LogConfig.java
    -- LogSegment.java
    -- OffsetIndex.java
    -- TimeIndex.java
-- record 目录,重要实体类群
-- handler 目录,就是读线程
    -- ReadHandler.java
-- exception、util、constant 还有这仨目录

类图

wal类图.jpg