【MySQL(2)| MySQL索引机制】

什么是索引?

索引是存储引擎用于快速找到记录数据行的一种分散存储的数据结构。sql

索引对于良好的性能很是关键,尤为是当表中的数据量愈来愈大时,索引对性能的影响愈发重要。数据库

在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,可是当数据量逐渐增大时,性能则会急剧降低。编程

因此 正确的建立合适的索引是提高数据库查询性能的基础bash

为何要使用索引?

  • 索引能够把随机IO编程顺序IO
  • 索引能极大的减小存储殷勤须要扫描的数据量
  • 索引能够帮助咱们在进行分组、排序等操做时,避免使用临时表

索引有哪些类型?

索引有不少类型,能够为不一样的场景提供更好的性能。微信

MySQL中,索引是在存储引擎层面实现的,因此,并无统一的索引标准,通常来讲,不一样存储引擎的工做方式是不同的,也不是全部的存储引擎都支持全部类型的索引数据结构

哈希索引

哈希索引基于哈希表实现,只有精确匹配索引全部列的查询才有效。性能

对于每个数据行,存储引擎都会对全部的索引列根据必定的计算规则计算出一个哈希码,而后哈希索引将全部的哈希码存储在索引中,同时在哈希表中会保存一个指向对应数据行的指针大数据

MySQL中,Memory引擎是显式支持哈希索引的,他也是该引擎默认的索引类型,值得注意的一点是:Memory引擎是支持非惟一哈希索引的,也就是说若是多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希表中。优化

哈希索引的应用场景

根据本人的理解,这种直接经过哈希索引的存储引擎,由于索引自身只须要存储对应的哈希值,因此索引的结构十分紧凑,这会让哈希索引查找的速度很是快ui

哈希索引的一些限制
  • 哈希索引只包含哈希值和行指针,而不存储字段值,因此不能使用索引中的值来避免读取行(即不能使用哈希索引来作覆盖索引扫描),不过,访问内存中的行的速度很快(由于memory引擎的数据都保存在内存里),因此大部分状况下这一点对性能的影响并不明显。
  • 哈希索引数据并非按照索引列的值顺序存储的,因此也就没法用于排序
  • 哈希索引也不支持部分索引列匹配查找,由于哈希索引始终是使用索引的所有列值内容来计算哈希值的。如:数据列(a,b)上创建哈希索引,若是只查询数据列a,则没法使用该索引。
  • 哈希索引只支持等值比较查询,如:=,in(),<=>(注意,<>和<=>是不一样的操做),不支持任何范围查询(必须给定具体的where条件值来计算hash值,因此不支持范围查询)。
  • 访问哈希索引的数据很是快,除非有不少哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中全部的行指针,逐行进行比较,直到找到全部符合条件的行。
  • 若是哈希冲突不少的话,一些索引维护操做的代价也很高,如:若是在某个选择性很低的列上创建哈希索引(即不少重复值的列),那么当从表中删除一行时,存储引擎须要遍历对应哈希值的链表中的每一行,找到并删除对应的引用,冲突越多,代价越大。

B-Tree索引

B-Tree索引使用B-Tree树数据结构存储数据,大多数MySQL引擎都支持这种索引(Archive引擎是个例外)

在这里插入图片描述

详细的B-Tree和B+Tree能够参考这篇文章

B树被做为实现索引的数据结构被创造出来,是由于它可以完美的利用“局部性原理”。

什么是局部性原理与磁盘预读?

因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理当一个数据被用到时,其附近的数据也一般会立刻被使用。程序运行期间所须要的数据一般比较集中。

因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。

预读的长度通常为**页(page)**的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。

B树为什么适合作索引?

(1)因为是m分叉的,高度可以大大下降;

(2)每一个节点能够存储j个记录,若是将节点大小设置为页大小,例如4K,可以充分的利用预读的特性,极大减小磁盘IO;

注意:高度下降的缘由在于:

  • 在利用了局部性原理前提下,咱们把一个节点的大小设为一页,一页4K,假设一个KEY有8byte,一个节点能够存储500个KEY,即j=500

  • m叉树,大概m/2<= j <=m,便可以差很少是1000叉树

  • 一层树:1个节点,1*500个KEY,大小4K

​ 二层树:1000个节点,1000500=50W个KEY,大小10004K=4M

​ 三层树:10001000个节点,10001000500=5亿个KEY,大小10001000*4K=4G

因此:《高性能Mysql第三版》这本书也说了,通常的B+树都不会超过三层,也就意味着绝大数数据经过三次IO就能够找到

B树,它的特色是:

(1)再也不是二叉搜索,而是m叉搜索;

(2)叶子节点,非叶子节点,都存储数据;

(3)中序遍历,能够得到全部节点;

B+树的特色是:

B+树,是在B树的基础上,作了一些改进

  • 非叶子节点再也不存储数据,数据只存储在同一层的叶子节点上;

  • 叶子之间,增长了链表,获取全部节点,再也不须要中序遍历;

以上改进让B+树比B树有更优的特性:

  • 范围查找,定位min与max以后,中间叶子节点,就是结果集,不用中序回溯(范围查询在SQL中用得不少,这是B+树比B树最大的优点);

  • 叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;非叶子节点存储记录的PK,用于查询加速,适合内存存储;

  • 非叶子节点,不存储实际记录,而只存储记录的KEY的话,那么在相同内存的状况下,B+树可以存储更多索引;

索引体现形式

Myisam引擎

使用Myisam引擎的表在数据库中会存在三个文件

以user表为例:

一个是表定义文件 user.frm

一个是索引存储文件 user.MYI

还有一个是数据存储文件 user.MYD

