开源一个纯 Go 编写的高性能内嵌型 KV 数据库 NutsDB,支持事务以及多种数据结构。

2019-03-06 15:32:33 +08:00
 xujiajun001

大家好,分享一个最近开源的 KV 数据库项目 NutsDB。是我对 nosql 一个阶段性实践吧。

NutsDB 是纯 Go 语言编写一个简单、高性能、内嵌型、持久化的 key-value 数据库。

NutsDB 支持 ACID 事务,所有的操作都在事务中执行,保证了数据的完整性。NutsDB 从 v0.2.0 版本开始支持多种数据结构,如列表(list)、集合(set)、有序集合(sorted set)。

项目地址

https://github.com/xujiajun/nutsdb

项目特性

项目背景

对于现状或多或少的不满

我想找一个用纯 go 编写,尽量简单(方便二次开发、研究)、高性能(读写都能快一点)、内嵌型的(减少网络开销)数据库,最好支持事务。因为我觉得对于数据库而言,数据完整性很重要。如果能像 Redis 一样支持多种数据结构就更好了。 而像 Redis 一般用作缓存,对于事务支持也很弱。

找到几个备选项:

BoltDB BoltDB 是一个基于 B+ tree,有着非常好的读性能,还支持很实用的特性:范围扫描和按照前缀进行扫描。有很多项目采用了他。虽然现在官方不维护,由 etcd 团队在维护 他也支持 ACID 事务,但是他的写性能不是很好。如果对写性能要求不高也值得尝试。

GoLevelDB GoLevelDB 是 google 开源的 leveldb 的 go 语言版本的实现。他的性能很高,特别是写性能,据官方 c++版本说可以到 40w+次写 /秒,他基于 LSM tree 实现。他不支持事务。

Badger Badger 同样是基于 LSM tree,不同的是他把 key/value 分离。据他官网描述是基于为 SSD 优化。同是他也支持事务。但是我简单写了 benchmark 发现他的写性能没我想象中高。

好奇心的驱使

对于如何实现 kv 数据库的好奇心吧。数据库可以说是系统的核心,了解数据库的内核或者自己有实现,对更好的用轮子或者下次根据业务定制轮子都很有帮助。

基于以上两点,我决定尝试开发一个简单的 kv 数据库,性能要好,功能也要强大(至少他们好的功能特性都要继承)。

如上面的选项,我发现大致基于存储引擎的模型分:B+ tree 和 LSM tree。基于 B+ tree 的模型相对后者成熟。一般使用覆盖页的方式和 WAL (预写日志)来作崩溃恢复。而 LSM tree 的模型他是先写 log 文件,然后在写入 MemTable 内存中,当一定的时候写回 SSTable,文件会越来越多,于是他一般作法是在后台进行合并和压缩操作。 一般来说,基于 B+ tree 的模型写性能不如 LSM tree 的模型。而在读性能上比 LSM tree 的模型要来得好。当然 LSM tree 的模型也可以优化,比如引入 BloomFilter。 但是这些模型还是太复杂了。我喜欢简单,简单意味着好实现,好维护,相对不容易出错。

直到我找到 bitcask 这种模型,他其实本质上也算 LSM tree 的范畴吧。 他模型非常简单很好理解和实现,很快我就实现了一个版本。但是他的缺点是不支持范围扫描。我尝试去优化他,又开发一个版本,基于 B+ tree 作为索引,满足了范围扫描的问题 ,读性能是够了,写性能很一般,又用 mmap 和对原模型作了精简,这样又实现了一版。写性能又提高了几十倍。现在这个版本基本上都实现上面提到的数据库的一些有用的特性,包括支持范围扫描和前缀扫描、包括支持 bucket、事务等,还支持了更多的数据结构( list、set、sorted set )。从 benchmark 来看,NutsDB 性能只高不低, 这是 example 里面的代码 https://github.com/xujiajun/nutsdb/blob/master/examples/batch/put/main.go ,100w 条数据,我本机基本上 2s 跑完 ,写性能可达到 40~50W+/秒。

天下没有银弹,NutsDB 也有他的局限,比如随着数据量的增大,索引变大,启动会慢。 只想说 NutsDB 还有很多优化和提高的空间,由于本人精力以及能力有限。所以把这个项目开源出来。更重要的是我认为一个项目需要有人去使用,有人提意见才会成长。

希望一起来参与贡献,欢迎 Star、提 issues、提交 PR !

5046 次点击
所在节点    Go 编程语言
38 条回复
bestkayle
2019-03-06 15:52:41 +08:00
支持
falcon05
2019-03-06 15:55:34 +08:00
学习了
whoisghost
2019-03-06 15:59:29 +08:00
我最近在看 BoltDB 的实现,快读完了,打算用 C 重写其核心部分练练手,了解如何实现持久化 k/v 数据库。先 star 之,之后我拜读下你写的。
reus
2019-03-06 16:12:30 +08:00
CockroachDB 的作者也写了个叫 pebble https://github.com/petermattis/pebble,最近才开始受到注意

还不够成熟,不过以作者的实力,如果投入足够,应该会是不错的。CockroachDB 现在用的是 RocksDB,我想他们可能也想用一个纯 go 实现的 kv 来代替吧。
1892
2019-03-06 16:41:58 +08:00
能否提供类似 select db 的函数,每次都要指定 bucket 有点繁琐
solupro
2019-03-06 16:49:18 +08:00
star 为敬
misaka19000
2019-03-06 16:54:44 +08:00
额,这是个 production 还是一个 toy ?
Damnever
2019-03-06 17:12:47 +08:00
想法很好,但有个大问题 https://github.com/xujiajun/nutsdb/issues/10
server
2019-03-06 17:27:46 +08:00
star
JohnSmith
2019-03-06 18:17:26 +08:00
@Damnever #8 意思是东西都在内存里的嘛~
wbrobot
2019-03-06 18:25:55 +08:00
bitcask 如果不 flush 的话,会一直增大到无法忍受吧,以前豆瓣有个 beansdb
Damnever
2019-03-06 18:27:09 +08:00
@JohnSmith 也不能这么说,操作系统会有一些策略把脏数据刷到磁盘,依赖操作系统正常情况下都没啥大问题,但是… 我只能说这个 benchmark 太不不公平了…
fuyufjh
2019-03-06 18:30:27 +08:00
先 star 为敬
azh7138m
2019-03-06 18:33:59 +08:00
@JohnSmith 要么是我对持久化有什么误解,要么是作者对持久化有什么误解 :)
liprais
2019-03-06 18:58:28 +08:00
@Damnever
这不跟 mongodb 当年的套路一样么,不做 flush / fsync 啥玩意都能跑的飞快........
runningman
2019-03-06 19:10:55 +08:00
咋都在喷 不如想办法改进
xujiajun001
2019-03-07 09:13:36 +08:00
@Damnever 你好 你在我项目中提交的 issue 我已经回复你了,不好意思现在才回你。我其实在代码中使用了 unmmap,我的依据是 https://linux.cn/man2/mmap.2.html 提到这样一句话 The file may not actually be updated until msync(2) or munmap(2) are called,按照他这么说,只调用 unmap 实测发现数据是更新的, 难道我理解错了。
xujiajun001
2019-03-07 09:14:43 +08:00
xujiajun001
2019-03-07 09:14:59 +08:00
@runningman 谢谢 你说的很中肯哈
xujiajun001
2019-03-07 09:16:34 +08:00
@JohnSmith 我发现数据还是会更新到磁盘的。我是调用了 unmap,可能再加个 flush 的话更保障一点。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/541765

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX