数据存储--日志存储类型与哈希索引

2017-12-17  本文已影响22人  MontyOak

所谓数据库,它的核心功能无非是存、取数据两种操作。所以,我们可以简单大实现以下两种方法,来实现一个最简单大数据库:

db_set () {
echo "$1,$2" >> database
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}

上面两个方法定义了一个最简单的key-value类型的数据库。下面是使用样例:

$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}

这两个方法所操作的文件叫database,它本身内容都是按逗号分割的键值对(可以粗略看作是CSV格式)。它本身的update操作不是去修改原有键的值,而是直接在现有文件后追加一条新的记录,所以获取值的时候应该以最后一个目标键对应的值为准。

$ db_set 42 '{"name":"San Francisco","attractions":["Exploratorium"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Exploratorium"]}
$ cat database
123456,{"name":"London","attractions":["Big Ben","London Eye"]}
42,{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
42,{"name":"San Francisco","attractions":["Exploratorium"]}

类似上面的只追加的记录文件叫做日志(log)。它广泛应用于实际数据库当中。追加写入操作的代价很小,速度很快,这也是日志被广泛应用的原因之一。
在上面例子当中,随着数据量的增加,db_get方法速度会越来越慢。它是从前到后扫描整个文件,所以它的时间复杂度是O(n)。
为了加快查找操作的速度,我们需要引入索引(index)的概念。它的主要思想是用其它形式来存储额外的元数据,来帮助加速定位目标数据的确切位置。增删索引对于原始数据本身没有任何影响,它影响的只是查询操作的速度,然而由于索引是原始数据之外的部分,需要在每次写入操作的时候进行更新维护,所以,索引应该按需来建。

哈希索引(Hash Indexes)

大多数语言都有内建叫做字典(dictionary)的数据结构。这种数据结构内部往往使用哈希来实现。类似的,对于键值对(key-value)数据库,也可以采用哈希索引的思想。对于上面只有两个方法的那个简单的键值对数据库实现,可以使用哈希索引:

哈希索引
在内存中维护一个哈希表,记录某一确定key所对应记录的缩进值,每当发生写入(包括insert和update)操作时,更新这个哈希表。这种做法也是Riak的内部数据引擎Bitcask的行为。这种做法适合用于更新操作频繁而键不多(方便将键放在内存中)发生的场景。
随着数据量的增加,日志文件也会进一步增大,我们需要定时做压缩操作来去除重复键占用的空间(由更新操作造成)。
压缩操作
推广来说,我们可以对多个文件采取压缩合并操作,来生成新的数据文件(压缩合并操作一般由子进程完成,以免阻塞主进程的读写操作)。
压缩合并操作
当查找指定键时,我们按生成时间倒序来依次去不同文件的哈希表中寻找键(由于压缩合并操作,哈希表的数量不会太多)。
实际工程(Bitcask的行为)中还有以下几点问题:

使用只追加的设计而非直接更新原始数据有以下几个优点:

哈希索引也存在诸多限制:

上一篇下一篇

猜你喜欢

热点阅读