本篇趟个雷,把数据库归入到轮子中了,前面说到了数据库
其实不算轮子,也说到了其实我写不出来数据库,这里所说的数据库严格来讲是关系型数据库
,他比轮子复杂多了,是一个和操做系统差很少复杂度的东西,因此才能经过一个oralce养活一家全球50强的公司,其次,数据库太复杂了,要写出来实在是力所不能及,可是后来有想了一下,若是咱们从另一个角度来审视数据库,那么也有比较容易的实现办法,那么,这一篇,咱们来造个数据库吧,看吧,把关系型
去掉了,由于,有了关系型
几个字,数据库就变得复杂多了。git
关系型数据库(Relational Database)是一个伟大的发明,通常的数据库的理论,大概会分红如下三个部分。github
首先,数据库是创建在关系模型基础上的,而且从理论上来说,是有完备的数学模型,也就是集合代数
来作支撑的,他把咱们真实世界中的联系和实体抽象成了关系模型
,并用这个发展出了数据库理论,这是数据库的理论基础。算法
其次,也有人经过这个关系模型,发明了SQL这种进行关系查询的编程语言,用来对这个关系型的数据集合进行操做。这个实际上给出了经过集合代数发展出来的关系型数据库怎么进行数据操做和检索的。sql
还有人,发展出了数据库设计的理论,也就是你们所熟悉的数据库三大范式【应该是5大范式】,用来教咱们在实际场景中怎么设计一个数据库,几大范式其实是把关系模型
这个抽象的概念变成了几条规则,按照这几条规则去设计数据库,就能产生最少的数据冗余,最能体现出关系
这个模型的核心。数据库
咱们发现,上面三个大的部分都是数据库的理论知识,其实并无人告诉咱们怎么来用代码实现一个数据库,由于科学家们认为实现它并不重要,那是工程师要考虑的事情,Too Simple,科学家只负责搞出理论,反正咱们也不是科学家,那么咱们就来作作工程师吧。编程
既然是工程师,首先想到的就是如何来实现一个数据库了,一个标准的数据库大概主要会包含如下几个大的模块。后端
底层的存储层,这个是必不可少的,他是整个数据库的核心数据结构,也就是数据是如何保存的,通常提供最简单的原子增删改查。缓存
存储层上面就是引擎层了,这里会对底层的存储层进行各类组合型的操做用来知足查询的需求之类的,并且数据库的事务支持也在这一层,咱们熟悉的innoDB
就是一个数据库的存储引擎,他其实包含的就是这个引擎层和存储层了,引擎层提供对数据层的操做方法集合。微信
在引擎层之上还有个SQL的解析层,主要用来对SQL语句进行解析,分析,优化了,而后把SQL语句转化成引擎层的接口,进行具体的数据操做。网络
最上面就是对外的UI了,也就是用户交互层了,通常咱们熟悉的就是网络交互了。
虽然看起来好像挺简单,就是这么三层,可是实际的数据库是很是很是复杂的,除了这些之外还有不少其余模块,好比用户权限管理,缓存模块,日志模块,备份模块等等等等
,你们能够仔细去看看innoDB
的书籍或者innoDB
的代码,光一个binlog
就特别麻烦。
其实要保存数据,搜索系统也能保存数据,并且检索起来更快,而且二者的底层数据结构其实差异不是很大,但为何用数据库呢?由于数据库的核心是可靠
,这个可靠
就是考数据库的引擎层来保证的,完整的binlog记录,崩溃后完整的重放机制,数据双写,内存数据定时刷新到磁盘,全部的这些都是为了保证数据的可靠
,不会丢失数据。
而上面说的每个功能,都能单独的写一篇长文,因此说要实现一个数据库实际上是很麻烦的,由于为了作到可靠
,必然会有不少冗余的数据或者冗余的操做来保证可靠
,但做为一个成熟的产品,还须要考虑到产品的性能
,因此,如何既可靠
又性能
优良,就变成了一个衡量数据库好坏的标准,固然,在这两点上,目前没人能干过oracle了。
数据库如此之复杂,咱们如何对他进行瘦身 ,来实现一个最小的数据库系统呢?咱们能够从另一个角度想一想,就是咱们拿数据库是干什么的?那就是存储和查询数据
,若是这么来想的话,就能简单很多。
首先,咱们知道数据库最重要的功能就是存储数据,那么底层的存储部分是不能少的,其次,存储的数据要提供查询功能,否则存了就没意义了,这也是不能少的,第三,须要提供一个对外的接口能够和用户交互,否则就既不能存也不能查了。
因此,一个最最基本的数据库至少应该包含数据层,查询层(引擎层)和UI(用户接口)层三层,那么咱们就用几个简单的文件来实现这三层,完成一个最小的数据库吧。
数据库的基本单位是列
,再上一级的基本单位就是表
了,并且咱们在建表的时候都会指定列的名称
,类型
,长度
这三个最基本的属性,若是全部列都有这三个属性,那么其实咱们是知道每一行数据最多有多少字节的,因此,咱们能够设定没一行数据的长度都是定长的,那么整个表的长度也是定长的了,这样查询的时候能够根据行的长度进行快速定位数据,因此,咱们的最底层数据就是一个定长的表格了,每一列存储的时候就像下面这样,而后有个meta信息来存储列的属性
这个看上去很简单吧?也容易实现吧,其实不少数据库也基本上确实是这么实现的,并不难理解吧?稍微注意一下的是每一列存储的时候,每一个字段的前四个字节保存的是这个字段的实际长度,而后才是字段的实际内容,若是长度小于建表时的设定长度,那么有一部分空间是浪费掉的,虽然是浪费了,但仍是值得的,由于可让查询的时候省很多事。
这么下来,每行记录就是一个定长的,而一个数据库的表就是一个二进制文件了,但仅仅是这样仍是不够的,由于这样结构,不管什么查询都须要扫描全表,依次进行判断,而咱们在建表的时候都会创建索引,为了创建索引,咱们还得实现一个B+树
来存储索引,而B+树
基本上是全部数据库的索引保存的数据结构,这里咱们也有实现,若是对B+树
感兴趣,能够看我以前的一篇文章,那篇有详细的B+树的实现方式,文章后有那篇文章的连接。
总之,数据底层咱们就用了一个定长的二进制文件和几棵B+树,再加上一个meta信息文件来实现了一个数据库的底层数据层,很简单哈,但基本上包括了数据库真实的底层,虽然真正的数据库比这复杂多了,但也跑不掉这几个数据结构,整个看下来,数据层的数据结构大致上长这样子。
固然,数据层实现完了之后,还须要对上提供几个简单的接口,好比
建表接口
CreateTable( []FieldInfo ),参数是每一个字段的信息,包括字段的名称,长度,类型
数据插入接口
AddData(map[string]string) ,参数是一个map,key是字段名称,value是字段内容
单字段查询接口
Find(fieldname,fieldvalue,op),参数是字段名称,字段值,操做类型(大于,小于,等于)
数据获取接口
GetData(docid),参数是docid,用来计算在文件中的偏移
底层已经有了,接下来就是上面的查询层(引擎层)了,这里我没用引擎
两个字,是由于最小数据库的实现上,实在算不上一个引擎系统,咱们实现最简单的基本查询SQL(建表sql,插入数据sql,单表查询sql
)的解析,在实际中,SQL的解析是一个异常复杂的工程,涉及到语法分析,预处理,优化查询等几个大的部分,由于SQL实际上是一门编程语言,要解析一门编程语言,那么编译原理那一套基本上都会用获得。
这里咱们换条路子,由于只实现三种简单的SQL语句,那么咱们直接用正则和字符串的匹配来对SQL进行解析,解析完成之后变成一个个数据层的对外接口,建表和插入数据都比较简单,解析了SQL之后直接调用上面的第一
和第二接口
就好了。
数据查询的时候,对查询SQL的WHERE
以后的部分,用了个小算法,就是逆波兰表达式来对WHERE
以后的语句进行解析,变成一个栈结构来存储查询的内容,而后经过弹栈的方式一个一个调用接口三
,而且对结果进行求交和求并的操做,最后获得结果之后,再依次调用接口四
获取最后的结果,若是对逆波兰表达式不了解,那么请自行百度一下,很简单的,主要用在对四则运算的优先级的解析中。
查询层的输入输出很简单,他对外实际上只提供一个接口。ExecSqlSentence( Sql ) string
,都是字符串,输入是一条条的sql语句,输出是数据。
对于用户的接口层就更加简单了,咱们只须要提供一个TCP服务就好了,用;分号
来分割每次用户的输入,也就是说,咱们telnet上咱们这个数据库,而后输入sql,数据库就会返回数据了。
我在github上创建了一个新的工程叫SparrowSys
,麻雀工程,意思很明显,这是一个后端的麻雀,是最简单的后端轮子,目前我也已经提交了一部分代码,数据库的尚未写完,后面会补上的。
数据库的部分在src
下的SparrowDB
里面,很明显的看到里面有DataLayer
,EngineLayer
,NetLayer
,对应的就是上面的三层,每层里面有一到两个文件,都很简单,目前DataLayer基本完成了,后面会把EngineLayer和NetLayer补上,后面的文章会说说使用,utils
文件夹中是一些公共的东西,后面的其余轮子会用到的,好比B+树
就在utils
里面。
目前这个工程里面东西很少,不建议看,后面我补全之后会说明,欢迎你们提交你的实现来代替个人。接受任何pull request
。
十天没有更新了,主要是代码没时间写,因此没有测试结果可看,原本准备等代码都写完了再来更新文章,但最近实在是太忙了,没时间写代码,那先放出文章,等代码补充完整了再说说测试效果吧。
代码地址:https://github.com/wyh267/SparrowSys
若是你以为不错,欢迎转发给更多人看到,也欢迎关注个人公众号,主要聊聊搜索,推荐,广告技术,还有瞎扯。。文章会在这里首先发出来:)扫描或者搜索微信号XJJ267
或者搜索西加加语言
就行