内存数据库经我们经常用到,例如Redis,那么如何从零实现一个内存数据库呢,本文旨在介绍如何使用Golang编写一个KV内存数据库MossDB。

特性

MossDB是一个纯Golang编写、可嵌入的、键值型内存数据库,包含以下特性

  • 可持久化,类似Redis AOF(Append only Log)
  • 支持事务
  • 支持近实时的TTL(Time to Live), 可以实现毫秒级的过期删除
  • 前缀搜索
  • Watch接口,可以监听某个键值的内容变化,类似etcd的Watch
  • 多后端存储,目前支持HashMap和RadixTree

命名由来

Moss

苔花如米小,也学牡丹开

MossDB虽小,但五脏俱全,也支持了很多重要功能。另外,巧合的是《流浪地球2》中的超级计算机550W名字就是Moss。

架构

内存数据库虽然使用简单,实现起来却有很多细节,Golang目前也存在不少优秀的开源内存数据库,比如buntdb、go-memdb,在编写MossDB过程中也借鉴了一些它们的特性。

MossDB的架构如图:

自上往下分为:

  • 接口层,提供API接受用户请求
  • 核心层,实现事务、过期删除、Watch等功能
  • 存储层,提供KV的后端存储以及增删改查
  • 持久化层,使用AOL持久化即每次修改操作都会持久化到磁盘Log中

快速开始

go get

go get github.com/qingwave/mossdb

MossDB提供了易用的API,可以方便地进行数据处理,下面的示例代码展示了如何使用MossDB:

更多示例见源码

具体实现

从下往上分别介绍下MossDB如何设计与实现,以及相关的细节。

AOF持久化

AOF源于Redis提供两种持久化技术,另外一种是RDB,AOF是指在每次写操作后,将该操作序列化后追加到文件中,重启时重放文件中的对应操作,从而达到持久化的目的。其实现简单,用在MossDB是一个不错的选择,但需要注意的是AOF缺点同样明显,如果文件较大,每次重启会花费较多时间。

Redis的AOF是一种后写式日志,先写内存直接返回给用户,再写磁盘文件持久化,可以保证其高性能,但如果中途宕机会丢失数据。MossDB中的AOF采用了WAL(预写式日志)实现,先写Log再写内存,用来保证数据不会丢失,从而可以进一步实现事务。

fsyncfsyncfsync
varint

对应编码后的二进制格式为:

|------------------------------------------------------------|
| Op | KeySize | ValSize | Timestamp | TTL | Key    | Val    |
|------------------------------------------------------------|
| 2  | 4       | 4       | 8         | 8   | []byte | []byte |
|------------------------------------------------------------|

binary.BigEndian.PutUint16binary.BigEndian.Uint16

存储引擎

MossDB提供了存储接口,只要实现了此接口都可以作为其后端存储

map
ListWatch

事务实现

要实现事务必须要保证其ACID特性,MossDB的事务定义如下:

MossDB中一次事务的流程主要包含以下几个步骤:

  • 首先加锁,保证其数据一致性
  • 对于写操作,生成Commits和Undo Records,然后写入内存;读操作则直接执行
  • 提交阶段,将Commits持久化到WAL中;若写入失败,则删除已写入数据;成功则设置数据的其他属性(TTL, Watch等)
  • 若中间发生错误,执行回滚操作,将Undo Records的记录执行
  • 事务完成,释放锁

Watch

由于工作中经常使用Kubernetes,对于其Watch接口印象深刻,通过Watch来充当其事件总线,保证其声明式对象的管理。Kubernetes的Watch底层由etcd实现,MossDB也实现了类似的功能。

Watch的定义如下:

watchers前缀Key

调用Watch会返回一个Channel,用户只需要监听Channel即可

TTL

过期删除再很多场景很有用,比如验证码过期、订单未支付关闭等。MossDB采用时间堆来实现精确的Key过期策略,具体原理可以参考之前的文章Golang分布式应用之定时任务,在查询操作时也会检查Key是否过期,如果过期则直接返回空数据。配合Watch操作可以精确管理数据的生命周期。

总结

至此,MossDB的实现细节已经分析完成,支持了事务、持久化、Watch与过期删除等特性,后续可能会支持HTTP API、存储快照等功能。

所有代码见https://github.com/qingwave/mossdb,欢迎批评指正以及Star。