做者:Derekgit
Github地址:https://github.com/Bytom/bytomgithub
Gitee地址:https://gitee.com/BytomBlockc...數據庫
本章介紹Derek解讀-Bytom源碼分析-持久化存儲LevelDB緩存
做者使用MacOS操做系統,其餘平臺也大同小異Golang Version: 1.8網絡
比原鏈默認使用leveldb數據庫。Leveldb是一個google實現的很是高效的kv數據庫。LevelDB是單進程的服務,性能很是之高,在一臺4核Q6600的CPU機器上,每秒鐘寫數據超過40w,而隨機讀的性能每秒鐘超過10w。併發
因爲Leveldb是單進程服務,不能同時有多個進程進行對一個數據庫進行讀寫。同一時間只能有一個進程,或一個進程多併發的方式進行讀寫。
比原鏈在數據存儲層上存儲全部鏈上地址、資產交易等信息。分佈式
LevelDB是google開發的一個高性能K/V存儲,本節咱們介紹下LevelDB如何對LevelDB增刪改查。函數
package main import ( "fmt" dbm "github.com/tendermint/tmlibs/db" ) var ( Key = "TESTKEY" LevelDBDir = "/tmp/data" ) func main() { db := dbm.NewDB("test", "leveldb", LevelDBDir) defer db.Close() db.Set([]byte(Key), []byte("This is a test.")) value := db.Get([]byte(Key)) if value == nil { return } fmt.Printf("key:%v, value:%v\n", Key, string(value)) db.Delete([]byte(Key)) } // Output // key:TESTKEY, value:This is a test.
以上Output是執行該程序獲得的輸出結果。源碼分析
該程序對leveld進行了增刪改查操做。dbm.NewDB獲得db對象,在/tmp/data目錄下會生成一個叫test.db的目錄。該目錄存放該數據庫的全部數據。
db.Set 設置key的value值,key不存在則新建,key存在則修改。
db.Get 獲得key中value數據。
db.Delete 刪除key及value的數據。性能
默認狀況下,數據存儲目錄在--home參數下的data目錄。以Darwin平臺爲例,默認數據庫存儲在 $HOME/Library/Bytom/data。
以上全部數據庫都由database模塊管理
在比原鏈中數據持久化存儲由database模塊管理,可是持久化相關接口在protocol/store.go中
type Store interface { BlockExist(*bc.Hash) bool GetBlock(*bc.Hash) (*types.Block, error) GetStoreStatus() *BlockStoreState GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) LoadBlockIndex() (*state.BlockIndex, error) SaveBlock(*types.Block, *bc.TransactionStatus) error SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint) error }
database/leveldb/store.go
var ( blockStoreKey = []byte("blockStore") blockPrefix = []byte("B:") blockHeaderPrefix = []byte("BH:") txStatusPrefix = []byte("BTS:") )
database/leveldb/store.go
func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) { return s.cache.lookup(hash) }
database/leveldb/cache.go
func (c *blockCache) lookup(hash *bc.Hash) (*types.Block, error) { if b, ok := c.get(hash); ok { return b, nil } block, err := c.single.Do(hash.String(), func() (interface{}, error) { b := c.fillFn(hash) if b == nil { return nil, fmt.Errorf("There are no block with given hash %s", hash.String()) } c.add(b) return b, nil }) if err != nil { return nil, err } return block.(*types.Block), nil }
GetBlock函數最終會執行lookup函數。lookup函數總共操做有兩步:
fillFn回調函數實際上調取的是database/leveldb/store.go下的GetBlock,它會從磁盤中獲取block信息並返回。