slava是我参与的一个github开源项目,项目的主要的工作是构建一个高性能、K-V云数据库。 slava项目的连接
我们在实现数据库的操作的时候,会首先定义一个数据库结构体,名为DB:
之后在这个数据结构的基础上编写数据库的底层功能实现,比如Put、Get,Remove等。
但是对于数据库中的Flush函数,该函数所实现的功能是清空该数据库,我的编写和其他成员,下文称为成员A,代码存在一定的差异。
成员A实现Flush函数:
作者认为,上面的实现是并不完整的,Flush函数要做的是清空数据库,而上面函数的操作,只清空了DB结构体中的并没有清空versionMap。
versionMap是用在Watch命令中的,用来提交事务的时候取出对应key的版本号,然后对比key被watch(监控)时的版本号,如果版本号不一致说明在执行事务的过程中有别的协程对key进行了修改,则事务提交不成功,回滚。如果,版本号前后一致则可以提交事务成功。
作者认为正确的Flush函数的写法应该如下:
论证一:作者认为,最方便的做法就是直接调用makeDB函数,直接返回一个新的DB结构体,旧的DB将被GC自动回收,这样才起到了清空这个数据库的目的。
btw:想更深入的了解Go语言的GC,可以阅读作者的另一篇blog:链接: Go语言GC详解
论证二:同时从另一个角度来看,我们可以假设在一个高并发的场景中,一个协程A正在执行事务,还没有提交,这时候另一个协程B调用了成员A中的FLush函数,清空了data但是并没有清空versionMap。我们假设除了B协程Flush数据库外没有别的协程修改watch的key。在B协程调用成员A的Flush后,A协程提交说了事务,这时候它会对比watch的key的版本号,发现版本号没有发生改变,这时候协程A成功提交事务!这显然是不正确的,因为协程B已经清空了data中的所有key,B协程已经对key进行了修改,所以事实上应该时A协程提交事务失败。前后矛盾!
论证三:再者,作者查阅了github上redis的源码,链接: 源码地址。在dict.c的包中实现了清空数据库的操作:
上述的源码中,先清除数据库中的所有元素,然后回收内存,最后重新建立一个空的数据库。这样的想法与作者的想法相吻合,只是Go语言可以自动回收内存,不需要像C语言那样手动回收内存。
所以综上,作者认为自己编写的代码是正确的。