以太坊的儲存層技術分析之二:以太坊和LevelDB的介面
LevelDB使用者介面非常簡單,主要就是Put(k,v),Get(k),Delete(k)。以太坊封裝了LevelDB介面,見如下類詳細程式碼:
---------------------------------------------------------------------------------------------
Go版本以太坊客戶端:
github.com/ethereum/go-ethereum/ethdb/database.go
介面函式定義:
type LDBDatabase struct {
fn string // filename for reporting
db *leveldb.DB // LevelDB instance
getTimer gometrics.Timer // Timer for measuring the database get request counts and latencies
putTimer gometrics.Timer // Timer for measuring the database put request counts and latencies
delTimer gometrics.Timer // Timer for measuring the database delete request
missMeter gometrics.Meter // Meter for measuring the missed database get requests
readMeter gometrics.Meter // Meter for measuring the database get request data usage
writeMeter gometrics.Meter // Meter for measuring the database put request data usage
compTimeMeter gometrics.Meter // Meter for measuring the total time spent in database compaction
compReadMeter gometrics.Meter // Meter for measuring the data read during compaction
compWriteMeter gometrics.Meter // Meter for measuring the data written during compaction
quitLock sync.Mutex // Mutex protecting the quit channel access
quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
}
func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error)
func (self *LDBDatabase) Put(key []byte, value []byte) error
func (self *LDBDatabase) Get(key []byte) ([]byte, error)
func (self *LDBDatabase) Delete(key []byte) error
func (db *LDBDatabase) NewBatch() Batch
func (self *LDBDatabase) Close()
---------------------------------------------------------------------------------------------
Java版本以太坊客戶端:
org.ethereum.datasource.leveldb.LevelDbDataSource.java
介面函式定義:
String name; DB db; boolean alive;
// The native LevelDB insert/update/delete are normally thread-safe // However close operation is not thread-safe and may lead to a native crash when accessing a closed DB. // This ReadWriteLock still permits concurrent execution of insert/delete/update operations however blocks them on init/close/delete operations private ReadWriteLock resetDbLock = new ReentrantReadWriteLock();
public boolean isAlive()
public void destroyDB(File fileLocation)
public byte[] get(byte[] key)
public void put(byte[] key, byte[] value)
public void delete(byte[] key)
public void updateBatch(Map<byte[], byte[]> rows)
public void close()
---------------------------------------------------------------------------------------------
以太坊客戶端go語言實現的版本,和java語言實現的版本,在關鍵預設引數上是有區別的,見如下:
表1:Java版本和Go版本關鍵資料庫引數差異
不僅關鍵資料庫引數有差異,java版本和go版本呼叫LevelDB後資料存放的目錄也是不同的。以太坊的儲存層儲存著兩類相對獨立但又有聯絡的資料:區塊鏈資料庫(chainDB)和賬戶狀態資料庫(stateDB)。其中,go版本將區塊鏈資料庫和賬戶狀態資料庫都存放在.ethereum目錄下;而java版本將兩者分開存放,分別放在block目錄下和state目錄下。
區塊鏈資料庫是一個區塊編號和區塊內容對應關係的資料庫;而賬戶狀態資料庫是一個維護鏈中所有賬戶地址和其狀態對應關係的資料庫 , 以賬戶地址為key,以賬戶狀態(包含nonce,餘額,storageRoot,codeHash,見黃皮書4.1)為value。賬戶狀態維護的是賬戶餘額變動歷史和合約賬戶執行歷史,每次餘額變動或合約程式碼被執行,都會生成一條記錄,並被記錄。所有賬戶狀態資料庫的查詢,以賬戶地址為查詢輸入,而所有區塊鏈上的查詢,以區塊編號等作為查詢輸入。技術上,可以理解為按照模組垂直劃分成2個數據庫例項。業務上,可以理解為交易流水賬一個數據庫例項,賬戶分戶賬一個數據庫例項。
另外,以太坊還維護了一個節點資訊的資料庫,go版本的該資料庫在nodes目錄下,java版本的資料庫在peers目錄下,該部分是動態組網時所用,不是區塊鏈本身內容。