一個(gè)全新的 kv 存儲(chǔ)引擎-LotusDB
經(jīng)歷了大概 4 個(gè)月的打磨,LotusDB 的第一個(gè) release 版本終于發(fā)布了,我看了下,有 200 多次 commit(接近 rosedb 一年多的 commit 次數(shù)了)。
項(xiàng)目地址:https://github.com/flower-corp/lotusdb
有了 rosedb 在 bitcask 模型上的實(shí)踐之后,以及自己在存儲(chǔ)這方面的一些經(jīng)驗(yàn)積累,去年底的時(shí)候,在上班路上突然想到的一個(gè) idea,讓我有了做一個(gè)新的 kv 存儲(chǔ)引擎的想法。
有了想法之后便是驗(yàn)證,因?yàn)槠鋵?shí)心里還是沒(méi)譜,我又在 Github 上翻了翻,并沒(méi)有同類(lèi)型的實(shí)現(xiàn)。后來(lái)又找一些大佬溝通了下,證明我的想法是可行的。
這期間還發(fā)現(xiàn)了 Usenix Fast 上的一篇關(guān)于優(yōu)化 LSM 的論文,發(fā)現(xiàn)論文的內(nèi)容跟我的 idea 非常類(lèi)似,這算是又多了一個(gè)理論依據(jù),于是便決定開(kāi)干了。
眾所周知,數(shù)據(jù)存儲(chǔ)引擎,目前最主流的兩種模型是 B+ 樹(shù)和 LSM 樹(shù),B+ 樹(shù)在關(guān)系型數(shù)據(jù)庫(kù)例如 Mysql 中應(yīng)用比較廣泛,而 LSM 的典型代表 rocksdb 也是大多數(shù)分布式系統(tǒng)數(shù)據(jù)落盤(pán)的首選。
B+ 樹(shù)讀性能穩(wěn)定,而 LSM 寫(xiě)吞吐高,LotusDB 在這基礎(chǔ)上做了一個(gè)巨大的改動(dòng),就是完全舍棄掉 LSM 中的 SST 文件,改由 B+ 樹(shù)來(lái)存儲(chǔ)索引,而 value 存放則參考了 Wisckey 和 bitcask 模型的設(shè)計(jì),存儲(chǔ)到單獨(dú)的 value log 文件中。
LotusDB 是對(duì) LSM 和 B+ 樹(shù)的優(yōu)勢(shì)結(jié)合,目前并沒(méi)有同類(lèi)型的實(shí)現(xiàn),我們應(yīng)該是第一個(gè)吃螃蟹的人。
LotusDB 的架構(gòu)圖如下:
前臺(tái)的寫(xiě)入和 LSM 完全一致,先寫(xiě) wal 再寫(xiě) memtable。
而讀取則會(huì)從 memtable 開(kāi)始,如果 memtable 找到了,直接返回;沒(méi)找到的話則從 B+ 樹(shù)中查詢(xún)索引,然后根據(jù)索引信息到 value log 中獲取 value。
大家可以先了解個(gè)大概,后續(xù)我會(huì)出一個(gè)完整的《LotusDB 設(shè)計(jì)與實(shí)現(xiàn)》系列文章,全面解析 LotusDB 的架構(gòu)細(xì)節(jié)以及代碼實(shí)現(xiàn),目前已經(jīng)寫(xiě)了幾篇待發(fā)布,歡迎關(guān)注公眾號(hào)的后續(xù)更新:
再來(lái)看看 LotusDB 提供的一些基本接口,目前實(shí)現(xiàn)了基礎(chǔ)的 Put、Get、Delete 接口,并且支持 Column Family(借鑒于 rocksdb),以及 value log 的自動(dòng) GC 回收。
簡(jiǎn)單的使用方法如下:
package main
import (
"github.com/flower-corp/lotusdb"
"io/ioutil"
"time"
)
// basic operations for LotusDB:
// put
// put with options
// get
// delete
// delete with options
func main() {
path, _ := ioutil.TempDir("", "lotusdb")
opts := lotusdb.DefaultOptions(path)
db, err := lotusdb.Open(opts)
if err != nil {
panic(err)
}
defer db.Close()
// 1.----put----
key1 := []byte("name")
err = db.Put(key1, []byte("lotusdb"))
if err != nil {
// ...
}
key2 := []byte("feature")
// 2.----put with options----
writeOpts := &lotusdb.WriteOptions{
Sync: true,
ExpiredAt: time.Now().Add(time.Second * 100).Unix(),
}
err = db.PutWithOptions(key2, []byte("store data"), writeOpts)
if err != nil {
// ...
}
// 3.----get----
val, err := db.Get(key1)
if err != nil {
// ...
}
if len(val) > 0 {
// ...
}
// 4.----delete----
err = db.Delete(key1)
if err != nil {
// ...
}
// 5.----delete with options----
deleteOpts := &lotusdb.WriteOptions{
Sync: false,
DisableWal: true,
}
err = db.DeleteWithOptions([]byte("dummy key"), deleteOpts)
if err != nil {
// ...
}
}
目前自認(rèn)為 LotusDB 的代碼質(zhì)量比之前的 RoseDB 好多了,單元測(cè)試更加完備,注釋清晰,代碼也更加簡(jiǎn)潔規(guī)范,如果你是 Go 新手,或者準(zhǔn)備學(xué)習(xí) Go,也能夠把項(xiàng)目當(dāng)做練習(xí)素材,自己對(duì)照著來(lái)學(xué)習(xí)。
當(dāng)然我們的愿景還是打造一個(gè)能夠在生產(chǎn)環(huán)境中實(shí)際落地的存儲(chǔ)引擎,目前的版本只是一個(gè)開(kāi)始,后續(xù)還會(huì)有非常多的工作,包括但不限于:
- batch 操作,保證原子性
- 多個(gè) Column Family 保證原子性
- 基于 SSI 的事務(wù)
- Iterator 迭代器
- 數(shù)據(jù)壓縮
- 數(shù)據(jù)備份
- index 的分裂