内存和磁盘设计

内存设计

根据之前论文之中所写,我们在内存中需要存储一份key所对应value的映射位置便于以后读出,在内存中存储的数据结构有许多,例如hash、跳表和b tree等。 在教程中我们使用的是b tree便于以后很方便的遍历key。
数据结构的话直接拿现成的包直接用就行了
btree库: https://github.com/google/btree

首先定义一个数据结构用于表示key存储在文件的位置信息

// LogRecordPos 数据内存索引信息 主要是根据key找到指定文件的指定位置读取指定数据
type LogRecordPos struct {
	FileId uint32 // 文件id
	Pos    int64  // 数据偏移
}

然后根据面向对象的思想定义一个接口,该接口用于操作内存索引,如果之后需要实现其他数据结构,直接实现该接口就行了。

type Indexer interface {
	Put(key []byte, pos *data.LogRecordPos) bool // 设置值
	Get(key []byte) *data.LogRecordPos           // 获取数据的位置信息
	Delete(key []byte) bool                      // 删除kv数据
}

// Item 表示 btree中的数据结构,需要实现了该type才能在Btree中添加数据
type Item struct {
	key []byte
	pos *data.LogRecordPos
}

func (item *Item) Less(than btree.Item) bool {
	// (*Item).key表示断言 如果than实现了btree.Item那么就转换成Item
	return bytes.Compare(item.key, than.(*Item).key) == -1
}

采用Btree数据结构实现了Indexer接口

type Btree struct {
	tree *btree.BTree
	lock *sync.RWMutex
}

func NewBtree() *Btree {
	return &Btree{
		tree: btree.New(64),
		lock: new(sync.RWMutex),
	}
}

func (btree *Btree) Put(key []byte, pos *data.LogRecordPos) bool {
	btree.lock.Lock()
	item := &Item{
		key: key,
		pos: pos,
	}
	btree.tree.ReplaceOrInsert(item)
	btree.lock.Unlock()

	return true
}

func (btree *Btree) Get(key []byte) *data.LogRecordPos {
	item := &Item{key: key}
	getItem := btree.tree.Get(item)

	if getItem == nil {
		return nil
	}

	return getItem.(*Item).pos
}

func (btree *Btree) 	Delete(key []byte) bool {
	btree.lock.Lock()

	item := &Item{
		key: key,
	}
	btree.tree.Delete(item)

	btree.lock.Unlock()

	return true
}

磁盘

磁盘一样使用一个抽象的接口,如果以后需要实现自己的文件引擎直接实现该接口就行。

type IOManagement interface {
	// Read 文件读取 读取的内容会存储到buffer中并返回读取字节数
	Read(offset int64, buffer []byte) (int, error)
	// Write 写数据到文件中 buffer表示写入内容 返回写入字节数
	Write(buffer []byte) (int, error)

	// Sync 将缓存区内容同步到文件中
	Sync() error

	// Close 关闭文件
	Close() error
}

使用最简单的go的库文件即可。


type FileIO struct {
	file *os.File
}

func CreateFileIo(filePath string) (*FileIO, error) {
	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return nil, err
	}

	return &FileIO{
		file: file,
	}, nil
}

func (fileIo *FileIO) Read(offset int64, buffer []byte) (int, error) {
	_, err := fileIo.file.Seek(offset, 0)
	if err != nil {
		return 0, err
	}

	return fileIo.file.Read(buffer)
}

func (fileIo *FileIO) Write(buffer []byte) (int, error) {
	return fileIo.file.Write(buffer)
}

func (fileIo *FileIO) Sync() error {
	return fileIo.file.Sync()
}

func (fileIo *FileIO) Close() error {
	return fileIo.file.Close()
}

磁盘数据读取逻辑如下图所示,首先去内存索引中去找到指定key所存储的value数据所在的文件位置,之后根据数据id以及数据存储文件所在偏移读取指定位置信息并进行返回:
image

posted @ 2024-06-10 12:49  RainbowMagic  阅读(8)  评论(0编辑  收藏  举报