干货 | 聊聊数据库索引
什么是索引,为什么需要索引?
What,Why,Where,How...记得在德国念书做第一个实验室项目的时候,教授就给我们说:想认识任何一门技术,先从这四个词开始。
先来看这样一个例子,超市里面有成千上万种商品,要从里面找到黑人牙膏,如果从进门开始在货架上一个个去找,要什么时候才能找到...
于是超市老板设计了一套货品摆放的规则:
一楼放生活用品类,二楼放食品类...
一楼的货架又分洗漱用品类,洗涤用品类,五金类...
洗漱用品类又按商品品牌进行归类...
同一个品牌又按价格排序...
以便快速找到客人想要的商品。
与上述例子类比,我们数据库里存储了1000万条用户数据,要从中查询到name = “zengjing”的记录,如果一条一条去查,要花多长时间?
我们先来看看此时数据库的行为,由于我们想要得到名字为zengjing的数据,在查询到第一个符合条件的行后,数据库并不会停止查询,因为可能还有其他符合条件的数据。
所以,必须一行行地查找直到最后一行,这就意味着数据库不得不检查上千万行数据才能找到所有姓名为zengjing的用户。这就是所谓的全表扫描。要花多长时间请大家自行脑洞...
于是,此时需要索引。对要查询的字段建立索引其实就是把该字段按照一定的方式排序。
注意!建立的索引只对该字段有用,如果查询的字段改变,那么这个索引也就无效了,比如上面数据库用户表是按照姓名的第一个字母排序的,那么你想要找手机号为18888888888的用户就不能用该索引了,还有就是如果索引太多也会降低查询的速度!
探究数据结构
欲了解索引的工作原理,必先了解其数据结构是什么?
如上述场景描述,显而易见,索引的目的就是加快查询速度。什么样的数据结构适合做索引,换句话说,什么样的数据结构能加速查询?
哈希,例如HashMap,查询/插入/修改/删除的平均时间复杂度都是O(1);
树,例如平衡二叉搜索树,查询/插入/修改/删除的平均时间复杂度都是O(lg(n));
任何遇到二择其一的时候必然会去比较其优劣,或者,找到更适合的场景。对数据结构有一定了解的童鞋都知道,在寻找值时哈希表效率极高,不管是读请求,还是写请求,哈希类型的索引,都要比树型的索引更快一些,那为什么,索引结构要设计成树型呢?
(请驻足于此,思考10分钟...)
我们来看看上面的场景中,SQL是这样的:select* from user where name = “zengjing”;此时确实是哈希索引更快,因为每次都只查询一条记录。
哈希索引的工作方式是将列的值通过hash算法计算出hash值,将其作为索引的键值(key),和键值相对应实际的值(value)是指向该表中相应行的指针。
因为哈希表基本上可以看作是关联数组,一个典型的数据项就像“zengjing => 0x7a656e676a696e67″,而0x7a656e676a696e67是对内存中表中包含“zengjing”这一行的引用。
在哈希索引的中查询一个像“zengjing”这样的值,并得到对应行的在内存中的引用。
那么下面场景呢?
group by, order by
>, <
哈希表是无序的数据结构,对于很多类型的查询语句哈希索引都无能为力。
例如上面几个场景。因为哈希表只适合查询键值对,也就是说查询相等的查询(例:WHERE name = “zengjing”)。
哈希表的键值映射也表明了其键的存储是无序的。这就是为什么哈希索引通常不是数据库索引默认的数据结构,因为若将其作为索引的数据结构,并没有树那么灵活。
二叉树 -> B Tree -> B + Tree
都是树,WHY B + TREE?先看看这几棵树:
1、二叉树
二叉树,是最为大家所熟知的一种数据结构,这里就不多介绍了,它为什么不适合用作数据库索引?
(请驻足于此,思考10分钟...)
A. 当数据量大的时候,树的高度会比较高,数据量大的时候,查询会比较慢;
B. 每个节点只存储一个记录,可能导致一次查询有很多次磁盘IO;
2、B Tree
B Tree,由图中所示,其明显特点为:
A. N叉搜索
B. 叶子节点和非叶子节点都存储数据
C. 中序遍历,可获得所有节点
B Tree被作为实现索引的数据结构被创造出来,是因为它能够完美的利用“局部性原理”:
A. 内存读写块,磁盘读写慢,而且慢很多;
B. 磁盘预读:磁盘读写并不是按需读取,而是按页预读,一次会读一页(4K)的数据,每次加载更多的数据,如果未来要读取的数据就在这一页中,可以避免未来的磁盘IO,提高效率;
C. 局部性原理:软件设计要尽量遵循“数据读取集中”与“使用到一个数据,大概率会使用其附近的数据”,这样磁盘预读能充分提高磁盘IO;
B Tree为何适合做索引?
A. 由于是m分叉的,高度能够大大降低;
B. 每个节点可以存储j个记录,如果将节点大小设置为页大小,例如4K,能够充分的利用预读的特性,极大减少磁盘IO;
3、B + Tree
B+ Tree,仍是N叉搜索树,在B Tree的基础上,做了一些改进:
A. 非叶子节点不再存储数据,数据只存储在同一层的叶子节点上;
B. 叶子之间,增加了链表,获取所有节点,不再需要中序遍历;
这些改进让B+ Tree比B Tree有更优的特性:
A. 范围查找,定位min与max之后,中间叶子节点,就是结果集,不用中序回溯;范围查询在SQL中用得很多,这是B+ Tree比BTree最大的优势。
B. 叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;非叶子节点存储记录的PK,用于查询加速,适合内存存储;
C. 非叶子节点,不存储实际记录,而只存储记录的KEY的话,那么在相同内存的情况下,B+ Tree能够存储更多索引;
最后,量化说下,为什么N叉的B+ Tree比二叉搜索树的高度有很大很大降低?
大概计算一下:
A. 局部性原理,将一个节点的大小设为一页,一页4K,假设一个KEY有8字节,一个节点可以存储500个KEY,即j=500
B. N叉树,大概n/2<= j <=n,即可以差不多是1000叉树
C. 那么:
一层树:1个节点,1*500个KEY,大小4K
二层树:1000个节点,1000*500=50W个KEY,大小1000*4K=4M
三层树:1000*1000个节点,1000*1000*500=5亿个KEY,大小1000*1000*4K=4G
可以看到,存储大量的数据(5亿),并不需要太高树的深度(高度3),索引也不是太占内存(4G)。
总结
数据库的索引最常用B+树:
A. 很适合磁盘存储,能够充分利用局部性原理,磁盘预读;
B. 很低的树高度,能够存储大量数据;
C. 索引本身占用的内存很小;
D. 能够很好的支持单点查询,范围查询,有序性查询。
以上就是关于《干货 | 聊聊数据库索引》的全部内容,本文网址:https://www.7ca.cn/baike/25551.shtml,如对您有帮助可以分享给好友,谢谢。