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

发布于 1周前 作者 h691938207 来自 Go语言

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

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

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

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

项目地址

https://github.com/xujiajun/nutsdb

项目特性

  • 高性能
  • 支持 ACID 事务
  • 支持基本的 Put、Delete、Get 操作
  • 支持前缀扫描
  • 支持范围扫描
  • 除了基本的 String,还支持多种数据结构如列表(list)、集合(set)、有序集合(sorted set)

项目背景

对于现状或多或少的不满

我想找一个用纯 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 !


更多关于Golang Go语言中开源一个纯Go编写的高性能内嵌型KV数据库NutsDB,支持事务以及多种数据结构的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

39 回复

支持

更多关于Golang Go语言中开源一个纯Go编写的高性能内嵌型KV数据库NutsDB,支持事务以及多种数据结构的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我最近在看 BoltDB 的实现,快读完了,打算用 C 重写其核心部分练练手,了解如何实现持久化 k/v 数据库。先 star 之,之后我拜读下你写的。

CockroachDB 的作者也写了个叫 pebble https://github.com/petermattis/pebble,最近才开始受到注意

还不够成熟,不过以作者的实力,如果投入足够,应该会是不错的。CockroachDB 现在用的是 RocksDB,我想他们可能也想用一个纯 go 实现的 kv 来代替吧。

能否提供类似 select db 的函数,每次都要指定 bucket 有点繁琐

star 为敬

额,这是个 production 还是一个 toy ?

#8 意思是东西都在内存里的嘛~

bitcask 如果不 flush 的话,会一直增大到无法忍受吧,以前豆瓣有个 beansdb

也不能这么说,操作系统会有一些策略把脏数据刷到磁盘,依赖操作系统正常情况下都没啥大问题,但是… 我只能说这个 benchmark 太不不公平了…

先 star 为敬

要么是我对持久化有什么误解,要么是作者对持久化有什么误解 :)


这不跟 mongodb 当年的套路一样么,不做 flush / fsync 啥玩意都能跑的飞快…

咋都在喷 不如想办法改进

你好 你在我项目中提交的 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 实测发现数据是更新的, 难道我理解错了。

谢谢 你说的很中肯哈

我发现数据还是会更新到磁盘的。我是调用了 unmap,可能再加个 flush 的话更保障一点。

文档说的没错,你用错了,要保证 nutsdb 宣称的 ACID 等高大上的特性,每次 commit 你都必须的 flush/sync,而 ACID 也不是说你简单加个锁就保证

想听一下作者对 redis 的看法。
虽说 redis 的事务不具备原子性,但是 redis 可以通过 lua 脚本支持事务。

=_= 楼主,我是来求资料的, Go 的 mmap 相关的貌似每个操作系统的 api 都不一样? 这方面的博客好少, 楼主有 Go 的 mmap 相关的资料吗, 弱弱求一份, 我大 Java 里的 mmap api 是统一的, Go 的这个搞的我很头疼 …

先 star.

谢谢你的意见建议。为了保证 ACID 的 D 特性 每次 commit 你都必须的 flush/sync,这个我理解的,强一致性。nutsdb 的实现为了写性能更高,现在对 nutsdb 的做法有点缓冲的味道,到 unmap 的时候才能保证数据一定落盘。
” ACID 也不是说你简单加个锁就保证“,能多说一点吗?

为了性能高这样做是没问题,但这个权衡牺牲了其它的特性就不能再说 nutsdb 有 ACID 等等特性,没有贬低的意思,简单的从 benchmark 来看这个惊人的结果就值得仔细思考下了,毕竟其它数据库生产环境验证优化了多年

就简单的说下 A 吧,毕竟是落盘的,简单的 unlock 来 rollback 就说不过去了

代码没细看,可能我说的不对,但 ACID 这一块我觉得你还是得仔细考量下,简单并不一定正确

redis 我一般当做缓存来用,不会去当 db 使用,事务支持也很弱。不过他的支持丰富的数据结构真的很棒。nutsdb 的数据结构 api 就是模仿他的名字命名。至于 lua 脚本的方式 ,我没试过,不过我在想在代码里写脚本 ,优点是灵活 缺点有点像直接写原生 sql 的感觉,不好维护 也不好看吧。最好 Redis 原生支持最好。

好的 我再想想。谢谢你。

如果抛开事务支持这一特性,NutsDB 可以秒杀其他了呢?

这就是一种可能存在的误解了… 所以作者这个项目如果作为自身学习是没问题的,大家也很支持,问题在于文案写的“越来越认真”导致了一些严重错误的东西被掩盖对于其它学习或者使用者来说是很误导人的

认同 “导致了一些严重错误的东西被掩盖对于其它学习”, 已经更新了 README 在警告说明处做了阐述,我还会继续更新文档的,让我好好整理下。谢谢 Damnever 关注。有问题 我继续改。
另外大家希望多多提 issue。
有问题直接去项目提 issue 哦。

你好 我已在最新的 master 和当前最新的版本 v0.3.0 支持了 强同步。真正支持了 D (持久化),性能报告也重新更新了。欢迎帮我 review,如有问题帮我指正。thank ;)

review 就谈不上了,benchmark 结果很不错,但要做全做好也是个学问,我不太懂就不瞎说了

就测试的规模来看,顶多几十兆的数据为啥还要搞个嵌入式的 kv 这么麻烦呢?

是的,做全做好也是个学问,我认可的。 我也在探索中,一有空就在改进优化中。后面有精力打算做成分布式的,这是后话。数据量的话, 当前版本 nutsdb 的数据存储上限取决于你的配置,如果是默认的全内存索引模型,瓶颈是内存,还有一种是内存+磁盘索引的模式,会放下比内存大的数据量。nutsdb 的场景数据,几十 M 肯定不止的。

我的意思是 benchmark 数据量可能太小了

嗯 。后面有空去弄下大一点的数据量的性能表现。

关于NutsDB这一在Go语言中开源的纯Go编写的高性能内嵌型KV数据库,以下是我的专业解读:

NutsDB是一个值得关注的数据库产品,它专为Go语言生态系统设计,并充分利用了Go语言的性能和灵活性。作为一个内嵌型KV数据库,NutsDB非常适合于需要高性能和事务支持的场景,如嵌入式系统、微服务、物联网应用等。

在数据结构上,NutsDB支持多种类型,这为用户提供了更大的灵活性。无论是简单的键值对,还是更复杂的结构,如列表、集合等,NutsDB都能轻松应对。这种多样化的数据结构支持使得NutsDB能够满足各种应用场景的需求。

此外,NutsDB还提供了事务支持,这是许多KV数据库所不具备的功能。事务支持确保了数据的一致性和完整性,使得在并发访问和修改数据时更加安全可靠。

从性能角度来看,NutsDB的纯Go实现确保了其高效性和低延迟。Go语言的编译型、并发性和垃圾回收机制都为NutsDB的性能优化提供了坚实的基础。

总的来说,NutsDB是一个功能强大、性能卓越的内嵌型KV数据库,它特别适合在Go语言环境中使用。如果你正在寻找一个高性能、事务支持且支持多种数据结构的KV数据库,那么NutsDB无疑是一个值得考虑的选择。无论是对于个人项目还是企业级应用,NutsDB都能提供稳定可靠的数据存储解决方案。

回到顶部