最近工作上需要写一个小工具,用于解析 beanstalkd 的 binlog。一如既往,我先在网上搜索相关的资料。令我惊讶的是,beanstalkd 官方文档中连关于 binlog 格式的只言片语都没有。网络上跟 beanstalkd binlog 格式相关的资料也几乎是个空白。所以在阅读了 beanstalkd 相关源码,并编写了对应工具之后,我试着在本文记下 beanstalkd 的 binlog 的一些相关信息。
binlog 文件格式
beanstalkd 的 binlog 位于 -b
选项指定的文件夹中,名字就叫 binlog.$order
。binlog 的序号 $order 从 1 开始,逐一递增。
binlog 文件大小是固定的,可以通过 -s
选项指定,默认为 10M。beanstalkd 在创建 binlog 文件时,会先用 0 填充文件的全部内容。
beanstalkd 的 binlog 内容非常浅显直白。每个 binlog 文件最开始是一个 int 类型的版本号,表示 binlog 版本号。
从 2013 年至今,最新的版本号是 7。可以这么认为,现行见到的 binlog 版本号都是 7。之后是每个操作的 binlog 记录。
值得一提的是,这里的 int 的大小是由编译时编译器决定的,虽然在主流平台上它的值是 32 位的。在解析时,最好用 int32 或等价操作去
处理它,而不是照搬 int 的定义。比如像 golang 里面的 int 就是默认 64 位的。
每个操作的 binlog 记录由四部份组成:
- len(tubeName),大小为 int
- 可选的 tubeName
- 表示 job 元数据的 record
- 可选的 job body 内容
job record 的格式如下:
// 注意我把 padding 标记成 _,原来的结构体定义里面是没有这个符号的
struct Jobrec {
uint64 id; // id >= 1
uint32 pri;
uint32 _; // 注意这里有一个内存对齐导致的 padding
int64 delay; // 精确到纳秒
int64 ttr; // 精确到纳秒
int32 body_size;
int32 _; // 这里是另外一个内存对齐导致的 padding
int64 created_at; // 创建时间, epoch 纪年,精确到纳秒
int64 deadline_at; // 下一个会因超时而产生状态变迁的时间
uint32 reserve_ct; // reserve 状态切换计数,_ct 结尾的都是状态计数
uint32 timeout_ct;
uint32 release_ct;
uint32 bury_ct;
uint32 kick_ct;
byte state;
byte[3] _; // 又一个 padding
};
创建的操作,会进行 write job full。也即会写入 len(tubeName),tubeName,record,body 这四项记录。
其他操作,会进行 write job short。只写入 0,record 这两项记录。这是因为 record 里面已经存有 id 了,
可以通过 id 获取对应 job 的 tubeName 和 body,所以就不需要在 binlog 里重复记录。
record 中的 state 取值如下:
enum // Jobrec.state
{
Invalid,
Ready,
Reserved,
Buried,
Delayed,
Copy
};
Ready, Reserved, Buried, Delayed 这四种状态对应的是 beanstalkd protocol
里面 job 的四种状态。
Invalid 对应的是删除特定的 job 的操作。
Copy 是 stats*
或 peek*
系列命令处理过程中产生的临时 job 才有的状态,不会记录到 binlog 中。
当我们解析到 id 为 0 的 job 时,意味着该 binlog 已经全部解析完毕,可以开始解析下一个 binlog 文件了。
总之解析每个 binlog 文件的流程是这样的:
binlog GC 和 compat
beanstalkd 会不停地产生 binlog 记录,如果缺少清理机制,很快就会把磁盘给塞满。
beanstalkd 在内部维护每一个 binlog 文件的 ref 计数。当一个文件里进行 write job full 操作时,会增加 ref;当一个 job
被删除时,会减少对应文件的 ref。当一个文件的 ref 减至 0,就可以放心地把它删掉了。这一操作,类似于 GC 中的引用计数,
在 beanstalkd 内部实现中也是把它称作 GC。
上述机制有一个问题。如果有个 job 一直不能够被删除掉,则对应的 binlog 文件也会一直无法删除掉。受影响的不止单个文件。由于
beanstalkd 需要保证 binlog 文件的有序性,该文件之后的其他 binlog 也无法被删除掉。
因此 beanstalkd 引入了 compat 机制来解决这个问题。
如果实际使用量不及日志量的一半,beanstalkd 会开始进行 compat。compat 不会修改现有 job,但会减少 job 对应文件的 ref,并新写入一条记录。
其效果等价于,把一个旧的 job 操作记录移动到新的文件中来。这么一来,就可以避免旧文件的回收被阻塞的情况了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。