Badger: A fast key-value store written purely in Golang (2017)
blog.dgraph.io
blog.dgraph.io
First of all, thanks to the growing Go community for using Badger. Within a short period of time since the original announcement [-1], Badger has become very popular.
A lot has changed since the original post. Despite being tagged as a non-goal at the time of release, Badger now runs concurrent ACID transactions providing serializable snapshot isolation guarantees.
In fact, Badger is being used to serve Petabytes of data (yes, PB, not TB). On my long TODO list to write a blog post about it. It has stood the test of time, being directly used by the community and Dgraph [0]. We have put it through many different crash testing scenarios, including ALICE[1], and run Jepsen style bank transaction tests every night for 8h [2].
There're some common questions around benchmarks on our blog posts. They were done before Badger had transactions and are somewhat old by now. But, Badger's read-write performance is still spectacular. In terms of write throughput, it outperforms RocksDB (called from Go), LevelDB BoltDB, and in terms of read performance, it goes neck-to-neck against B+ tree based DBs, which typically perform better than LSM trees. Recently, there was a benchmark by Ethereum 2.0 folks, which I ran [3]. You can see the ASCIInema here [4].
There's an open bounty of $1337 on Badger to help us find scenarios where Badger could lose data [5]. So, if you got ideas, throw them in!
Thanks again for using Badger!
[-1]: Well, the original link here on HN [0]: https://dgraph.io [1]: https://blog.dgraph.io/post/alice/ [2]: https://github.com/dgraph-io/badger/commit/c10276c9d3b0c4274... [3]: https://github.com/rawfalafel/db-benchmarks/pull/1 [4]: https://asciinema.org/a/Qgyf8C50YRbtH7WpixSaBKDUQ [5]: https://github.com/dgraph-io/badger/issues/601
1. Wasn't it simpler to just store the Key/Pointer(Offset) in RocksDB and put the values in the value log? Then the whole database just becomes a simple wrapper on top of RockDB(or whatever K/V database) with bunch of files as value log.
2. Why 16 bytes for value pointer? 8 bytes is sufficient.
3. If LSM size is around 5GB, screw LSM. I would rather use a simple immutable HashTable which gives you consistent snapshot out of the box for persisting to disk. Every 15 minutes launch a thread to serialize the index to disk.
Am I missing something or there is some more detail missing? I mean the reasoning to write a whole database from scratch isn't convincing.
2. It appears to be 12 bytes. ID, length, offset, all as 4 byte uint32 values: https://github.com/dgraph-io/badger/blob/5242a997f5101ee00c4...
Edit: Actually, the benchmarks turn off sync writes, which makes them silly to me. If you're okay with losing data, you probably wouldn't have chosen a disk based kv store. https://github.com/dgraph-io/badger-bench/blob/master/rw-ben...
The default install has sync writes turned on, so why benchmark it differently?
[1] https://www.meetup.com/RocksDB/photos/29331158/475279758/ [2] https://pingcap.com/blog/2016-11-15-Travelling-Back-in-Time-...
Supporting versioning while also doing value log GC is a very tough challenge, something that has taken us many iterations to get right. And there're still improvements that we can make, and users ask us for.
2. Skipping, another comment tackles that.
3. Using a hashtable would not give you sorted iterations, something that many users use very heavily. A 15 min thread idea would work a lot worse than LSM tree, just by definition.
CockroachDB has RocksDB at the bottom: https://www.cockroachlabs.com/docs/stable/architecture/stora...
Etcd uses a fork of BoltDB: https://github.com/etcd-io/bbolt
Mixpanel, DataDog, Linkedin and Pinterest also created their own DB tech. Linkedin and Pinterest use RocksDB. Instagram lately started doing something similar by using RocksDB as the storage layer for their Cassandra cluster.
It seems that most of the design effort of this project was focused on fast disk access.
Files are essentially mmap'd.
Regarding the hash table suggestion, you would lose compression/compaction and indexing/prefix scans.
Yes, but using LSM tree has a performance penalty over plain hash tables.
Yes, you would lose compression. But you could still have prefix scans by replacing the hash table by e.g. a red/black tree.
Goroutines do an equivalent job but write is synchronous and thus blocks one thread of the go scheduler, as far as I can tell.
Also the benefit of the async io API is that it allows sending multiple requests in ones call. Syscalls in go do have an overhead which can be mitigated this way, I guess.
If you use it as an in-memory DB, then I'd recommend using the LSM options, instead of Default options.