问题

flushall目前是为了删除所有数据,在tendis的实现中,很容易可以想到这个操作可以通过清空rocksdb的内容来实现。 然而,master清空所有数据后,如何同步给slave就变成了一个问题。因为repllog本身是记录在rocksdb内,清空所有数据表示repllog也会被清理,那么slave如何接受这个flushall请求呢?

解决方案

方案1. flushall的replog作为最后一个binlog写到rocksdb中,并且保证所有slave已经拉取最后这个flushall的repllog之后,再执行真正的清理rocksdb操作。

方案2. flushall的repllog作为新的rocksdb的第一个binlog,即先清理所有rocksdb数据,然后将flushall的repllog写到第一个repllog中。由于flushall的作用是清空所有数据,即使目前存在slave仍然落后,只要slave执行flushall后,数据也同样被清理,虽然过程中有部分repllog没有执行,但最终数据也是完全正确的。

考虑到方案1的flushall操作要等待slave完全跟上后才能执行,会导致master阻塞,并且控制逻辑比较复杂,最后选择方案2.

实现重点

方案2需要考虑的重点是

  1. flushall操作过程中,整个kvstore需要被阻塞,包括发送binlog到slave的操作 — 对KVStore上X锁
  2. flushall操作清空完数据后,需要还原被flush之前的RocksKVStore::getNextBinlogSeq(),因为这样slave就可以像什么都没发生一样,继续同步新rocksdb的repllog,并且第一个就会读到flushall

详见 RocksKVStore::flush()RocksKVStore::restart(restore, nextBinlogId

注意slave的RocksKVStore::getNextBinlogSeq()要与master一致。

  1. flushall的binlog需要区分于其他不同binlog,因为flushall需要对KVStore上X锁,其他只需上IX锁。因此,命令applybinlogsv2做了一些改造

applybinlogsv2 storeId binlogs cnt flag

最后一个参数flag,0(BinlogFlag::NORMAL)表示普通binlog, 1(BinlogFlag::FLUSH)表示flushall这类操作,同时保证了两类不同的binlog不会参杂在一起,简化了处理逻辑。也就是说,如果flag==1, cnt也恒为1.

  1. flushall/flushalldisk/flushdb 0目前都是清空所有数据,效果是一样的。

  2. 类似的swapdb等无法精确操作key的命令,可以用类似的方式来实现