源码位置:ethereum/go-ethereum/ethdb
.
├── database.go // leveldb封装
├── database_js.go // 暂不支持
├── database_js_test.go
├── database_test.go // 测试用例
├── interface.go // 数据库接口定义
├── memory_database.go // 内存数据库,用于测试
├── table.go // 表操作封装
└── table_batch.go // 表操作封装
以太坊的数据存储使用在eveldb,nosql key-value数据库:
leveldb特点:
以太坊测试使用内存数据库,源码实现memory_database.go中。封装了一个map,然后使用锁来进行安全访问。
type MemDatabase struct {
db map[string][]byte
// 使用lock来进行安全访问
lock sync.RWMutex
}
// 新建一个内存数据库
func NewMemDatabaseWithCap(size int) *MemDatabase {
return &MemDatabase{
db: make(map[string][]byte, size),
}
}
// 使用锁来控制安全访问,其他操作同理
func (db *MemDatabase) Put(key []byte, value []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
db.db[string(key)] = common.CopyBytes(value)
return nil
}
func (db *MemDatabase) Has(key []byte) (bool, error) {
...
}
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
...
}
func (db *MemDatabase) Len() int { return len(db.db) }
bath批量操作同理
type memBatch struct {
db *MemDatabase
writes []kv
size int
}
定义了数据库的操作 增删改查 关闭等
package ethdb
// 批处理最大数据.
const IdealBatchSize = 100 * 1024
//批处理和常规数据库都支持的数据库写操作
type Putter interface {
Put(key []byte, value []byte) error
}
//批处理和常规数据库都支持的数据库删除操作.
type Deleter interface {
Delete(key []byte) error
}
// Database wraps all database operations. All methods are safe for concurrent use.
// 数据库接口,增删改查、关闭、批处理。线程安全的
type Database interface {
Putter
Deleter
Get(key []byte) ([]byte, error)
Has(key []byte) (bool, error)
Close()
NewBatch() Batch
}
// Batch is a write-only database that commits changes to its host database
// when Write is called. Batch cannot be used concurrently.
// 不能多线程使用
type Batch interface {
Putter
Deleter
ValueSize() int // amount of data in the batch
Write() error
// Reset resets the batch for reuse
Reset()
}
封装levelDB,leveldb源码在https://github.com/syndtr/goleveldb。以太坊使用meter系统来进行数据库的性能统计
type LDBDatabase struct {
fn string // filename for reporting
db *leveldb.DB // LevelDB instance
// metter用户测试计算数据
compTimeMeter metrics.Meter // Meter for measuring the total time spent in database //安全退出
quitLock sync.Mutex // Mutex protecting the quit channel access
// 在关闭数据库之前停止meter
quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
// 日志
log log.Logger // Contextual logger tracking the database path
}
// NewLDBDatabase returns a LevelDB wrapped object.
// 返回一个db实例 file db文件
func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
logger := log.New("database", file)
// Ensure we have some minimal caching and file guarantees
if cache < 16 {
cache = 16
}
if handles < 16 {
handles = 16
}
logger.Info("Allocated cache and file handles", "cache", common.StorageSize(cache*1024*1024), "handles", handles)
// Open the db and recover any potential corruptions
// 打开数据库,并回复数据
db, err := leveldb.OpenFile(file, &opt.Options{
OpenFilesCacheCapacity: handles,
BlockCacheCapacity: cache / 2 * opt.MiB,
WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
Filter: filter.NewBloomFilter(10),
})
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
db, err = leveldb.RecoverFile(file, nil)
}
// (Re)check for errors and abort if opening of the db failed
// 如果数据库打开失败,返回nil
if err != nil {
return nil, err
}
return &LDBDatabase{
fn: file,
db: db,
log: logger,
}, nil
}
leveldb内部支持多线程访问,所以这里可以直接调用,并不需要lock控制。
// 增
func (db *LDBDatabase) Put(key []byte, value []byte) error {
return db.db.Put(key, value, nil)
}
// 查找数据是否存在
func (db *LDBDatabase) Has(key []byte) (bool, error) {
return db.db.Has(key, nil)
}
//查
func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
dat, err := db.db.Get(key, nil)
if err != nil {
return nil, err
}
return dat, nil
}
// 删除
func (db *LDBDatabase) Delete(key []byte) error {
return db.db.Delete(key, nil)
}
// 迭代
func (db *LDBDatabase) NewIterator() iterator.Iterator {
return db.db.NewIterator(nil, nil)
}
根据传入的前缀 初始化收集器,创建quitchain
// Meter configures the database metrics collectors and
func (db *LDBDatabase) Meter(prefix string) {
// Initialize all the metrics collector at the requested prefix
db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil)
db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil)
db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil)
db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil)
db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil)
db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil)
db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil)
// Create a quit channel for the periodic collector and run it
db.quitLock.Lock()
db.quitChan = make(chan chan error)
db.quitLock.Unlock()
// 刷新时间3秒
go db.meter(3 * time.Second)
}
// 定期统计信息公布到metrics子系统
// 表统计样例
// This is how a stats table look like (currently):
// Compactions
// Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)
// -------+------------+---------------+---------------+---------------+---------------
// 0 | 0 | 0.00000 | 1.27969 | 0.00000 | 12.31098
// 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294
// 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884
// 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000
//
// This is how the write delay look like (currently):
// 写入统计
// DelayN:5 Delay:406.604657ms Paused: false
//
// This is how the iostats look like (currently):
// io统计
// Read(MB):3895.04860 Write(MB):3654.64712