gkvdb的介绍及设计

gkvdb是Go语言开发的基于DRH(Deep-Re-Hash)深度哈希分区算法的高性能Key-Value嵌入式数据库,gkvdb是开源的、免费的,基于MIT协议进行发布,项目地址:https://gitee.com/johng/gkvdb。gkvdb的使用方式极其简单,可参考项目地址中的说明进行操作即可。为了保证gkvdb的高性能,除了使用简单可靠的DRH算法外,gkvdb的一些组件也进行了非常优秀的设计,本文主要对gkvdb的一些核心组件进行简单的介绍。

单数据文件

单数据文件设计的目的是为提高在机械磁盘下的随机数据读写性能问题,gkvdb数据库只有4种文件:binlog、xxx.ix、xxx.mt、xxx.db

其中binlog为数据库日志文件,ix为数据表索引文件,mt为数据表元数据文件,db为数据表数据存放文件。ix和mt文件对应DRH算法中的节点,db文件对应DRH中的数据层。

键名最大长度为255字节,键值最大长度为16MB,数据库默认支持的数据库大小为1TB,和数据大小有关系,例如,如果键值对长度为1KB,那么支持的数据量为1024x1024x1024=1073741824,约为11亿。

单数据文件除了便于管理以外,对于提升磁盘IO性能也是极大的。在所有基于磁盘的数据库设计中,影响性能的因素,除了算法设计,往往就是磁盘IO,特别是在访问量大的情况下将会更加明显(当然同一文件的锁操作影响也会比较大)。

binlog日志

binlog是介于接口层与底层数据库文件之间的日志层,用于数据更新到数据持久化之间的缓冲,提升接口层的响应效率,是事务功能的基础,并保证数据库的高可用。所有的更新操作必须先写入binlog文件成功后,才算是成功,写入失败或者只写入了操作的部分数据,都算是操作失败。例如:批量写入100条数据,如果在写入的过程中系统异常断电,造成只写入了99条,那么该批数据都算是写入失败,这批数据将不会被同步到底层数据库文件中。

此外,需要说明的是,gkvdb的binlog和mysql的binlog不同,不会保留所有的操作日志,当binlog成功持久化到底层数据库文件之后,对应的日志内容将会被清除。gkvdb被设计为是一个可嵌入式的单节点KV数据库,保证简单性、实用性及高性能,gkvdb本身不提供主备功能,如果想要支持数据库主备或者多节点的数据库支持,请参考 分布式集群管理软件dister,dister提供了多节点分布式的数据库同步及备份功能,gkvdb是dister的核心数据库组件,是dister的分布式KV数据管理功能的基础。

事务功能

gkvdb的所有数据更新操作,都是按照事务来实现,即使是一条单独的数据写入/删除操作。

当一个事务开始后,所有的事务操作都是基于内存,直到事务Commit之后,才会真实地写入到binlog中;反之,如果执行事务Rollback,那么事务的数据将会在内存中被清除,没有真实地提交到binlog中,也便不会有记录。

binlog日志是事务功能的基础,同时binlog中的日志存放,也是以事务为单位进行存放,日志文件格式如下:

同一个操作的操作列表被包含在一对事务编号中间,也就是说,如果一个事务想要执行成功,必须按照这样的格式写入到binlog中,前后缺少一个字符都不行。当数据异常的情况下(数据不完整),整个事务操作都将会被忽略掉,binlog会自动寻址到下一条事务进行同步。其中,“是否同步”的标志位表示该事务数据是否已经同步到底层数据库文件,如果为0,表示未同步,那么异步线程将会自动将此数据同步到底层数据库文件中,并标识该标志位为1。当所有的binlog数据的同步标志位都为1的时候,binlog数据将会被清除。

数据更新操作图例

空间管理器

在单数据文件的设计中,离不开碎片的存在,碎片是指当磁盘文件中的一段内容被删除,或者内容修改时原有地址空间不足引起地址重新分配时,原有地址的空间没有地方再引用,便成为了碎片(有点类似GC,可以将空间管理器看做基于文件的GC)。gkvdb设计了一个专门的文件空间管理器,来回收和再分配这些碎片,并且gkvdb的空间管理器可以对较小的碎片进行自动地合并,对于较大的碎片按需进行拆分。gkvdb的空间管理器的计算是基于内存的,底层采用了B树进行索引(后期考虑替换为跳表实现),效率相当高。gkvdb数据库在初始化的时候会重新计算碎片,保证文件空间的充分利用。

空间整理器

举个例子:比如删除了若干键值对后,造成了5000MB的碎片,后续继续分配时也无法充分利用完这些碎片,因此这些碎片会继续占据磁盘空间,造成磁盘空间的浪费。

这个时候,就需要空间整理器来处理了,空间整理器相对空间管理器来说是独立的,专注于不同的职责。针对于大型的文件碎片,空间整理器会将数据进行自动迁移,使得文件中的数据靠前紧凑,将碎片往文件末尾迁移,最后进行truncate矫正文件大小,以达到整理碎片的目的。

空间整理器是异步进行的,针对空间大小超过1KB的碎片进行处理。

文件指针池

文件指针池和数据库连接池比较类似,是基于IO复用的一种设计。

一般的文件操作过程是这样的:当文件被打开之后,会创建一个文件指针,在文件操作结束后,文件指针就会被立即关闭掉。

这样的话,在每一次操作或者请求中,对文件的操作都会反复创建/关闭指针,这样产生的问题是:一个是操作效率比较低,另一个是系统的最大文件打开数是有限的,在文件操作频繁/访问量大的情况下,会影响整体系统的执行效率(文件锁,资源竞争),甚至引发一些严重的问题。文件指针池对于同一个文件在异步高并发下的读取性能提升非常大(因为读取不需要锁,每一个协程一个文件指针),但是对于写入性能的提升不大,因为文件写入往往都必须加锁以保证数据写入的顺序性。

在gkvdb中,对于同一个数据文件,会有一个专门的文件指针池,当文件指针使用完毕之后会立即放回到这个池中,下一次使用的时候从池中获取,免去了反复创建/关闭指针的性能开销。如果访问量大的情况下,池中的文件指针不够时,也会创建新的文件指针,使得池的容量不断增加,没有上限。

当然,如果池中的文件指针在一段时间闲置后,会被自动关闭掉,默认闲置时间为60秒。

 

 

 

3 Replies to “gkvdb的介绍及设计”

    1. github项目已同步,地址:https://github.com/johng-cn,后续项目改动将会同时提交到gitee和github上。

Leave a Reply

Your email address will not be published.