本文主要整理了数据库经常使用的算法。算法
咱们虽然没有必要从头开始了解数据库的底层算法是什么,可是了解大概原理是必要的。数据库
其实如今不少技术均可以从经典算法中找到原型,好比Hadoop其实就是合并算法演变过来了。数组
这样说来算法至关于内功,若是能理解了这些算法,再学其余的技术,就是一鞭一条痕 一掴一掌血服务器
在了解全部算法以前,须要先了解算法复杂度,这里的算法复杂度主要指的是时间复杂度,是当数据量增长时运算如何增长的一种度量。至关于给算法一把标尺,这样咱们才比如较那种算法更优。同时当数据量已经到了海量的级别,咱们必须尽量的扣性能,这样才能保证整个架构的可用性。数据结构
这里的复杂度主要指的算法的时间复杂度,是当数据量增长时运算如何增长。架构
下图标识了几种经常使用算法的复杂度,咱们能够有个直观的认识。函数
面临海量数据时oop
正由于哈希表、均衡树以及好的排序算法的时间复杂度是这个样子,咱们才会选用它。
性能
数据库里面最经常使用的排序算法莫过于合并排序,它主要用在对查询优化、数据库联接上。优化
假设如今给了咱们两个数量为4的序列,要把他们按照从小到大的顺序合并成一个8元素的序列,应该怎么作。
能够双方都出一个元素过来比较,谁小则放到8元素序列中。好比下图中:
第二轮:左边的1已经放到下面去了,右边的2尚未动。因此要比较的是3和2,固然2小 。
第三轮:比较3和4
重复
所有过程能够看以下的Gif
总结一下:
仔细看上面的算法,是否是两个序列合并以后就成了有序的呢。
不过要执行这种算法,有个必要条件是要合并的序列必须是有序的,这样才能够只比较当前元素完成排序。
可是对任意一个序列来讲不多是彻底有序的。那么此时就陷入了僵局。
那么咱们可不能够这样想,单个元素确定是有序的吧,因此咱们若是把两个1元素的序列合并,确定是能够用这种算法的,这样就获得一个2元素的有序序列,若是此时还有一个2元素的有序序列,是否是能够再合并。而后是4元素序列合并,接下来是8元素序列合并。
大概是下图这个样子
那要怎么获得这样1元素的序列呢?固然是拆分。8元素拆分为4,4拆分为2,拆分为1 。
好了,这个算法就完整了。
首先为了得到1元素的序列 ,咱们须要把要排序的序列进行拆分,拆分之后再进行合并。
使用3个步骤将序列分为一元序列。步骤数量的值是 log(N) ,好比如今是 N=8, log(N)=3
为何呢?由于拆分的每一步都是把原序列长度除以2,因此要执行多少步就是能把原序列除2多少次,正好就是对数的定义嘛。
一样,排序阶段也有log(N)个步骤,理由与上面拆分阶段的相同。
而每次个步骤,全部的元素都须要动一下才能移到下一个序列里面,因此每一个步骤都须要执行N次运算。
也就是说**总体成本是 N*log(N) 次运算。**
完整过程以下:
若是熟悉Hadoop就知道,里面的MapReduce其实就是这种思想,分而治之,把一个大的任务拆分红若干小的任务,最后再各个击破,合并便可。
能够说MapReduce就是合并算法修改后的结果,它能够运行在多处理、多服务器这种架构上罢了。
说到数据库的数据结构,咱们最容易想到就是的相似于Excel那样的数据表了。
以下图
每一行表示一个主体,而每一列则是若干属性或者说叫字段。
优势是很是的直观,缺点是太过简单,当数据量太大的时候,查找不易。
那么为了优化查找,主要有两种方法一是构建查找树,一是Hash表。下面咱们分别介绍。
若是直接在数组或者阵列上进行查找,并且若是碰巧它又是有序的,天然好办,可使用折半、插值等方法。可是实际上,大部分的数组不大多是有序的,因此须要在进行排序,须要消耗大量的资源和时间。
那么有没有办法能够插入和删除效率还不错,并且又比较高效的进行查找呢?
若是咱们一筹莫展的话,不妨从最简单的状况入手,若是如今只有{62},而后须要把88插入进来,就成了{62,88},若是如今要插入58,同时还保持有序,天然须要把88日后挪一下。能够不挪吗?
咱们知道树这种结构,能够方便的插入和删除,而后引申出二叉树结构了。
首先将62定为根结点,88比62大,因此作为62的右子树,同理,58成为左子树。
下图就是一棵二叉排序树,只要对它进行中序遍历就能够得到一个有序的序列
好比此时咱们要查询93,则能够像下面同样查询。
咱们只要查到了93,就能够知道它再哪一行,而后在这一行里面去查找,范围天然小了许多。
那么查询的成本呢?天然就是树的层数,即$log(N)$
那么设想这样一个例子。
若是数据库中的一张表含有一个country的字段,如今要找谁在China工做。若是是阵列的话,咱们须要将整张表都扫一下
可是若是把country字段中全部的元素创建一个二叉查找树,则最多使用$log(N)$就能够查找表明China的节点,而后经过这个节点就知道有哪些行须要考虑了。
这就是索引,索引就是用其余的数据结构来表示某些列,能够加速对此列的查找。
可是新的问题又来了,查找某个值用二叉查找树挺好的,可是若是要查找两个值之间的多个元素怎么办?使用二叉查找树须要查找每一个树的节点才能判断是否在两个值之间。
因而咱们引入了一种改进的树——B+树
其特征为:
能够看出,多了一整行用来保存信息的,此时若是要找40~100之间的值,
只须要先找到40,而后遍历后续节点便可。
好比找了M个后续节点,则须要$M+log(N)$次便可。
可是B+节点须要保持顺序,若是在数据库里面增长或者删除一行,则须要B+树进行较大的变化,查入和删除也是O(logN)复杂度的。
因此索引不能太多,它会减慢插入、更新、删除行的操做。
前面说过使用Hash表进行查找,其时间复杂度只有O(1),应该是最快的查找方法了。
不论是普通的序列仍是顺序表,咱们都须要拿要查找的值与序列中的元素进行比较,那么可否只根据关键字key就能查找到对应的内存位置呢?
也就是存在这样一种函数:
$$存储位置 = f (关键字)$$
这就是所谓的散列技术,这个 $f$就是散列函数,又称为哈希函数。
采用散列数据将记录存储在一块连续的空间里面,这块空间就叫散列表或者哈希表。
存储的时候,经过散列函数能够计算记录的散列地址,并按照此地址进行存储。
在查找的时候,也一样使用此散列函数计算相应的地址进行查找。
也就是在哪存的就去哪找,那么散列技术既是一种存储技术,又是一种查找技术
散列技术最适合的场景是查找与给定值相等的记录。由于不须要比较,效率大大提升。
可是若是遇到一个key对应多条记录,就不适合用散列表了。好比使用关键字“男”去查找一个班的学生,明显是不合适的。
一样,散列表也不适合范围查找,也就是查找40~100学号的同窗。
另外,咱们还会常常遇到两个关键字使用散列函数却获得一样的地址,这样就冲突了,能够能够把这个key称为散列函数的同义词,因此还须要设计方法来避免冲突。
上面说过一个好的哈希函数最关键的是让散列地址均匀的分布在存储空间里面,这样能够减小冲突,通常来讲经常使用的散列函数都是将原来的数字按照某种规律变成另外一种数字的。
那么若是$p$选得很差,冲突就会比较多了。
根据经验,若是表长为$m$,则$p$为小于或等于$m$的最小质数或不包含小于20质因子的合数。
解决冲突咱们只介绍一种,就是链地址法
咱们能够将全部关键字为同义词的记录存储在一个单链表中,这样每一个链表就叫哈希桶
因此散列表只存储全部同义词子表的头指针,这样不管有多少冲突 ,只须要在当前位置给单链表增长结点。
那么一个好的哈希函数应该让哈希桶里面的元素很是少才对,这样就能够直接在表里面查找到,时间复杂度为O(1)