几个常用数据库性能分析

最近公司需要选型一款单机KV数据库来做业务承载,所以我对比了目前市面上比较流行的几个KV数据库并记录下来,包括boltdb,rocksdb,pebbledb,badgerdb四款,我将简单分析一下各数据库的特点,最后用自己的简单测试程序跑一下各数据库。对比一下性能差异

boltdb

github地址:https://github.com/boltdb/bolt Star:11.7k Fork:1.5K

最后 Merge pull request 时间是2018年3月,很久没有维护了。

代码量:

rocksdb 和mysql对比_github


特点

  • 直接使用API存取数据,没有查询语句;
  • 支持完全可序列化的ACID事务,这个特性比LevelDB强;
  • 数据保存在内存映射的文件里。没有wal、线程压缩和垃圾回收;
  • 通过COW技术,可实现无锁的读写并发,但是无法实现无锁的写
  • 写并发,这就注定了读性能超高,但写性能一般,适合与读多写少的场景

与其他数据对比

LevelDB,RocksDB

LevelDB及其派生类(RocksDB,HyperLevelDB)与Bolt类似,因为它们是捆绑到应用程序中的库,但是它们的底层结构是日志结构的合并树(LSM树)。LSM树通过使用预写日志和称为SSTables的多层排序文件来优化随机写入。Bolt在内部使用B +树,并且仅使用一个文件。两种方法都需要权衡。 如果您需要较高的随机写入吞吐量(> 10,000 w / sec),或者需要使用旋转磁盘,那么LevelDB可能是一个不错的选择。如果您的应用程序是大量读取或进行大量范围扫描,那么Bolt可能是一个不错的选择。 另一个重要的考虑因素是LevelDB没有事务。它支持键/值对的批量写入,并且支持读取快照,但不能使您安全地执行比较和交换操作。Bolt支持完全可序列化的ACID事务。

LMDB

Bolt最初是LMDB的端口,因此在架构上相似。两者都使用B +树,具有ACID语义和完全可序列化的事务,并支持使用单个写入器和多个读取器的无锁MVCC。 这两个项目有些分歧。LMDB专注于原始性能,而Bolt专注于简单性和易用性。例如,出于性能考虑,LMDB允许执行几种不安全的操作,例如直接写入。Bolt选择禁止可能使数据库处于损坏状态的操作。Bolt唯一的例外是DB.NoSync。 API也有一些区别。打开LMDB时需要最大的mmap大小,mdb_env而Bolt会自动处理增量mmap的大小。LMDB使用多个标志来重载getter和setter函数,而Bolt将这些特殊情况拆分为自己的函数。

注意事项和局限性

选择合适的工具来完成这项工作很重要,而Bolt也不例外。在评估和使用Bolt时,需要注意以下几点:

  • Bolt非常适合读取密集型工作负载。顺序写入性能也很快,但是随机写入可能会很慢。您可以使用DB.Batch()或添加预写日志来帮助缓解此问题。
  • Bolt在内部使用B + tree,因此可以有很多随机页面访问。与旋转磁盘相比,SSD可以显着提高性能。
  • 尝试避免长时间运行的读取事务。Bolt使用写时复制功能,因此在旧事务使用旧页时无法回收这些旧页。
  • 从Bolt返回的字节片仅在事务期间有效。一旦事务被提交或回滚,它们所指向的内存就可以被新页面重用,或者可以从虚拟内存中取消映射,unexpected fault address访问时会出现恐慌。
  • Bolt在数据库文件上使用排他写锁定,因此不能被多个进程共享。
  • 使用时要小心Bucket.FillPercent。为具有随机插入的存储桶设置较高的填充百分比将导致数据库的页面利用率非常差。
  • 通常使用较大的水桶。较小的存储桶一旦超过页面大小(通常为4KB),就会导致页面利用率下降。
  • 批量加载大量随机写入新的存储桶可能很慢,因为在提交事务之前页面不会拆分。建议不要在单个事务中将100,000个以上的键/值对随机插入到一个新的存储桶中。
  • Bolt使用内存映射文件,因此底层操作系统可以处理数据的缓存。通常,操作系统将在内存中缓存尽可能多的文件,并根据需要将内存释放给其他进程。这意味着在使用大型数据库时,Bolt可能会显示很高的内存使用率。但是,这是预料之中的,操作系统将根据需要释放内存。只要Bolt的内存映射适合进程虚拟地址空间,它就可以处理比可用物理RAM大得多的数据库。在32位系统上可能会出现问题。
  • Bolt数据库中的数据结构是内存映射的,因此数据文件将是特定于字节序的。这意味着您无法将Bolt文件从小字节序计算机复制到大字节序计算机并使其正常工作。对于大多数用户而言,这不是问题,因为大多数现代CPU的字节序都很少。
  • 由于页面在磁盘上的布局方式,Bolt无法截断数据文件并将可用页面返回到磁盘。取而代之的是,Bolt会在其数据文件中维护未使用页面的空闲列表。这些空闲页面可以被以后的事务重用。由于数据库通常会增长,因此这在许多用例中效果很好。但是,请务必注意,删除大块数据将不允许您回收磁盘上的该空间。

RocksDB

github地址:https://github.com/facebook/rocksdb/ Star:16.9K Fork:4.4K

C++编写,现在还在维护

代码量:

rocksdb 和mysql对比_数据_02

特点

