问题
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
需要考虑的重点是
flushall
操作过程中,整个kvstore需要被阻塞,包括发送binlog到slave的操作 — 对KVStore上X锁flushall
操作清空完数据后,需要还原被flush之前的RocksKVStore::getNextBinlogSeq()
,因为这样slave就可以像什么都没发生一样,继续同步新rocksdb的repllog,并且第一个就会读到flushall
。
详见 RocksKVStore::flush()
,RocksKVStore::restart(restore, nextBinlogId
注意slave的RocksKVStore::getNextBinlogSeq()
要与master一致。
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.
flushall
/flushalldisk
/flushdb 0
目前都是清空所有数据,效果是一样的。类似的
swapdb
等无法精确操作key的命令,可以用类似的方式来实现