一个小型数据库的核心组件

若是想要了解存储,我比较推荐的方式仍是从了解数据库开始。从目前来看,数据库发展了这么多年,各类理论相对的比较完善,面对各类应用场景,其核心处理模式也已经很是的成熟了,在新的海量数据的时代,人们只是对扩展性提出了更高的要求,而对数据存储的其余方面却仍然但愿能保持以前的水平。

而从目前实际的发展来看,基本上目前发展的核心思路并无绕开人们在数据库理论领域内所积累的那些关键的特性。所以,若是你但愿可以快速的在海量数据的在线处理领域内积累知识,从传统数据库领域入手是绝对不会错的。java

下面,就让咱们对数据库作个简单的解刨,看看数据库里面有哪些核心的组件吧。程序员

映射(Map):
首先就须要有可以存储数据并提供查询的结构,这个结构,在java里面就是Map。C里面也是Map.他的核心做用就是,创建一种key与value的映射关系,当给定某个key的时候,他可以返回这个key所对应的value给用户。这是用户在进行查询时的主要数据结构。算法

预写式日志(write-aheadlogging,WAL):
就是个队列,记录了你每一次写的操做。天然而然的,由于你的每次写操做都被记录下来了,因此就算计算机断电了,只要这个日志没有损坏,计算机重启后按照这个log,重放在断电时的那些写操做,就能够保证你的数据不丢。
这里,必定会有人问:既然我数据都存储在k-v表里了,明显就不会丢失了。为何还要有这个log呢?这其实就是一个计算机的本质性问题了,别看现代计算机运算速度这么快,他终归也只是个“图灵机”实现,或者更具象化一点,就是一台打字机,一次只能打一个字母,那么可能会有人问了,若是我要用几个字母来表示同一个意思,应该怎么作呢?在英语中,最简单的方式就是在词组和词组之间增长空格。好比writeaheadlogging.就是三个由字母组成的单词。在计算机里,也有相似的问题,用户的一次写入操做,可能对应计算机内的多步操做,如何可以保证这屡次的操做要么所有成功,要么所有失败呢?WAL就是个解决的方法,他利用的是操做系统里的一个原子操做fsync().该操做的做用是将一小段数据写入到磁盘,从而保证数据不会丢失。
咱们来看一下总体的操做思路:记录用户的写入操做(insert,update,delete)->进行内部屡次key-value映射的构建,包括主数据,辅助索引数据等->标记该用户操做完成。sql

触发器(trigger)
一个不难理解的概念,当发生insert,update,delete等操做的时候,可能会有一些需求须要依托这些操做而被触发执行其余的操做。好比每一行针对表A的更新,都会引起B表内的更新。那么这个“引起”的过程,就是触发器。在一些其余的语言里面,这也被叫作callback,IFTTT,Listener等。但核心概念都同样,被动的由于某个事件而触发一段代码逻辑的运行。
在一些数据库的实现中,甚至二级索引的更新也是使用触发器来完成的哦:)
在数据库内,触发器所有是同步实现的,也就是说,只有当数据写入的操做,以及触发器的操做所有都执行完成后,才会返回用户执行成功。数据库

锁(lock)
锁的主要目标是容许线程圈定一批资源,并规定该资源只容许发出圈定请求的那个线程进行访问,而其余线程则必须等待。
这个概念产生的主要缘由其实仍是与计算机是图灵机有关。。原本计算机就是台图灵机,一个时钟周期内只能打一个字母,但这样他就很难同时作好几件事情,好比听着歌写代码,这件事其实从计算机硬件来讲是作不到的,他只能模拟,利用时分复用的方式,把cpu的运算分解成小片,每一个线程都只占用一小段时间,从而可以作到同一时间作好几件事。可是,想想,若是咱们但愿一我的A用打字机打iamgod.而但愿另一我的B用同一台打字机打pigismoney.开始,时间片分配给A,他打印了iam后,A被cpu换出,B被换入,打印了pig后被其余人换出,那么咱们天然就发现。。数据就变成了。。。那么锁的做用就是保证一个逻辑的原子操做没有完结的时候,这张打印纸只属于A,其余人不能对其进行访问或进行修改。
明白了原理,来简单看看实现,锁主要是由排他锁(写锁)和共享锁(读锁)构成,在数据库的锁实现中,有不少针对共享锁和排他锁相互组合的细节性描述,但其核心的问题却永远没变:
1)尽量的减小同一时间内被阻塞的线程数,从而提高并行度。
2)尽量的避免死锁
能够说数据库实现的是好是坏,关键就看着锁的优化好很差,这在分布式场景或者在单机内都是最重要的一个机制。编程

