当咱们使用汉语字典查找某个字时,咱们会先经过拼音目录查到那个字所在的页码,而后直接翻到字典的那一页,找到咱们要查的字,经过拼音目录查找比咱们拿起字典从头一页一页翻找要快的多,数据库索引也同样,索引就像书的目录,经过索引能极大提升数据查询的效率。mysql
在数据库中,常见的索引实现方式有哈希表、有序数组、搜索树。sql
哈希表是经过键值对(key-value)存储数据的索引实现方式,能够将哈希表想象成是一个数组,将索引经过哈希函数计算获得该行数据在数组中的位置,而后将数据存到数组中,容易发现一个问题,若是两个索引经过哈希函数计算后获得的数组位置相同要怎么办?咱们能够采用哈希链表,数组的每一个 value 都是一个链表,新数据直接添加到链表尾部。数据库
因此数据库查询过程为:索引经过哈希函数计算数据所在位置 --> 遍历指定位置的链表,找到知足条件的数据。数组
每次有新数据加入时,新数据时直接添加到链表尾部,因此添加数据时很方便。数据结构
哈希表不擅长进行区间查询,通常都用于等值查询,由于两个相邻索引经过 hash 函数后计算获得的数组位置不必定还保持相邻,须要哈希屡次才能把区间的数据全查出来。函数
顾名思义,有序数组是按索引大小将数据保存在一个数组上,由于该数组是有序的,能够经过二分法很容易查到位置,找到第一个位置后,经过向左或者向右遍历很容易获得所求区间的数据。所以,不管是等值查询仍是区间查询,效率都极高。性能
但缺陷也是显而易见的,当向数组中间 n 位置插入一条数据时,需将 n 后面的数据所有日后移动,因此,这种索引通常用于静态存储引擎。spa
二叉搜索树:一棵空树,或者是具备下列性质的二叉树:若它的左子树不空,则左子树上全部结点的值均小于它的根结点的值;若它的右子树不空,则右子树上全部结点的值均大于它的根结点的值;二叉搜索树的左、右子树也分别为二叉搜索树。指针
平衡二叉树:平衡二叉树是在二叉搜索树的基础上引入的,指的是结点的左子树和右子树的深度差不超过 1。code
多叉树:每一个结点能够有多个子结点,子节点的大小从左到右依次递增。
数据库通常使用平衡树来当索引的存储数据结构,当使用平衡二叉实现索引时,结构以下图。
从图中可发现,每次查询最多须要访问 4 个节点必能获得所要数据。例如查询 user2 时,查询过程为:userA-->userC-->userF-->user2。因此查询速度很高,复杂度为 O (log (n));平衡二叉树的更新复杂度也为 O (log (n))。
区间查询时,因为搜索树的特性(左子树小于右子树),能够很快的排除掉不知足条件的节点,查起来速度也是很快的。
思考下为何用平衡搜索树呢?
由于普通的二叉树可能由于插入的数据最后变成一个很长的链表,查询复杂度退化成 O (n)。
若是搜索树存于内存中,与多叉树相比,二叉树的搜索速率是最高的,但实际上数据库使用的是 n 叉树而不是二叉树。
在 mysql 的 innodb 引擎中,使用 B + 树来存储数据,B + 树是一种多叉平衡查找树。
在 B + 树中,咱们将节点分为叶子结点和非叶子结点,非叶子结点上保存的是索引,并且一个节点能够保存多个索引;数据所有存于叶子结点上,而且叶子结点之间经过指针链接起来。
根据叶子结点的内容不一样,innodb 索引分为主键索引和非主键索引。非主键索引也称为二级索引。主键索引的叶子结点中保存的数据为整行数据,而非主键索引叶子节点保存的是主键的值。
经过主键索引查询数据时,咱们只需查找主键索引树即可以获取数据;经过非主键索引查询数据时,咱们先经过非主键索引树查找到主键值,而后再在主键索引树搜索一次,这个过程称为回表,也就是说非主键索引查询会比主键查询多搜索一棵树。因此咱们应尽量使用主键查询。
B + 树是一颗 N 叉树,N 是由什么决定的?可否调整?
添加新行时,将会在索引表上添加一条记录,若是是索引递增插入时,数据都是追加在当前最大索引以后,不会对树中其余数据形成影响;若是新加入的数据的索引值位于节点的中间,须要挪动部分节点的位置,从而保持索引树的有序性。
并且,相邻多个节点是存储在同一个数据页上的,此时,若是是在已经存储满状态的数据页中插入节点,会申请新的数据页,将部分数据挪动到新的数据页,这个过程称为页分裂,页分裂除了会影响性能,还会下降磁盘空间利用率。不规则数据插入时,会形成频繁的页分裂。因此,通常状况下会采用递增主键,使新数据递增插入。
当相邻两个页因为删除了数据,利用率很低以后,会将数据页作合并。
什么状况下应该使用业务逻辑字段作主键?有什么优缺点?
综上,从性能和存储空间方面考量,自增主键每每是更合理的选择,可是当业务场景有且只有一个索引,并且该索引为惟一索引时,此时更适合使用业务逻辑字段做为主键,一个是避免回表,还有一个是只有一个索引也不须要考虑二级索引的空间占用状况了。
由于数据修改、删除、页分裂等缘由,会致使数据页空间利用率下降,此时,能够考虑重建索引,将数据按顺序插入,提升磁盘空间利用率。
重建普通索引时,直接先删除索引,再从新建立便可。
alter table T drop index k; alter table T add index(k);
主键索引不能经过上面的语句去重建,由于删除主键索引后,innodb 会以下处理:
select _rowid from table
查询)。因此删除主键索引的结果实际上是修改了主键字段,而普通索引的叶子节点存的是主键的值,因此,一旦修改了主键字段,普通索引也会有影响,叶子节点的值将被修改为新的主键字段。
当主键索引须要重建时,更好的作法是直接使用 alter table t engine=innodb
重建表。
喜欢本文的朋友,欢迎关注公众号「会玩code」,专一大白话分享实用技术