WAL
简单看一下RocksDB的WAL
SyncWAL
接口处有一个叫SyncWAL的函数,作用是把当前的日志同步到盘上。
相关的数据结构有一个deque<LogWriterNumber>
,以及一个锁叫做log_write_mutex_
。
其中LogWriterNumber
中保存了log filter number,log writer,一个标识当前日志正在进行Sync的标记,以及pre_sync_size,代表在调用Sync之前的已经写入当前日志的数据大小,这里注释是说这个值会被记录在Manifest中,也就是说RocksDB会记录每个日志都有哪些数据被刷到了盘中(具体目的还不清楚)。
SyncWAL中会取出当前MemTable的LogFileNumber,如果当前有日志正在进行Sync,判断的标记就是LogWriterNumber中的getting_sync,就会睡在一个CV上等待唤醒。
然后会把所有小于刚才记录的log file number的日志都调用PrepareForSync,表示当前日志正在进行Sync,并记录每个日志已经写入的数据量,即pre_sync_size。
然后把所有需要同步的日志都调用SyncWithoutFlush
,表示不会写入数据(写入PageCache),只会调用Sync。这里调WithoutFlush的含义我也不是很懂,感觉一把刷下去就好了。。可能是有什么feature上的兼容吧。
刷完以后可能会同步一下原数据,这里调的是FsyncWithDirOptions,猜测就是创建新的文件后就需要Sync一下Dir。
结束后会调MarkLogsSynced,里面会判断下,如果一个日志已经全部都Sync了,就会把他从刚才的deque中移除,否则的话就会调用FinishSync,标记Sync已经结束,等待下一轮的Sync。
如果打开了track_and_verify_wals_in_manifest
就会把刚才的变更记录到Manifest中,注释中说到他的用处就是用来及时发现Corruption。
如果要记录已经Sync的WAL的话,会调用ApplyWALToManifest
来把这个VersionEdit记录下来。
写链路上的WAL
上面看SyncWAL的时候遇到了几个问题,结合一些实现可以找到对应的答案。之所以会有SyncWAL这个接口是因为RocksDB提供一个配置叫need log sync,表示每次写WAL是否需要同步到盘上。在need log sync为false的时候,一般配合SyncWAL的调用使用。
而之所以要区分Flush和Sync,是因为RocksDB也提供一个选项叫manual wal flush,默认是关闭的。在写入WAL的时候,如果打开了manual wal flush,就会放弃调用Flush,而是等待用户手动调用FlushWAL。
在主链路的PreprocessWrite中,会和SyncWAL做类似的逻辑,即调用PrepareForSync,其目的注释中也有写,是为了防止并发的SyncWAL的调用。不过防止并发的Sync调用的目的目前还没太想明白,应该只是为了记录已经Sync的数据大小。
然后Leader会调用WriteToWAL,里面会做MergeBatch,将本次Batch写入到LogWriter中,并调用Sync。
在WAL写完后,同样有一个和SyncWAL类似的处理逻辑,就是调用MarkLogsSynced,这里会调用FinishSync,并且在配置打开的时候,会将Sync的日志信息记录到Manifest中。
SwitchWAL
在PreprocessWrite的时候,如果目前log的长度超过了阈值,就会调用SwitchWAL。只有在CF的数量大于1的时候才会调用SwitchWAL。
里面有个option叫atomic flush,说的是在没开WAL的时候,如果也希望多个CF的写入是原子的,就需要打开这个选项,和主链路无关先不细看。
这里会尝试对若干个CF调用SwitchMemTable,选择CF的条件是CF的LogNumber小于oldest_alive_log(这里感觉是需要联动看的)。
调用SwitchMemTable就会尝试打开一个新的LogWriter,并添加到上面提到的std::deque<LogWriterNumber>
这个结构中。并且会对这些CF调用FlushRequested,表示需要Flush L0。
在SwitchMemTable的时候,只有在最新的LogWriter中有新的写入的时候,才会做添加一个新的LogWriter。表示的是一个LogWriter不能记录同一个CF多个不同MemTable的写入。(同样理由还得再想想)
CreateWAL
CreateWAL实际上是在SwitchMemTable内部调用的,即在需要创建新的LogWriter的时候,就会调用CreateWAL。这里主要是看一下,除了SwitchWAL会调用SwitchMemTable,还有哪些地方会调用。
在写入MemTable的时候,比如MemTable的Add,会调用UpdateFlushState,如果MemTable过大,会将flush_state_置为FLUSH_REQUESTED
。
在WriteBatch写入MemTable的时候,会调用CheckMemTableFull,如果当前MemTable是ShouldScheduleFlush,就会将flush_state_ CAS为FLUSH_SCHEDULED
,并将当前的CF传给flush_scheduler
在Preprocess的时候会判断,如果flush_scheduler_
非空的话,就会调用ScheduleFlushes
,里面则会Switch掉所有非空的MemTable。
还有一种情况就是在Preprocess的时候,会判断write_buffer_manager->ShouldFlush
,里面会判断memtable的内存占用是否过大,过大的话会调用HandleWriteBufferManagerFlush
,同样是和ScheduleFlushes
类似的逻辑,Switch掉所有非空的MemTable。
文章评论