Talent Plan TinyKV Project1 StandaloneKV
2021/12/6 23:20:25
本文主要是介绍Talent Plan TinyKV Project1 StandaloneKV,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
6.824做完了,代码写的乱糟糟,想着重写一遍,整理下思路,后来发现了tinykv,相比于6.824还多了个事务,就准备把tinykv也做一下。
文档翻译
在本项目中,实现一个单机的、支持Column Family的KV存储服务。Column Family表示Key的命名空间,不同Column Family间可以有相同的Key存在。
服务提供Put/Delete/Get/Scan四种基本操作。
本项目可以拆解为两步去实现:
- 实现单机的存储引擎。
- 实现原生的服务接口。
tinykvpb.proto和kvrpcpb.proto定义了rpc接口和请求响应消息,kv/main.go中注册了RPC服务。
proto文件由protocol-buffer生成,不需要修改。
Server底层由Storage抽象类支持,需要为StandAloneStorage实现Storage的所有接口。
type Storage interface { // Other stuffs Write(ctx *kvrpcpb.Context, batch []Modify) error Reader(ctx *kvrpcpb.Context) (StorageReader, error) } 复制代码
Storage底层由badger(类似LevelDB或RocksDB的存储引擎)支持,StandAloneStorage即badger的简单封装。
目前不需要考虑kvrpcpb.Context的涵义。
一些提示。
- 使用badger.Txn来实现Reader()函数,即badger提供的事务支持。
- badger不支持Column Family,engine_util提供了一系列函数,利用前缀来实现Column Family,使用它们来实现Write()函数。
- 使用Connor1996/badger而不是dgraph-io/badger。
- 使用Discard()关闭badger.Txn,这之前需要关闭所有迭代器。
最后就是基于Storage实现RawGet/RawScan/RawPut/RawDelete,完成后通过make project1进行测试。
StandAloneStorage
engine_util封装了badger的接口,StandAloneStorage就是要在engine_util的基础上再封装一层。因此StandAloneStorage结构也很简单。
type StandAloneStorage struct { engine *engine_util.Engines } 复制代码
StandAloneStorage为抽象类Storage的具体实现,因此需要实现Write和Reader两个函数,函数签名如下。
func (s *StandAloneStorage) Write(ctx *kvrpcpb.Context, batch []storage.Modify) error func (s *StandAloneStorage) Reader(ctx *kvrpcpb.Context) (storage.StorageReader, error) 复制代码
Project1中,还用不到kvrpcpb.Context,storage.Modify对应Put或Delete两个写操作,storage.StorageReader同样是一个抽象类。
Reader
type StorageReader interface { GetCF(cf string, key []byte) ([]byte, error) IterCF(cf string) engine_util.DBIterator Close() } 复制代码
可以看到StorageReader的两个函数,屏蔽了事务,简化了接口。实现StorageReader需要用到engine_util提供的GetCFFromTxn和NewCFIterator两个函数。
func GetCFFromTxn(txn *badger.Txn, cf string, key []byte) (val []byte, err error) func NewCFIterator(cf string, txn *badger.Txn) *BadgerIterator 复制代码
获取badger.Txn的函数engine_util并未给出,需要直接调用badger.DB.NewTransaction函数。
func (db *DB) NewTransaction(update bool) *Txn 复制代码
update为真表示Put/Delete两个写操作,为假表示Get/Scan两个读操作。
Write
type Modify struct { Data interface{} } type Put struct { Key []byte Value []byte Cf string } type Delete struct { Key []byte Cf string } 复制代码
Modify表示一个Put/Delete操作,Write中通过断言确定是Put还是Delete,进而调用engine_util提供的PutCF和DeleteCF两个函数。
func PutCF(engine *badger.DB, cf string, key []byte, val []byte) error func DeleteCF(engine *badger.DB, cf string, key []byte) error 复制代码
其实这两个函数内部实现依旧是用了事务的,相比于Reader,Write对事务的屏蔽并没有让我们自己实现。
Server
StandAloneStorage是Storage的实现,Server还要在他之上,后续还会有RaftStorage,这样Server底层的存储引擎就是可以更换的,分别对应单机的和分布式的存储服务。
type Server struct { storage storage.Storage } 复制代码
Project1要求我们为Server实现原生的服务接口,这部分在raw_api.go中,需要实现的函数的签名如下。
func (server *Server) RawGet(_ context.Context, req *kvrpcpb.RawGetRequest) (*kvrpcpb.RawGetResponse, error) func (server *Server) RawPut(_ context.Context, req *kvrpcpb.RawPutRequest) (*kvrpcpb.RawPutResponse, error) func (server *Server) RawDelete(_ context.Context, req *kvrpcpb.RawDeleteRequest) (*kvrpcpb.RawDeleteResponse, error) func (server *Server) RawScan(_ context.Context, req *kvrpcpb.RawScanRequest) (*kvrpcpb.RawScanResponse, error) 复制代码
实现这四个接口,需要调用之前实现的Write、Reader.GetCF、Reader.IterCF这三个函数,一个可能不太理解的是Scan函数,因为badgerDB内部是有序存储的,所以可以根据一个Key,向后获取N个Key,RawScanRequest.Limit就是N的涵义。
engine_util封装了DBIterator,提供了Item、Valid、Next、Seek用于实现RawScan函数。
Column Family
可能你还没理解CF是什么,其实本质就是字符串而已,用作Key的前缀,起到命名空间的作用。
const ( CfDefault string = "default" CfWrite string = "write" CfLock string = "lock" ) func KeyWithCF(cf string, key []byte) []byte { return append([]byte(cf+"_"), key...) } 复制代码
例如默认的CF为"default",那么该CF下的名为"apple"的Key,实际存储为"default_apple"这个字符串。
这篇关于Talent Plan TinyKV Project1 StandaloneKV的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享