記憶體和磁碟設計

RainbowMagic發表於2024-06-10

記憶體設計

根據之前論文之中所寫,我們在記憶體中需要儲存一份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()
}

相關文章