1.Batch的日常使用
batch的日常使用比較簡(jiǎn)單,使用Batch是提高leveldb寫入性能的一個(gè)關(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)部擁有一個(gè)batch的對(duì)象池,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對(duì)象的開(kāi)銷,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使用一個(gè)byte數(shù)組存儲(chǔ)具體數(shù)據(jù),這里data存儲(chǔ)實(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的起始位置及其長(zhǎng)度
valuePos, valueLen int //value的起始長(zhǎng)度以及其長(zhǎng)度
}
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部分存儲(chǔ)key和value的長(zhǎng)度,主要是為了decodeBatch的方便。
func (b *Batch) appendRec(kt keyType, key, value []byte) {
//1.計(jì)算存儲(chǔ)kt keylen key valuelen value的data所需要的長(zhǎng)度
n := 1 + binary.MaxVarintLen32 + len(key)
if kt == keyTypeVal {
n += binary.MaxVarintLen32 + len(value)
}
//容量不足時(shí)及時(shí)擴(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)識(shí)位 占8位
o++
//序列化key值的長(zhǎng)度并存儲(chǔ)到data內(nèi),key最長(zhǎng)不超過(guò)32位
o += binary.PutUvarint(data[o:], uint64(len(key)))
index.keyPos = o
index.keyLen = len(key) //記錄key的位置以及長(zhǎng)度值
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的長(zhǎng)度
}
b.data = data[:o]
b.index = append(b.index, index) //添加一個(gè)batch索引項(xiàng)
b.internalLen += index.keyLen + index.valueLen + 8 //內(nèi)部batch的長(zhǎng)度 key value長(zhǎng)度再加上8位的標(biāo)識(shí)位
}
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很多的時(shí)候,擴(kuò)容的擴(kuò)充容量越小
//batch index < batchGrowRec時(shí)擴(kuò)充為 2*old cap + n的新長(zhǎng)度
copy(ndata, b.data)
b.data = ndata
}
}