6.824做完了,代码写的乱糟糟,想着重写一遍,整理下思路,后来发现了tinykv,相比于6.824还多了个事务,就准备把tinykv也做一下。

文档翻译

在本项目中,实现一个单机的、支持Column Family的KV存储服务。Column Family表示Key的命名空间,不同Column Family间可以有相同的Key存在。

服务提供Put/Delete/Get/Scan四种基本操作。

本项目可以拆解为两步去实现:

  1. 实现单机的存储引擎。
  2. 实现原生的服务接口。

tinykvpb.proto和kvrpcpb.proto定义了rpc接口和请求响应消息,kv/main.go中注册了RPC服务。

proto文件由protocol-buffer生成,不需要修改。

Server底层由Storage抽象类支持,需要为StandAloneStorage实现Storage的所有接口。

type Storage interface {// Other stuffsWrite(ctx *kvrpcpb.Context, batch []Modify) errorReader(ctx *kvrpcpb.Context) (StorageReader, error)
}
复制代码

Storage底层由badger(类似LevelDB或RocksDB的存储引擎)支持,StandAloneStorage即badger的简单封装。

目前不需要考虑kvrpcpb.Context的涵义。

一些提示。

  1. 使用badger.Txn来实现Reader()函数,即badger提供的事务支持。
  2. badger不支持Column Family,engine_util提供了一系列函数,利用前缀来实现Column Family,使用它们来实现Write()函数。
  3. 使用Connor1996/badger而不是dgraph-io/badger。
  4. 使用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.DBIteratorClose()
}
复制代码

可以看到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   []byteValue []byteCf    string
}type Delete struct {Key []byteCf  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相关推荐

  1. Talent Plan 学习营初体验:交流+坚持 开源协作课程学习的不二路径

    Talent Plan 是 PingCAP 联合华东师范大学.华中科技大学.中国科学技术大学.武汉大学和神州数码面向高校和工程师的未来数据库内核人才培养计划.通过结业考核的学员将获得官方认证的证书,并 ...

  2. 暑期特别企划 | 快来接收 PingCAP Talent Plan 的小惊喜!

    PingCAP Talent Plan 学习通道自开通以来,收获了海内外小伙伴的密切关注,有 100 余名小伙伴参与到线上课程的学习中,第二期线下课程也于 5 月中旬圆满落幕.结合大家的意见,我们对 ...

  3. 对于LSM Tree写放大问题的一些浅薄学习

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可. 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权. 文章目录 引言 性能评估 优化方案 ...

  4. 【大总结2】大学两年,写了这篇几十万字的干货总结

    本文是我大学两年知识的总结.涵盖数据结构.算法.语言基础.操作系统.关系数据库.NOSQL.网络/前端/项目基础知识.安全和测试.框架的学习.中间件和工具.设计模式和框架原理.我推荐的资料.我的建议 ...

  5. 【Rust日报】 2019-04-04

    成功故事:Rust在企业领域的应用 #zalando 本文描述了zalando公司从Scala转向Rust的成功故事. 缘起: 2016年该文作者作为Scala开发者加入了Zalando公司.半年后, ...

  6. 黄东旭当选 CCF 数据库专业委员会、开源发展委员会、大数据专家委员会执行委员

    近日,CCF 数据库专业委员会.大数据专家委员会分别公布了执行委员最新名单,PingCAP 联合创始人兼 CTO 黄东旭成功当选数据库专业委员会.大数据专家委员会执行委员.此外,CCF 开源发展委员会 ...

  7. GitHub 标星超 26600,TiDB 社区运营的道与术!

    采访嘉宾 | 姚维 作者 | 夕颜 出品 | AI科技大本营(ID:rgznai100) 年前,企业级开源软件服务供应商 PingCAP 宣布完成 2.7 亿美元的 D 轮融资,创造了全球数据库历史新 ...

  8. 程序媛眼中的 PingCAP:无法抗拒的五大吸引力 | PingCAP 招聘季

    今早开电脑,看到老板深夜的留言瑟瑟发抖- 老板:大妹子啊,最近忙不? (俺是左思右想搔头摸耳揣摩老板啥意思,想来老板是耿直 Boy, 这次居然没有直接说要干啥,自行脑补了 N 多场景,最后一咬牙还是如 ...

  9. 算法工程师-机器学习-数据科学家面试准备[外企和国外公司、春招、秋招]

    该仓库记录了如何准备机器学习工程师面试,尤其海外与外企岗位 代码地址: https://github.com/LongxingTan/mle-interview 申请岗位:软件工程师-机器学习方向.机 ...

最新文章

  1. 以Dapper、Zipkin和LightStep [x]PM为例阐述分布式跟踪的过去、现在和未来
  2. java servlet 作用_servlet的作用,servlet有什么用?
  3. 地图相关应用系统部署到现场报错原因汇总
  4. java itextsharp_使用 c#中的 itextsharp 以 pdf 格式填充 xml
  5. [IE编程] IE的Killbit 技术详解
  6. java itextpdf使用HTML模板生成pdf文件,并设置table
  7. 79-Spark Standalone架构设计要点分析
  8. TCP慢开始与拥塞避免
  9. 保障Web服务的安全
  10. 什么是并口硬盘和串口硬盘?有何区别?
  11. 自动化测试方案设计和实现
  12. JAVA SSM毕业设计——基于SSM的校园二手交易平台的设计与实现
  13. 数据的种类(结构化数据、非结构化数据、半结构化数据)
  14. C语言左右对齐、占位
  15. iostat命令参数详解
  16. QT编写USB PRINTER驱动
  17. 拉钩作业:Bikeshare数据集 预测共享单车骑行量
  18. java可以制作动画么_java – 如何为陀螺制作动画?
  19. 浅谈对软件企业OEM的理解
  20. 基于Python的Django框架的二手物品交易平台的设计与实现

热门文章

  1. 判断一个坐标点是否在不规则多边形内部的算法
  2. java幸运抽奖代码实验结论_java-第四章-实现幸运抽奖功能
  3. 数据结构— —单链表
  4. 实验吧-杂项-异性相吸(异或加密)
  5. 刷题笔记之十一 (计算字符串的编辑距离+微信红包+年终奖+迷宫问题+星际密码+数根)
  6. 2019牛客暑期多校训练营(第一场)E.ABBA
  7. 最新金蝶K3 wise15.1 金蝶K3 15.0 最新金蝶K3 V15.1 金蝶K3 15.1 WISE15.1 金蝶ERP K3行政事业15.1 K3行政事业12.1 金蝶医疗HRP 金蝶SHR
  8. 数据库设计思想深究----Mysql(图文)
  9. 2018巅峰极客writeup(Misc)
  10. c语言五子棋键盘操控,c语言写的鼠标操作的五子棋游戏,欢迎观赏!