深入浅出索引( 上)

大家都知道数据库的索引是用来提升检索速度的,但是索引内部是如何工作的,今天就来说说这个话题。

索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。帮助我们更快的查到我们需要的数据。

索引的常见模型

索引的种类有很多种,这里介绍三种常见,比较简单的数据结构,它们分别是哈希表、有序数组和搜索树。

主要从使用角度分析一下三种模型的区别:

  • 哈希表

哈希表是一种以键值(k-v)存储数据的结构,只要输入待查找的值,比如key就可以很快的找到value。

但是如果多个k对应一个value,解决方法时拉出一个链表。

假设一张表,只有身份证和姓名,需要根据身份证号找到姓名,对应的哈希索引示意图如下:
在这里插入图片描述
图中,User2和User4根据身份证号算出来的值都是N,后面还跟了一张链表。那么如何利用D-card-n2找出name2?

首先将ID_card_n2通过哈希函数算出N;然后按顺序遍历找到User2。

有序哈希插入不方便、区间查询方便;无序哈希,插入方便(顺延插入),区间查询不方便

哈希表这种结构适用于只有等值查询的场景

  • 有序数组

以上情况使用有序数组来实现的话,示意图如下所示:
在这里插入图片描述
假设身份证号没有重复,数组按照身份证号递增顺序保存。如果要查找ID-card-n2对应的名字,用二分法就可以查询到,这个时间复杂度是logN。

同时很显然这个索引结构支持范围查询。并且查询效率也是很高的。 但是, 在需要更新数据的时候就麻烦了, 你往中间插入一个记录就必须得挪动后面所有的记录, 成本太高。

有序数组索引只适用于静态存储引擎,就是那种不会修改的数据。

  • 二叉搜索树

二叉搜索树也是经典数据结构,同样还是上面的例子,示意图如下所示:
在这里插入图片描述
二叉搜索树特点是:每个节点的左儿子小于父节点,父节点小于右儿子。

当然为了维持O(log(N))的查询复杂度,就需要保持这棵树的平衡二叉树。为了做这个保证,更新的时间复杂度也是O(log(N))。

为了让一个查询尽量少地读磁盘, 就必须让查询过程访问尽量少的数据块。 所以就不能使用二叉树,而是多叉树,至于多少叉,取决于数据块的大小,但是总体都满足大小从左往右递增。

以InnoDB的一个整数字段索引为例,N差不多是1200。当树高是4的时候,就可以存储1200的3次方个值,但是考虑到数据块总是在内存中的,只需要访问三次磁盘访问速度是很快的。

  • 总结

在MySQL中,索引是在存储引擎层实现的,所以没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。下面以InnoDB为例,简要分析一下其中的索引模型。

InnoDB的索引模型

在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。

InnoDB使用了B+数索引模型,所以数据都是存储在B+树中的。

每一个索引在InnoDB里面对应一棵B+树。

假设我们有一个主键列为ID的表,表中有字段k,并且在k上有索引。

建表语句时:

mysql> create table T(
id int primary key,
k int not null,
name varchar(16),
index (k))engine=InnoDB;

插入适当的数据:

mysql> insert into T3 (id,k,name)values(100,1,'R1'); 
Query OK, 1 row affected (0.00 sec)

mysql> insert into T3 (id,k,name)values(200,2,'R2');  
Query OK, 1 row affected (0.09 sec)

mysql> insert into T3 (id,k,name)values(300,3,'R3');
Query OK, 1 row affected (0.00 sec)

mysql> insert into T3 (id,k,name)values(500,5,'R4');
Query OK, 1 row affected (0.00 sec)

mysql> insert into T3 (id,k,name)values(600,6,'R5');  
Query OK, 1 row affected (0.01 sec)

两棵树的示例示图如下:
在这里插入图片描述
从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主键索引。

主键索引的叶子节点存的是整行数据。
非主键索引的叶子节点内容是主键的值。

那么,基于之主键索引和普通索引的查询有什么区别?

  • 如果语句是select * from Twhere ID=500, 即主键查询方式, 则只需要搜索ID这棵B+树;
  • 如果语句是select * from Twhere k=5, 即普通索引查询方式, 则需要先搜索k索引树, 得到ID的值为500, 再到ID索引树搜索一次。 这个过程称为回表。

也就是说, 基于非主键索引的查询需要多扫描一棵索引树。 因此, 我们在应用中应该尽量使用主键查询。

索引维护

B+树为了维护索引的有序性,在出入新值的时候需要做必要的维护。如果插入新的行ID值为700,则只需要在R5记录后面插入一个新纪录。如果插入的ID=400,需要逻辑上挪动后面的数据,空出位置。

更加糟糕的情况是,如果R5所在的数据页满了,这时需要申请一个新的数据页,然后挪动部分数据过去。 这个过程称为页分裂。 在这种情况下, 性能自然会受影响。

除了性能外, 页分裂操作还影响数据页的利用率。 原本放在一个页的数据, 现在分到两个页中,整体空间利用率降低大约50%。

当然有分裂就有合并。 当相邻两个页由于删除了数据, 利用率很低之后, 会将数据页做合并。 合并的过程, 可以认为是分裂过程的逆过程。

自增主键场景

在建表的时候这样来定义自增主键:NOT NULL PRIMARY KEY AUTO_INCREMENT。

插入的时候不需要指定自增的主键。

从业务角度去考虑,如果你的表内有除了自增主键以外的唯一字段,那么是用自增主键还是用唯一字段呢?

由于每个非主键索引的叶子节点上都是主键的值,如果用唯一字段,那么每个二级索引的叶子节点占用N(具体长度由唯一字段长度决定)字节,如果用整型做主键,则只需要4字节,如果是长整型就需要8字节。

显然,主键长度越小,普通索引占用空间就越少。

所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值