执行优化器
这是关系数据库得名的缘由,主要的做用是将关系查询转换成key-value查询,输入是sql的抽象语法树(ast),输出则是执行计划,就是各位在数据库命令行打explainsql时候出来的那些东西。
理解上很简单,但实际上实现起来倒是最为复杂的,在上个世纪,大部分的执行优化器使用rulebasedoptimizer,也就是基于规则的优化,但在现代数据库实现中,大部分的优化器都采起了costbasedoptimizer了,他们之间最大的不一样,就是cbo更多的考虑了数据实际的区分度状况,从而能更简单准确的从。多个可选的索引中选择一个正确的索引。缓存

sql解析器
做用很简单,把用户输入的sql转化为计算机能够理解的抽语法树(不懂就去看编译原理:)安全

好了,基本组件儿介绍完毕,下面咱们利用这些核心组件来尝试拼装一些外围的概念。服务器

第一个概念是:存储过程。
我第一次接触数据库的时候,对存储过程比较不理解。认为数据库么,使用关系模型就足够了啊,为何还要支持一种相似编程语言的东西来额外的增长系统的复杂度呢?并且在当时,有大量的高级程序员在介绍他们的经验的时候都会分享说:尽量不使用存储过程,那玩意儿很是不容易维护,也会增长很是多的使用成本,应该把全部业务逻辑放在客户端。那么我天然就有个疑问,既然这些事情客户端都能作,那么还要存储过程干什么?可能第一次接触数据库的人也会有我以前的困惑吧。。。呵呵,因此既然我已经能解答这个问题,在这里天然而然的也要尝试给有相同问题的人解惑。
存储过程其实不是个复杂的概念,他的核心目标就是让数据库端可以运行逻辑代码(判断,循环..etc),甚至在oracle,存储过程能够作任何事。咱们排除oracle但愿用户只用数据库来完成一切功能的阴谋论,来看看事情的本源是什么?或者说,有什么事情是存储过程能作,而其余方式作不了的?
很简单,也有不少人提到过,就是性能好。那么,为何会性能好呢?
这与咱们目前的软件结构有关系,在当前,大部分状况下,数据库是一台独立的机器,而应用服务器则是另一台独立的机器,那么,相互独立的机器之间要进行交互操做,势必须要使用网络来进行通讯。
网络通讯的代价比使用内存指针变动的代价大很是多,这就致使了一个直接的问题,若是使用网络进行屡次交互,那么延迟会远远地大于使用内存来进行消息交互。延迟变大,意味着锁持有时间变长,也就意味着单位时间内针对同一个数据的操做频率降低,TPS就会降低。
这才是存储过程之因此可以提高性能的关键。它不是恶魔,但也不是天使,能不能发挥出特定的优点,要看具体的业务场景须要。
咱们作个简单的总结:
存储过程的好处,就是能够减小网络交互开销,能够用来封装一些须要高性能的小的业务逻辑单元。
存储过程的坏处,就是绑定到特定数据库上,同时,由于大部分存储过程是面向过程的代码,因此运维难度相对较大,不适于处理复杂业务逻辑。
第二个概念是:视图
视图这个概念也是我开始看数据库时候很晕的一个概念,在任何一个数据库内,数据库的说明文档中都会给出特别多中视图的实现,看起来就特别容易晕。常常有的困惑是:为何视图不能写数据?以及,join自己也挺方便的的,我为何还须要视图?
这里,为了解答这个问题,咱们就须要来看看一种最多见的计算机优化方法:将不肯定性变成肯定性。
不少状况下,若是你能提早预知不肯定性的范围,每每就能大范围的减小锁的范围,或者将计算量进行分解。
视图,从必定程度上也是利用将不肯定性变成肯定性的方式,来实现join查询速度的优化和聚焦。
若是计算机不知道你预先须要把哪些表进行join操做,他能作的就只有使用最悲观的方式来对用户的行为进行假定,也就是最坏状况下,全部表均可能产生关联关系,而且关联的次数和频率都是均等的。那么针对这种场景,最安全的策略就是不缓存任何join的中间结果,而只使用通用的join算法进行join计算。
可是,若是用户经过本身的实际业务场景,发现其实有两个表是固定的被join在一块儿而进行查询的。这种状况就符合了”将不肯定性变成肯定性“这个优化的前提,所以就能够进行一些优化,view从某种程度上来讲,就是告知数据库这种肯定性的一种手段。
数据库在获知这种hint后,就可使用一些新的,空间换时间的方式,来预先进行一些操做,从而下降在join查询计算发生时所消耗的计算量。从而提高查询性能,下降系统开销。
ok,本篇到这,本篇主要是介绍了数据库的一些关键的概念,在下一篇,我将使用一些实际查询的例子,来帮助你们更易于理解在实际数据库中,上面的这些核心概念是如何被应用的。
相关文章
相关标签/搜索