1.背景介绍
1、索引是什么
索引是对数据库表中一个或多个列的值进行排序的结构
2、索引可以做什么
加快检索表中数据,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构
2.知识刨析
索引分类:
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于同时使用多条单列索引
全文索引:对文本的内容进行分词,进行搜索
索引方式:
Hash
B-Tree
B+Tree
RTree
建索引的几点原则
1.选择唯一性索引
唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。
2、限制索引的数目
索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。
3、尽量使用数据量少的索引
如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。
4、尽量使用前缀来索引
如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。
5、删除不再使用或者很少使用的索引
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
6、最左前缀匹配原则,非常重要的原则。
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a 1=”” and=”” b=”2” c=”“> 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
3.常见问题
1、索引是怎样加快效率的?
在没有索引的情况下,数据库查找数据的时候会进行全盘扫描来寻找需要的数据,而索引将经常需要查询的数据单独建立了有序的结构,从而提高了效率
2、不同引擎有什么区别?
没有最好的引擎,只有最适合的引擎。
ISAM:读取操作的速度很快,不支持事务处理,不能在表损坏后恢复数据。
MyISAM:读取操作更快,不支持事务,不能在表损坏后恢复数据。
InnoDB:慢很多,但是支持事务处理和外来键。
4.解决方案
确定需求后在决定引擎
MyISAM:
(1)做很多count 的计算;
(2)插入不频繁,查询非常频繁;
(3)没有事务。
InnoDB:
(1)可靠性要求比较高,或者要求事务;
(2)表更新和查询都相当的频繁,并且表锁定的机会比较大的情况指定数据引擎的创建
5.编码实战
6.扩展思考
1、Hash(类似HashMap)
1、使用特定的哈希函数将索引列中的值生成哈希码 hashcode ,不同的值生成的哈希码不一样
2、哈希索引将所有的哈希码和指向每个数据行的指针保存到索引中
3、如果生成的哈希码是一样的,则在那一行生成链表
4、使用了查询语句后,引擎检测使用了索引,然后会将值转换成哈希码,找到对应的索引中的哈希码,最后取出数据对比,以确保是要查找的行
例子:现有如下一个表
有7为学生及相应的得分,其中有3位重名
在姓名列建立Hash索引,假设有这样的哈希函数F(),他返回这样的值
(都是假设数据,并非真实数据)
那么建立的Hash索引数据结构如下
每个槽的编号是排序的,但是数据行不是
相同的姓名(Addision)生成的哈希码是相同的,将会以链表方式解决
假如现在要查询 select score from table where name = 'Fred'
MySQL会先计算 'Fred' 的哈希值,即 f( ' Fred ' )=8464
然后使用该值寻找对应的槽,然后就找到对应的指针“指向第4行的指针”
最后会到第4行比对name是否是 ' Fred ' ,确保是要查找的行,输出:60
限制:
1、哈希索引不存储字段值,不能用来做覆盖索引
2、哈希索引数据不是按照索引值排序的,无法用于排序
3、哈希索引是按照索引列全部内容进行计算哈希值的,所以不支持范围查找
4、哈希冲突多的话,效率会受影响,不稳定,存储引擎必须遍历链表中的所有行指针,逐行对比知道找到相应的行。同样的删除也会受影响
二叉树
一个简单的二叉树结构<br/>
以此为例,假如找H,那么要先找到F(代表该二叉树)
然后指向E,最后指向H
一个简单的二叉树结构
以此为例,假如找H,那么要先找到F(代表该二叉树)
然后指向E,最后指向H
2、B-Tree平衡多路二叉树
如图是一个3阶B-Tree
系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
例子
假设现在有如图这样一个B-Tree索引,找键10携带的数据
1、根据根节点找到磁盘块1,读入内存。【I/O操作第1次】
2、比较关键字10在小于17区间,找到磁盘块1的指针P1。
3、根据P1指针找到磁盘块2,读入内存。【I/O操作第2次】
4、比较关键字10在区间(8,12),找到磁盘块2的指针P2。
5、根据P2指针找到磁盘块6,读入内存。【I/O操作第3次】
6、在磁盘块6中的关键字列表中找到关键字10。
分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作由于内存中的关键字是一个有序表结构,
可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素,
但是该表中查询数据最多只使用3次I/O操作,但是相对于普通二叉树,和平衡二叉树,B-Tree缩减了节点个数,
使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。
3、B+Tree
只有叶子记录关键字记录的指正,非叶子节点只进行数据索引,所有数据地址必须要到叶子节点才能获取到,所以每次数据查询的次数都一样。
例子
如图是一个3阶B+Tree
假设现在有如图这样一个B+Tree索引,找键63携带的数据
1、根据根节点找到磁盘块,读入内存。【磁盘I/O操作第1次】
2、比较关键字63在区间[28,65),找到磁盘块的指针P2。
3、根据P2指针找到磁盘块,读入内存。【磁盘I/O操作第2次】
4、比较关键字63在区间[56,65),找到磁盘块的指针P3。
5、根据P3指针找到磁盘块,读入内存。【磁盘I/O操作第3次】
6、在磁盘块中的关键字列表中找到关键字63。
分析上面过程,发现不管查找哪个数据都需要3次I/O操作,
所以查询速度稳定,查询速度非常接近二分法。
另外B+Tree中非叶子节点不需要保存关键字记录的指针,
每个节点能保存的关键字大大增加,所以B+Tree比B-Tree更有效率、更稳定。
4、聚集索引(InnoDB引擎)
聚集索引的主索引的叶子节点存储的就是数据本身,辅助索引的叶子节点存储的是对应数据的主键键值。
在指定主键时会建立主索引
每个主键都携带着数据行,找到主键即找到了数据行
如果没有指定主键,系统内部会自动生成一个“行id”,
但是无法操作该数据,然后生成一个包含该id的聚集索引
所以系统中总会有且仅有一个聚集索引。
聚集索引中的索引结构
左边是主索引,右边是辅助索引
聚簇索引的数据是根据主键的顺序保存。
如果只需要查找索引的列,就尽量不要加入其它的列,这样会提高查询效率。
5、非聚集索引(MyISAM引擎)
非聚集索引结构
非聚集索引中的主索引和辅助索引几乎是一样的,主索引不允许重复和空值,他们的叶子都存储着键值对应的数据的物理地址
所以非聚集索引中的索引和数据是分开存储的
非聚簇索引中的数据是根据数据的插入顺序保存。
7.参考文献
1. 百度百科:数据库索引:https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B4%A2%E5%BC%95/8751686?fr=aladdin
2.How does InnoDB behave without a Primary Key?:https://blog.jcole.us/2013/05/02/how-does-innodb-behave-without-a-primary-key/
3.从Mysql源代码角度分析一句简单sql的查询过程:https://blog.csdn.net/vipshop_fin_dev/article/details/79688717
4.BTree和B+Tree详解:"https://blog.csdn.net/chuixue24/article/details/80027689
5.为什么索引可以加速查询?:https://blog.csdn.net/tongdanping/article/details/79878302
6.哈希索引:https://blog.csdn.net/olizxq/article/details/82313489
7.btree索引和hash索引的区别:https://www.cnblogs.com/ziqiumeng/p/7680204.html
8.更多讨论
鸣谢
感谢观看,如有出错,恳请指正