1.Batch的日常使用
batch的日常使用比較簡單,使用Batch是提高leveldb寫入性能的一個關(guān)鍵。
db, _ := leveldb.OpenFile("file", nil)
batch := leveldb.Batch{}
batch.Put([]byte("k1"), []byte("y"))
batch.Delete([]byte("k1"))
db.Write(&batch, nil)
本文將結(jié)合batch的內(nèi)部結(jié)構(gòu)分析Put、Delete等操作的相關(guān)原理。
2.Batch的內(nèi)部結(jié)構(gòu)
db實(shí)例內(nèi)部擁有一個batch的對象池,batchPool, 在常規(guī)的db.put操作中,實(shí)際數(shù)據(jù)也是插入到batch 中的:
batch := db.batchPool.Get().(*Batch)
batch.Reset()
batch.appendRec(kt, key, value)
return db.writeLocked(batch, batch, merge, sync)
使用batchPool的方式減少了batch對象的開銷,batch的結(jié)構(gòu)定義如下
type Batch struct {
data []byte
index []batchIndex
// internalLen is sums of key/value pair length plus 8-bytes internal key.
internalLen int
}
batch使用一個byte數(shù)組存儲具體數(shù)據(jù),這里data存儲實(shí)際的key和value值。batchIndex用于data內(nèi)部的key,value的索引
Batch內(nèi)部數(shù)據(jù)結(jié)構(gòu)圖示如下:

圖1. batch internal datastructure.png
type batchIndex struct {
keyType keyType //key的類型 插入或者刪除
keyPos, keyLen int //key的起始位置及其長度
valuePos, valueLen int //value的起始長度以及其長度
}
3.Batch的關(guān)鍵函數(shù)
3.1 appendRec 數(shù)據(jù)插入函數(shù)
該內(nèi)部函數(shù)是Batch的Put以及Delete函數(shù)的內(nèi)部實(shí)現(xiàn),Put或者Delete以key的類型進(jìn)行區(qū)分。
batch的data部分存儲key和value的長度,主要是為了decodeBatch的方便。
func (b *Batch) appendRec(kt keyType, key, value []byte) {
//1.計算存儲kt keylen key valuelen value的data所需要的長度
n := 1 + binary.MaxVarintLen32 + len(key)
if kt == keyTypeVal {
n += binary.MaxVarintLen32 + len(value)
}
//容量不足時及時擴(kuò)充容量
b.grow(n)
index := batchIndex{keyType: kt}
o := len(b.data)
data := b.data[:o+n] //更新slice, 即增加了data的實(shí)際length
data[o] = byte(kt) // o位置加上標(biāo)識位 占8位
o++
//序列化key值的長度并存儲到data內(nèi),key最長不超過32位
o += binary.PutUvarint(data[o:], uint64(len(key)))
index.keyPos = o
index.keyLen = len(key) //記錄key的位置以及長度值
o += copy(data[o:], key) //寫入key
if kt == keyTypeVal {
o += binary.PutUvarint(data[o:], uint64(len(value)))
index.valuePos = o
index.valueLen = len(value)
o += copy(data[o:], value) //寫入value 返回copy的長度
}
b.data = data[:o]
b.index = append(b.index, index) //添加一個batch索引項
b.internalLen += index.keyLen + index.valueLen + 8 //內(nèi)部batch的長度 key value長度再加上8位的標(biāo)識位
}
3.2 grow 擴(kuò)容函數(shù)
func (b *Batch) grow(n int) {
o := len(b.data)
if cap(b.data)-o < n {
div := 1
if len(b.index) > batchGrowRec { //3000
div = len(b.index) / batchGrowRec
}
ndata := make([]byte, o, o+n+o/div)
//當(dāng)batch index很多的時候,擴(kuò)容的擴(kuò)充容量越小
//batch index < batchGrowRec時擴(kuò)充為 2*old cap + n的新長度
copy(ndata, b.data)
b.data = ndata
}
}