tom的mysql查询缓存_Derek解读Bytom源码-持久化存储LevelDB

本文详细解读了Bytom使用LevelDB数据库的操作,包括Set, Get, Delete等操作流程,以及LevelDB的高效性能和单进程特性。重点介绍了查询块过程中的缓存策略和数据存储的前缀管理,展示了数据库接口与状态维护关键部分的代码片段。
摘要由CSDN通过智能技术生成

作者:Derek

简介

本章介绍Derek解读-Bytom源码分析-持久化存储LevelDB

作者使用MacOS操作系统,其他平台也大同小异

Golang Version: 1.8

LevelDB介绍

比原链默认使用leveldb数据库。Leveldb是一个google实现的非常高效的kv数据库。LevelDB是单进程的服务,性能非常之高,在一台4核Q6600的CPU机器上,每秒钟写数据超过40w,而随机读的性能每秒钟超过10w。

由于Leveldb是单进程服务,不能同时有多个进程进行对一个数据库进行读写。同一时间只能有一个进程,或一个进程多并发的方式进行读写。

比原链在数据存储层上存储所有链上地址、资产交易等信息。

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。

accesstoken.db token信息(钱包访问控制权限)

core.db 核心数据库,存储主链相关数据。包括块信息、交易信息、资产信息等

discover.db 分布式网络中端到端的节点信息

trusthistory.db

txdb.db 存储交易相关信息

txfeeds.db 目前比原链代码版本未使用该功能,暂不介绍

wallet.db 本地钱包数据库。存储用户、资产、交易、utox等信息

以上所有数据库都由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

}

BlockExist 根据hash判断区块是否存在

GetBlock 根据hash获取该区块

GetStoreStatus 获取store的存储状态

GetTransactionStatus 根据hash获取该块中所有交易的状态

GetTransactionsUtxo 缓存与输入txs相关的所有utxo

GetUtxo(*bc.Hash) 根据hash获取该块内的所有utxo

LoadBlockIndex 加载块索引,从db中读取所有block header信息并缓存在内存中

SaveBlock 存储块和交易状态

SaveChainStatus 设置主链的状态,当节点第一次启动时,节点会根据key为blockStore的内容判断是否初始化主链。

比原链数据库key前缀

database/leveldb/store.go

var (

blockStoreKey = []byte("blockStore")

blockPrefix = []byte("B:")

blockHeaderPrefix = []byte("BH:")

txStatusPrefix = []byte("BTS:")

)

blockStoreKey 主链状态前缀

blockPrefix 块信息前缀

blockHeaderPrefix 块头信息前缀

txStatusPrefix 交易状态前缀

GetBlock查询块过程分析

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函数总共操作有两步:

从缓存中查询hash值,如果查到则返回

如果为从缓存中查询到则回调fillFn回调函数。fillFn回调函数会将从磁盘上获得到块信息存储到缓存中并返回该块的信息。

fillFn回调函数实际上调取的是database/leveldb/store.go下的GetBlock,它会从磁盘中获取block信息并返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值