用数据库的时候常常有几个疑问:数据库
1:为啥经过加索引就能提高数据的查询料率?缓存
2:为啥加多了索引会致使增删改的效率变低?数据结构
3:为啥有的人能用好有的人用很差?性能
这些问题咱们可能不必定能说出答案。知道这些问题的答案有什么好处呢?若是开发的应用使用的数据库表中只有1万条数据,那么了解与不了解真的没有差异, 然而, 若是开发的应用有几百上千万甚至亿级别的数据,那么不深刻了解索引的原理, 写出来程序就根本跑不动,就比如若是给货车装个轿车的引擎,这货车还能拉的动货吗?优化
这其中很重要的一点就是咱们使用的数据库的结构是:关系型数据库。spa
用一个很简单很让人无语的解释是:索引对于数据库就像是目录对于新华字典。想从一新华字典里找到一个字的释义最快的方式就是经过目录去寻找对应的页码,而想从数据库里找到对应的数据最快的方式是经过索引。3d
想要理解索引原理必须清楚一种数据结构「平衡树」(非二叉),也就是b tree或者 b+ tree。固然, 有的数据库也使用哈希桶做用索引的数据结构 , 然而, 主流的RDBMS都是把平衡树当作数据表默认的索引数据结构的。指针
咱们平时建表的时候都会为表加上主键, 在某些关系数据库中, 若是建表时不指定主键,数据库会拒绝建表的语句执行。 事实上, 一个加了主键的表,并不能被称之为「表」。一个没加主键的表,它的数据无序的放置在磁盘存储器上,一行一行的排列的很整齐, 跟我认知中的「表」很接近。若是给表上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,也就是上面说的「平衡树」结构,换句话说,就是整个表就变成了一个索引。没错, 再说一遍, 整个表变成了一个索引,也就是所谓的「汇集索引」。 这就是为何一个表只能有一个主键, 一个表只能有一个「汇集索引」,由于主键的做用就是把「表」的数据格式转换成「索引(平衡树)」的格式放置。blog
上图就是带有主键的表(汇集索引)的结构图。其中树的全部结点(底部除外)的数据都是由主键字段中的数据构成,也就是一般咱们指定主键的id字段。最下面部分是真正表中的数据。 假如咱们执行一个SQL语句:索引
select * from table where id = 1256;
首先根据索引定位到1256这个值所在的叶结点,而后再经过叶结点取到id等于1256的数据行。 这里不讲解平衡树的运行细节, 可是从上图能看出,树一共有三层, 从根节点至叶节点只须要通过三次查找就能获得结果。以下图
假如一张表有一亿条数据 ,须要查找其中某一条数据,按照常规逻辑, 一条一条的去匹配的话, 最坏的状况下须要匹配一亿次才能获得结果,用大O标记法就是O(n)最坏时间复杂度,这是没法接受的,并且这一亿条数据显然不能一次性读入内存供程序使用, 所以, 这一亿次匹配在不经缓存优化的状况下就是一亿次IO开销,以如今磁盘的IO能力和CPU的运算能力, 有可能须要几个月才能得出结果 。若是把这张表转换成平衡树结构(一棵很是茂盛和节点很是多的树),假设这棵树有10层,那么只须要10次IO开销就能查找到所须要的数据, 速度以指数级别提高,用大O标记法就是O(log n),n是记录总树,底数是树的分叉数,结果就是树的层次数。换言之,查找次数是以树的分叉数为底,记录总数的对数,用公式来表示就是
用程序来表示就是Math.Log(100000000,10),100000000是记录数,10是树的分叉数(真实环境下分叉数远不止10), 结果就是查找次数,这里的结果从亿降到了个位数。所以,利用索引会使数据库查询有惊人的性能提高。
然而, 事物都是有两面的, 索引能让数据库查询数据的速度上升, 而使写入数据的速度降低,缘由很简单的, 由于平衡树这个结构必须一直维持在一个正确的状态, 增删改数据都会改变平衡树各节点中的索引数据内容,破坏树结构, 所以,在每次数据改变时, DBMS必须去从新梳理树(索引)的结构以确保它的正确,这会带来不小的性能开销,也就是为何索引会给查询之外的操做带来反作用的缘由。
非汇集索引和汇集索引同样, 一样是采用平衡树做为索引的数据结构。索引树结构中各节点的值来自于表中的索引字段, 假如给user表的name字段加上索引 , 那么索引就是由name字段中的值构成,在数据改变时, DBMS须要一直维护索引结构的正确性。若是给表中多个字段加上索引 , 那么就会出现多个独立的索引结构,每一个索引(非汇集索引)互相之间不存在关联。 以下图
每次给字段建一个新索引, 字段中的数据就会被复制一份出来, 用于生成索引。 所以, 给表添加索引,会增长表的体积, 占用磁盘存储空间。
非汇集索引和汇集索引的区别在于, 经过汇集索引能够查到须要查找的数据, 而经过非汇集索引能够查到记录对应的主键值 , 再使用主键的值经过汇集索引查找到须要的数据,以下图
无论以任何方式查询表, 最终都会利用主键经过汇集索引来定位到数据, 汇集索引(主键)是通往真实数据所在的惟一路径。
不使用汇集索引就能查询出所须要的数据, 这种非主流的方法 称之为「覆盖索引」查询, 也就是平时所说的复合索引或者多字段索引查询。 文章上面的内容已经指出, 当为字段创建索引之后, 字段中的内容会被同步到索引之中, 若是为一个索引指定两个字段, 那么这个两个字段的内容都会被同步至索引之中。
先看下面这个SQL语句
//创建索引
create index index_birthday on user_info(birthday);
//查询生日在1991年11月1日出生用户的用户名
select user_name from user_info where birthday = '1991-11-1'
这句SQL语句的执行过程以下
首先,经过非汇集索引index_birthday查找birthday等于1991-11-1的全部记录的主键ID值
而后,经过获得的主键ID值执行汇集索引查找,找到主键ID值对就的真实数据(数据行)存储的位置
最后, 从获得的真实数据中取得user_name字段的值返回, 也就是取得最终的结果
咱们把birthday字段上的索引改为双字段的覆盖索引
create index index_birthday_and_user_name on user_info(birthday, user_name);
这句SQL语句的执行过程就会变为
经过非汇集索引index_birthday_and_user_name查找birthday等于1991-11-1的叶节点的内容,然而, 叶节点中除了有user_name表主键ID的值之外, user_name字段的值也在里面, 所以不须要经过主键ID值的查找数据行的真实所在, 直接取得叶节点中user_name的值返回便可。 经过这种覆盖索引直接查找的方式, 能够省略不使用覆盖索引查找的后面两个步骤, 大大的提升了查询性能,以下图
在了解数据库索引以前,首先了解一下数据库索引的数据结构基础,B+tree
B+tree 是一个n叉树,每一个节点有多个叶子节点,一颗B+树包含根节点,内部节点,叶子节点。根节点多是一个叶子节点,也多是一个包含两个或两个以上叶子节点的节点。
B+tree的性质:
1.n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
2.全部的叶子结点中包含了所有关键字的信息,及指向含这些关键字记录的指针,且叶子结点自己依关键字的大小自小而大顺序连接。
3.全部的非终端结点能够当作是索引部分,结点中仅含其子树中的最大(或最小)关键字。
B+tree结构原型图大概以下(引用):