0%

大道至简 -- 之iDB NoSQL数据库开发随笔(一)

iDB NoSQL数据库是结构化的面向列(Structured-Column Oriented),并基于目录(Directory-Based)的层级式(Hierarchy)NoSQL开源数据存储系统(项目主页:https://github.com/snowyu/iDB),iDB有别于传统数据库基于文件的存储形式。我们可以把它视作新一代数据库系统(A new way to database engine)的核心。

几个概念:

  • 面向列:所谓面向列(Column Oriented)是和面向行(Row Oriented)相对而言的,绝大多数的关系型数据库就是面向行存储的,而iDB则是面向列基于Key/Value的存储系统。
  • 结构化:所谓结构化(Structured)是指值(Value)可以是带结构数据的复杂类型,而层级则是指Key可以有不同的层级关系,基于目录树的形式。
  • 基于目录:基于目录(Directory-Based)也是相对于基于文件(File-Based)而言,对于传统的数据库系统几乎都是通过创建少量的随机文件,然后把大量的数据存放在里面,而iDB则完全不同,也可以说是恰恰相反,它是创建大量的目录和文件,把数据分散在目录中,你可以认为这是一种创新。

Background (设计背景)

iDB NoSQL数据库引擎的设计初衷是希望能更为合理的设计组织数据库引擎的层次架构,以及看到现代文件系统设计发展的变化有感而发,两者融合就促使我萌发设计iDB的念头。

KISS 原则

大家都应该知道KISS(Keep It Simple, Stupid)原则,即保持架构简单而愚蠢的原则,因为简单,所以看上去足够的蠢。而将架构设计得足够的简单是架构师的梦寐以求,大道至简,说着看是容易,实际做起来,其实真难,知易行难是也。我希望iDB的架构设计做到了足够简单以及足够的愚蠢,这也是我从事架构设计中最接近KISS原则的一次架构体验,我很乐于与大家分享我这设计过程的经验和错误。

文件系统与数据库

前面我提到现代文件系统的发展和变化,这和NoSQL数据库的设计存在什么样的联系?数据库引擎的设计需要考虑文件系统的特性么?我们可以做一个列表来对比,文件系统和数据库的功能特性:

从操作对象和操作方式上看

  • 数据
  • 文件系统: 文件名/文件内容
  • NoSQL : Key/Value
  • 操作
  • 文件系统: 读、写、删除、列举、文件
  • 管理操作: 磁盘系统检查(fsck),磁盘卷热备
  • NoSQL : Get, Set. Delete, List Key
  • 管理操作: 数据库校验, 数据库热备

从内部存储结构上看

内部存储结构上,绝大多数数据库系统都是使用Btree树或者其变体来存储数据(也有用hash表实现的)。而目前越来越多的现代文件系统正在用Btree树或者其变体来存储目录或文件。比如:

  • ext3/ext4 使用Htree(Btree的变体)来索引目录,不过这ext3中需要手动启用dir_index功能,ext4默认是启用的。
  • btrfs 使用btree,又被称为 “Btree FS”。
  • ZFS 使用 Extensible hash table
  • Reiser4 使用 Dancing B*-tree
  • XFS B+tree

可以看到在最基本数据存储和访问上,数据库和文件系统有惊人的相似性。为了最大限度的优化数据库的性能,开发者做了许多与文件系统相同的重复工作,磁头位置预估,扇面连续,defragment等等,并针对SSD做优化。但是这些相同的工作文件系统也都在做。

不要重复发明轮子(DRTW)

那么不同的地方在哪里?数据库更复杂,除了基本的存储和访问需求外,它还需要对结构化的数据进行检索和处理。但是因为老的数据库思路是申请一个文件,也即是磁盘空间的一个区域在这上面进行数据存储和访问,这样,文件系统的成果自然无法用上。

因此,根据懒人法则第一条,能躺绝坐,能坐绝不站。站在巨人的肩膀上,才能看得更远,走得更远。那么怎么样对数据存储才能利用上文件系统的成果呢?

让我们跳出来文件这个限制框框思考,为啥数据库的数据一定要保存在文件中?为啥不能直接用文件的目录系统直接作为NoSQL数据库的存储呢?当我们跳出这个限制:数据必须保存到文件中。我们就会欣然发现,只要我们正视文件系统的一些限制,其实目录也可以存数据。既然文件系统都已经充分考虑并已经成熟(N多程序在通过不同方式使用着文件系统),为啥要重复发明轮子?作为数据库引擎应该更专注于数据处理才对,而不是数据存储。

KISS IT

iDB就是这样的一次尝试。它试图充分而彻底的利用文件系统本身来进行数据存储,你甚至可以利用文件系统自己去浏览管理数据,看上去,我所做的一切似乎仅仅只是制定了数据存储的规范和一系列的约定。其中,最重要,最核心的约定是文件系统的目录名称为Key名,该目录下的“.value”文件内容为值的内容,“.type”文件内容为该Key的类型说明。

听上去够简单吧。也许简单到能让你发笑。

古语有云:

上士闻道,勤而行之;中士闻道,若存若亡;下士闻道,大笑之。不笑不足以为道。

就这么个简单东东,我从去年7月份开始设计,断断续续直到现在,逐步理清思路,才实施了一个能实用的东西。中间走了些弯路,单是规范修改了好几次。而且这一切仅仅还只是一个开端。如此,是不是够蠢。

Stay simple ,stay foolish…

如上所述就是开发iDB的设计背景,下面从哪里谈起呢?想写的东西太多,就先从iDB规范讲起吧。

iDB 架构与规范

草拟规范是架构设计中尤为重要的一环,架构如果没有规范,便如同建筑师没有设计蓝图就开始修建房屋,无论是自顶向下还是自底向上设计,每一步都离不开规范文档。单从数据库系统的设计上来讲,由底向上看,描述数据在文件系统中如何存储数据的存储规范便是整个系统的基石,如果没有它,那么一切都是空谈。

在我们进行iDB设计的过程中,起初iDB规范只有一个,后来逐渐分为多个,下面让我们从底一步一步向上看:

  • iDB存储规范:用于描述数据在文件系统中是如何存储的。它是与文件系统的接口规范

  • iDB数据库规范:用于描述iDB数据的类型、索引规范以及其它相关约定

    • iDB Key/Value 数据库规范
    • iDB 关系型数据库规范
  • iDB 存储服务规范:用于描述iDB存储服务的功能规范,分为三层:

    • 读写分离的异步通讯IO层
    • Memcache 内部缓存层
    • 实际iDB存储层
  • iDB云数据库服务规范:以下规范实际上与iDB存储无关,只是用于描述一种分布式NoSQL数据库的规范方案,我强称之为iDB云数据库规范。

    • iDB 节点控制器: 控制和监控数据库,作为与各种数据库的中间代理层
    • iDB 集群控制器: 管理iDB节点控制器,数据定位
    • iDB 云控制器: 是暴露在互联网上顶层组件。为外部世界提供RESTful API以及Web接口

目前只实现iDB存储规范中的内容,文档还很乱,写得比较糟糕,但是具体思路已经敲定了,只剩下文字增订的事情,但是iDB整体规范依然还在草拟中。

iDB存储规范

iDB存储规范是iDB最基础的规范约定,描述了iDB基础数据是如何被存储和访问。

我们大概已经知道了,目录名为Key名,目录名下的".value"文件内容为键值。我们将".value"称之为key的一个属性,".value"是其中最重要的一个属性。一个
key(键)可以有多个属性,对于一般使用者来说,是无法直接看见这些属性的,这些属性为iDB规范服务的。

基础概念

小结,从以上的描述,我们可以总结出以下的概念

  • Key Name: 键名,同于目录名
  • Attribute: 键属性, 同于文件名,其文件内容为键属性值。
  • .value: 键值属性
  • .type: 键值类型属性

注意: 这里约定所有的字符串编码统一为utf-8编码格式。其中包括key name。值内容为纯字符串的形式表示。“redis"类型例外,如果类型为"redis”,则值内容为redisIO的raw格式。

路径为iDB层次性的体现,因此键路径出现了:

  • Key Path: 键路径,同于目录路径

然后就有了子健:

  • Subkey: 子健,属于键路径下的某个键,称为该键路径下的子健。

随着对子健遍历需求的出现,又诞生了对键的分页(分区)的需要。

分区/分页(Partition Key)

对iDB数据库根目录下的子健的遍历就是对整个数据库的key的遍历。如果数据库中数据巨大,那么一次遍历传回所有key数据,可能消耗大量的时间和资源,在这种情况下可能需要分页(分区)的形式来组织key列表。

那么分区(分页)是否是必要的?

(未完待续)