RocksDB最初的设计理念就是其应该在高速存储设备以及服务器压力下能有很好的性能表现。他应该能榨取Flash或者RAM子系统提供的所有读写速度潜能。他应该能支持高速的点查询和区间查询。可以通过配置支持很高的随机查询负荷,很高的更新负荷或者两者兼有。其架构应能很简单地对读放大,写放大和存储空间放大进行调优。go程序使用需要用到包装库gorocksdb

RocksDB文档很齐全,可以看官方文档介绍


PebbleDB

github地址:https://github.com/cockroachdb/pebble Star:1.9K Fork:124

纯go编写,现在还在维护

该项目是由cockroach工作室进行维护,pebbledb基础是levelsdb,然后把rocksdb的部分功能用go移植并对某些功能进行优化。

代码量

rocksdb 和mysql对比_分布式_03

特点

此db可以堪称rocksdb的go语言版本,但只实现了rocksdb的部分功能,也增加了一些rocksdb没有的功能和优化,可以看下github上的项目介绍。


BadgerDB

github地址:https://github.com/dgraph-io/badger Star:9.1K Fork:756

纯go编写,现在还在维护

下面是作者对此的介绍

Badger was written with these design goals in mind:

  • Write a key-value database in pure Go.
  • Use latest research to build the fastest KV database for data sets spanning terabytes.
  • Optimize for SSDs.

Badger’s design is based on a paper titled WiscKey: Separating Keys from Values in SSD-conscious Storage.

代码量

rocksdb 和mysql对比_rocksdb 和mysql对比_04

特点:

badgerdb对key,value进行了分别的处理,只把key存到了lms结构,value是用的Log文件进行处理,适合key小value大的情况。

Badgerdb官网提供了一个功能对比

Feature

Badger

RocksDB

BoltDB

Design

LSM tree with value log

LSM tree only

B+ tree

High Read throughput

Yes

No

Yes

High Write throughput

Yes

Yes

No

Designed for SSDs

Yes (with latest research 1)

Not specifically 2

No

Embeddable

Yes

Yes

Yes

Sorted KV access

Yes

Yes

Yes

Pure Go (no Cgo)

Yes

No

Yes

Transactions

Yes, ACID, concurrent with SSI3

Yes (but non-ACID)

Yes, ACID

Snapshots

Yes

Yes

Yes

TTL support

Yes

Yes

No

3D access (key-value-version)

Yes4

No

No


数据测试

测试代码地址为:https://github.com/huxinhuxin/kvtest

测试说明:

设定假如设定N次(启动参数-a决定,默认为1000),将会依次执行后续操作:

  1. 顺序N次写
  2. 随机读N次
  3. 随机删N次
  4. 随机写N次
  5. 遍历所有Key

KV全都是默认配置,现在都是单线程运行,sync属性都设置为true

N=10000

顺序写

随机读

随机删

随机写

遍历key

总耗时

Boltdb

2.498 s

39.848 ms

2.713 s

2.881 s

917.552µs

8.137 s

pebbledb

1.142 s

56.418 ms

711.88 ms

815.38ms

34.94 ms

2.763 s

badgerdb

2.833 s

39.154 ms

1.038 s

2.880 s

14.114 ms

6.829 s

Rocksdb

1.501 s

59.55 ms

39.86 ms

1.561 s

60 ms

3.23 s

磁盘占用:

Boltdb 59M pebbled 80M badgerdb 80M

N=100000

顺序写

随机读

随机删

随机写

遍历key

总耗时

Boltdb

26.459 s

527.01 ms

1m29.48 s

1m29.35 s

7.337 ms

3m25.84 s

pebbledb

7.7904 s

691.34 ms

6.553 s

8.202 s

420.94 ms

23.705 s

badgerdb

27.361 s

443.42 ms

10.182 s

28.262 s

98.491 ms

1m6.51 s

Rocksdb

15.93 s

1.63 s

396.42 ms

16.21 s

739.81 ms

34.92 s

磁盘占用:

Boltdb 591M pebbled 392M badgerdb 793M Rocksdb 380M

如果sync设置为false的话就可以快很对,但可能会掉数据,应为不是及时写盘,下面给出sync设置为false的情况,但仅供参考,生产环境暂时不考虑这个参数。

N=100000

顺序写

随机读

随机删

随机写

遍历key

总耗时

Boltdb

4.847 s

489.82 ms

50.370 s

48.122 s

7.030 ms

1m43.85 s

pebbledb

1.517 s

687.61 ms

147.55 ms

6.175 s

293.21 ms

8.84 s

badgerdb

1.880 s

428.59 ms

1.253 s

2.151 s

100.30 ms

6.521 s

Rocksdb

1.74 s

1.59 s

416.88 s

1.479 s

1.514 s

6.805s


总结

BoltDB

RocksDB

PebbleDB

BadgerDB

存储结构

B+TREE

LSM-TREE

LSM-TREE

Key是lms

读速度

最快




写速度

较慢


最快


存储文件数量

1个

多个

多个

几个

代码量(包含测试代码)万行

0.9

29

27

2.8

是否还在维护





成熟度(使用的项目)



少,但基于PebbleDB的CockroachDB使用还是挺多的


文档是否齐全

一般

最多

最少

较多

  • boltdb适合读比重较大的项目
  • badgerdb适合key小但value比较大的项目
  • rocksdb和pebbledb适合对写性能要求比较高的项目,c++程序用rocksdb,golang的话用pebbledb比较合适,当然rocksdb有部分功能pebble没有实现,所以需要先搞清楚项目需求