基于mmap的KV数据存储实现(一)概述

原创 2015年11月18日 22:36:08

前言

工作中遇到一些对kv存储的需求,比如推荐系统中需要存储一个商品id对应的相似商品id的 list,或者是一个用户的浏览过的商品id的list,这就需要一个键值对去存储。本文描述的存储基于这个需求简单的实现了一个版本,实际工作中要复杂的多,为了让读者便于理解,就基于这个分析一个主键为string的key,value是id的list的存储实现。本存储用java实现,底层采用mmap的方式,可以实现落盘,并且性能优秀。

存储系统结构

  • index
    数据的索引,每个key都会在index中记录它对应的value存储的位置。
  • block
    value实际存储的位置。
  • table
    一个完整的数据表,包含了block,index以及config。
  • config
    一些配置信息,记录比如当前使用的是哪一个索引。
  • log
    记录每条数据的日志信息

整个系统由以上几个主要模块构成,每写一条数据如下图所示。
这里写图片描述
如上图所示,如果有一个写入数据(key,value)的请求,首先会把这一条记录追加写在日志文件里,然后在通过配置信息选取一个索引(index)文件里,从索引文件里找到这个数据的key的记录,如果没有记录,新建一条记录,这个记录就是这个key的value在block中的位置,如果索引文件有这个记录,可以获取这个位置,然后在block的对应位置写下这个数据,一次写入请求完成。
图中的模块index部分分有index0和index1,存在两个的原因是用于在系统扩容时,需要重新建立索引,可以在两个索引中切换。block的部分其实是有许多块的,可以理解为这些小的block快组成一个大得Block,每个block的大小是固定的一个值,不够就会新增一个block的块,图中如果不够会新增一个block5,。当然事实上index0或者index1本身也是有许多数据块组成的,但这些数据块整体上是属于一份索引,也就是说index0或者index1本身是包含所有索引信息的,设置两个只是用于扩容重建的时候。而block部分实际上读者可以理解为就是一个整体就是大的Block。就相当于index0对应整个Block,重建索引时索引信息切换到index1,此时index1对应Block,这样做的好处是Block只需要存一份数据,而且index扩容和Block的扩容可以分离。
之后的文章会对每一个模块的实现进行详细分析。

设计要点

底层读写文件

如何将数据落盘有很多种做法,简单的介绍两种

  • 数据在内存积累到一定值之后后台另启一个线程存到文件,可以理解为定时将数据刷入磁盘。
  • 写日志文件,这种方式就是当系统挂了或者需要重启时通过遍历操作日志重新将操作进行模拟写入,恢复原先数据。

本存储系统采用的是借助操作系统的mmap功能,将刷磁盘的工作交给操作系统管理。mmap可以将一个文件映射到内存,进程可以直接读写内存,比普通写文件性能快很多(关于mmap建议读者自行去了解)。有了这个工具,这个刷磁盘的操作变得十分容易,性能也有保障,唯一的缺点就是因为mmap是操作系统控制刷到磁盘的,并不是实时的,在系统遇到问题时挂了会造成数据丢失,索性这个系统还有日志信息,从而也能实现恢复数据功能。
补充一个常识,内存读写最快,磁盘中顺序读写比随机读写快,所以index 和 block部分都采用mmap的方式,并且是随机读写的,log日志不需要随机读写,可以采用直接追加文件的顺序读写方式。
采用mmap管理之后,仍旧会存在一些磁盘整理的问题,比如index扩容,block扩容,或者数据被删除之后有很多碎片等等问题,也将在之后的文章中细讲。

并发问题

存储系统性能要好必然面临着一些多线程的问题。很多系统会采用读写都加锁,这样会导致性能有很大的瓶颈。由于需求是读的频率远高于写的频率,所以系统最初的时候采用了读写锁的方式,即在写的时候只能是一个线程在写,读的时候可以多线程读。后来发现其实性能也不好,因为写的频率并不是非常低,最后采用了写的时候也可以读,但是写的时候不能有另一个线程再写。这个就会牵涉到一个并发问题,正在写一个key的时候,有线程正好读这个key的值,这样会导致数据错误,解决办法简单来讲是版本号check或者是双buffer切换,之后的文章会详细分析。

数据结构与算法

本存储用到许多数据结构和算法,在index部分会用到hash算法,包括一些数论分析,string的hash。在block中会用到类似内存分配的算法,treap的应用等,也会在之后做详细分析。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android中通过程序获取SIM Card的信息

在Android中通过程序获取SIM Card的信息并不是很困难,以下一段代码希望!public String readSIMCard() { TelephonyManager...

[转]Java文件映射[mmap]全接触

转自: http://site.douban.com/161134/widget/articles/8506170/article/18487141/ ------------------------...

为何要在Java中使用内存映射文件(Memory Mapped File)或者MappedByteBuffer

尽管从JDK 1.4版本开始,Java内存映射文件(Memory Mapped Files)就已经在java.nio包中,但它对很多程序开发者来说仍然是一个相当新的概念。引入NIO后,Java IO已...

Java文件映射[Mmap]揭秘

Java文件映射[mmap]揭秘 前言相信现在做Java的人没有人不用NIO来进行IO相关的操作了吧。这个新的IO类库[虽然现在已经不新了]为我们带来了基于块的IO处理方式,通过预定义的Buffer,...
  • kabini
  • kabini
  • 2009-06-21 15:52
  • 8797

Java mmap

作为NIO的一个重要的功能,Mmap方法为我们提供了将文件的部分或全部映射到内存地址空间的能力,同当这块内存区域被写入数据之后[dirty],操作系统会用一定的算法把这些数据写入到文件中[这一过程ja...

理解java中的mmap

jdk中的MappedByteBuffer非常类似linux中的mmap将文件映射到虚拟内存,这样可以利用操作系统自带的虚拟内存实现来提高io效率, 很多文档中提到这一点,具体大家可以自行测试。Map...
  • pwlazy
  • pwlazy
  • 2012-03-19 18:35
  • 6780

linux伙伴系统

前段时间由于实验室相关项目的需要+OS课的presentation,看了linux2.6.24内核伙伴系统的源码。写一下总结。 引言     伙伴系统作为一种古老而历经检验的系统,也被Linux内...

Android数据存储概述

Android数据库存储

Android的5种数据存储方式概述

Android有5种数据存储方式,具体分类如下: 1.应用内数据存储方式,程序外不可访问:SharedPreferences存储    应用场景:存储应用配置信息和常用信息。    获取SharedP...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)