badfer是一個純Go實現的快速的嵌入式K/V資料庫,針對LSM tree做了優化。
安裝
$ go get github.com/dgraph-io/badger/...
資料庫
開啟一個資料庫
opts := badger.DefaultOptions
opts.Dir = "/tmp/badger"
opts.ValueDir = "/tmp/badger"
db, err := badger.Open(opts)
if err != nil {
log.Fatal(err)
}
defer db.Close()
複製程式碼
儲存
儲存kv
使用 Txn.Set()方法
err := db.Update(func(txn *badger.Txn) error {
err := txn.Set([]byte("answer"), []byte("42"))
return err
})
複製程式碼
批量設定
wb := db.NewWriteBatch()
defer wb.Cancel()
for i := 0; i < N; i++ {
err := wb.Set(key(i), value(i), 0) // Will create txns as needed.
handle(err)
}
handle(wb.Flush()) // Wait for all txns to finish.
複製程式碼
WriteBatch不允許任何讀取。對於讀-修改-寫,應該使用事務API。
設定生存時間 TTL
Badger允許在鍵上設定一個可選的生存時間(TTL)值。一旦TTL結束,KEY將不再是可檢索的,並且將進行垃圾收集。TTL可以使用Txn.SetWithTTL() 設定為一個time.Duration
的值
設定後設資料
Txn.SetWithMeta()
設定使用者後設資料
使用 Txn.SetEntry()
可以一次性設定key, value, user metatadata and TTL
遍歷keys
要遍歷鍵,我們可以使用迭代器,可以使用 Txn.NewIterator()`方法獲得迭代器。迭代按位元組字典排序順序進行。
err := db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 10
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
err := item.Value(func(v []byte) error {
fmt.Printf("key=%s, value=%s\n", k, v)
return nil
})
if err != nil {
return err
}
}
return nil
})
複製程式碼
字首掃描
要遍歷鍵字首,可以將Seek()和ValidForPrefix()組合使用:
db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
prefix := []byte("1234")
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
item := it.Item()
k := item.Key()
err := item.Value(func(v []byte) error {
fmt.Printf("key=%s, value=%s\n", k, v)
return nil
})
if err != nil {
return err
}
}
return nil
})
複製程式碼
鍵的遍歷
Badger支援一種獨特的迭代模式,稱為只有鍵的迭代。它比常規迭代快幾個數量級,因為它只涉及對lsm樹的訪問,而lsm樹通常完全駐留在RAM中。要啟用只有鍵的迭代,您需要設定IteratorOptions。PrefetchValues欄位為false。這還可以用於在迭代期間對選定的鍵執行稀疏讀取,只在需要時呼叫item.Value()。
err := db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchValues = false
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
fmt.Printf("key=%s\n", k)
}
return nil
})
複製程式碼
資料流
Badger提供了一個流框架,它可以併發地遍歷資料庫的全部或部分,將資料轉換為自定義鍵值,並連續地將資料流輸出,以便通過網路傳送、寫入磁碟,甚至寫入Badger。這是比使用單個迭代器更快的遍歷Badger的方法。Stream在管理模式和正常模式下都支援Badger。
stream := db.NewStream()
// db.NewStreamAt(readTs) for managed mode.
// -- Optional settings
stream.NumGo = 16 // Set number of goroutines to use for iteration.
stream.Prefix = []byte("some-prefix") // Leave nil for iteration over the whole DB.
stream.LogPrefix = "Badger.Streaming" // For identifying stream logs. Outputs to Logger.
// ChooseKey is called concurrently for every key. If left nil, assumes true by default.
stream.ChooseKey = func(item *badger.Item) bool {
return bytes.HasSuffix(item.Key(), []byte("er"))
}
// KeyToList is called concurrently for chosen keys. This can be used to convert
// Badger data into custom key-values. If nil, uses stream.ToList, a default
// implementation, which picks all valid key-values.
stream.KeyToList = nil
// -- End of optional settings.
// Send is called serially, while Stream.Orchestrate is running.
stream.Send = func(list *pb.KVList) error {
return proto.MarshalText(w, list) // Write to w.
}
// Run the stream
if err := stream.Orchestrate(context.Background()); err != nil {
return err
}
// Done.
複製程式碼
刪除一個key
使用Txn.Delete()
方法刪除一個key
獲取key value
通過 txn.Get獲取value
err := db.View(func(txn *badger.Txn) error {
item, err := txn.Get([]byte("answer"))
handle(err)
var valNot, valCopy []byte
err := item.Value(func(val []byte) error {
// This func with val would only be called if item.Value encounters no error.
// Accessing val here is valid.
fmt.Printf("The answer is: %s\n", val)
// Copying or parsing val is valid.
valCopy = append([]byte{}, val...)
// Assigning val slice to another variable is NOT OK.
valNot = val // Do not do this.
return nil
})
handle(err)
// DO NOT access val here. It is the most common cause of bugs.
fmt.Printf("NEVER do this. %s\n", valNot)
// You must copy it to use it outside item.Value(...).
fmt.Printf("The answer is: %s\n", valCopy)
// Alternatively, you could also use item.ValueCopy().
valCopy, err = item.ValueCopy(nil)
handle(err)
fmt.Printf("The answer is: %s\n", valCopy)
return nil
})
複製程式碼
如果不存在 Txn.Get()
將會返回一個 ErrKeyNotFound
錯誤
請注意,Get()返回的值只在事務開啟時有效。如果需要在事務外部使用值,則必須使用copy()將其複製到另一個位元組片。
事務
只讀事務
只讀事務使用 DB.View()方法
err := db.View(func(txn *badger.Txn) error {
// Your code here…
return nil
})
複製程式碼
讀寫事務鎖
讀寫事務可以使用 DB.Update()方法
err := db.Update(func(txn *badger.Txn) error {
// Your code here…
return nil
})
複製程式碼
手動管理事務
直接使用DB.NewTransaction()
函式,手動建立和提交事務。它接受一個布林引數來指定是否需要讀寫事務。對於讀寫事務,需要呼叫Txn.Commit()
來確保事務已提交。對於只讀事務,呼叫
txn.reject()
就可以了。commit()
也在內部呼叫
txn .reject()
來清除事務,因此只需呼叫Txn.Commit()就足以執行讀寫事務。
但是,如果您的程式碼由於某種原因(出錯)沒有呼叫Txn.Commit()
。就需要在defer中呼叫 txn . reject()
// Start a writable transaction.
txn := db.NewTransaction(true)
defer txn.Discard()
// Use the transaction...
err := txn.Set([]byte("answer"), []byte("42"))
if err != nil {
return err
}
// Commit the transaction and check for error.
if err := txn.Commit(); err != nil {
return err
}
複製程式碼