由于Myisam引擎的索引和数据是分开存储的,叫作非汇集索引UnClustered Index)。而且在B+tree树的叶子节点存储的是数据行的地址,在检索数据时,以此从根节点开始检索,直到找到对应的关键字,而后到数据区获取数据行地址,最后根据这个数据行地址返回检索的数据行

Innodb引擎

Innodb和Myisam最大的不一样是他是以主键为索引来组织数据的存储,叫作聚簇索引(Clustered Index),也叫做汇集索引

可能你会说,若是个人表没有主键怎么办?你也尽可放心:

  • 若是你没有主键,innodb会选择一个惟一的非空索引代替;

  • 若是没有这样的惟一索引可用,Innodb会本身建立一个隐式的row-id索引用于组织存储数据,

使用Innodb引擎的表在数据库中会存在三个文件

以teacher表为例:

一个是表定义文件 teacher.frm

一个是数据和索引存储文件 teacher.IBD

此处引入一个聚簇索引(也叫汇集索引):数据库表行中数据的物理顺序与键值得逻辑顺序(也就是索引)相同,汇集索引并非一种单独的索引类型,而是一种数据存储技术。

当有聚簇索引时,它的全部数据行实际上存放在索引的叶子页中,此处应该注意的是,由于没法同时把数据行存放在两个不一样的地方,因此一个表只能有一个聚簇索引。

聚簇索引的优势
  • 能够把相关数据保存在一块儿
  • 数据访问更快。聚簇索引将索引和数据保存在同一个b-tree中,所以从聚簇索引中查询数据一般比在非聚簇索引中查找要快
  • 使用覆盖索引(后文会有介绍)扫描的查询能够直接使用页节点中主键值
聚簇索引的缺点
  • 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到Innodb表中速度最快的方式。
  • 更新聚簇索引列的代价很高,由于Innodb会强制将每一个被更新的行移动到新的位置
  • 聚簇索引可能致使全表扫描变慢,尤为是行比较稀疏,或者因为页分裂(当行的主键值要求必须将这一行插入到某个已满的页时,存储引擎会将该页分裂成两个页面来存储该行,这就是一页分裂操做,页分裂会占用更多的磁盘空间)致使数据存储不连续的时候
  • 二级索引访问须要两次索引查找

关于最后一点,是由于,二级索引叶子节点保存的并非指向行的物理位置的指针,而是保存的是主键值,这意味着经过二级索引查找行的时候,存储引擎首先须要找到二级索引所对应的主键值,而后经过主键值再去聚簇索引找到对应的行。

小结

MyISAM和InnoDB都使用B+树来实现索引:

  • MyISAM的索引与数据分开存储
  • MyISAM的索引叶子存储指针,主键索引与普通索引无太大区别
  • InnoDB的汇集索引和数据行统一存储
  • InnoDB的汇集索引存储数据行自己,普通索引存储主键
  • InnoDB必定有且只有一个汇集索引
  • InnoDB建议使用趋势递增整数做为PK,而不宜使用较长的列做为PK

其余索引策略

覆盖索引

若是一个索引包含全部须要查询的字段的值,咱们就称之为“覆盖索引”

换言之,若是查询列能够经过索引节点中的关键字直接返回,则该索引称之为覆盖索引

覆盖索引的优势
  • 索引条目一般远小于数据行大小,由于只须要读取索引,天然极大减小了数据访问量,减小数据库IO
  • 将随机IO变成顺序IO:由于索引是按照列值顺序存储的。

联合索引

单列索引能够理解成是一种特殊的联合索引

不少人对多列索引的理解都不够,一个常见错误哪就是 为每一个列建立独立的索引,或者按照错误的顺序建立多列索引

记得以前看过一个博客说 建议把 where条件里边的列都加上索引,实际上这个建议是很是错误的。

在多个列上创建独立的单列索引大部分状况下并不能提升MySQL的查询性能

联合索引有几个选择原则:

  • 常常用的列优先【最左匹配原则】
  • 选择性(离散度)高的列优先【离散度越高 选择性越好】
  • 宽度小的列优先【最少空间原则】

哪么如何选择合适的索引列顺序?

选择合适的索引列顺序

正确的顺序依赖于使用该索引的查询,而且同时须要考虑如何更好的知足排序和分组的须要

在B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,也就是最左匹配原则,因此多列索引的列顺序相当重要。

有一个经验法则:

将选择性最高的列放到索引最前列

在不须要考虑排序和分组时,这个法则是很好的。由于此时索引的做用只是用于优化where条件的查找。这种状况下这样设计的索引可以最快的过滤出须要的行。可是查询性能还和查询条件的具体值以及值得分布有关

判断哪一个列的选择性更高

select * from paymen where staff_id = 2 and customer_id = 584;

select sum(staff_id = 2),sum(customer_id = 584) from payment \G

select count(distinct staff_id)/count(*) as staff_id_selectivity,
count(distinct customer_id)/count(*) as customer_id_selectivity,
count(*)
from payment \G

值越大 选择性更高
复制代码

比你优秀的人比你还努力,你有什么资格不去奋斗!

选用B+Tree的缘由

  • B+树是B-树的变种(PLUS版)多路绝对平衡查找树,他拥有B-树的优点
  • B+树扫库、表能力更强
  • B+树的磁盘读写能力更强
  • B+树的排序能力更强
  • B+树的查询效率更加稳定

总结

最后在网上看到一个顺口溜,以下:

全值匹配我最爱,最左前缀要遵照;

带头大哥不能死,中间兄弟不能断;

索引列上少计算,范围以后全失效;

LIKE百分写最右,覆盖索引不写星;

不等空值还有or,索引失效要少用。

可谓是很精辟

欢迎关注微信公众号 程序猿杂货铺 ID zhoudl_l

微信公众号

在这里插入图片描述
相关文章
相关标签/搜索