mysql之索引原理

索引宗旨

减少查询范围,无序变为有序

mysql  b+树索引支持范围查找和最左匹配查找

磁盘IO的性能开销远大于内存IO,  每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。

多路搜索树

每个节点有N个元素,和N+1个孩子,尽量减少树的高度,减少磁盘IO次数

IO次数取决于B+树的高度

1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。

因为每次磁盘IO,  寻址到每一页数据量是4k-8k, 如果索引字段越小,磁盘块的数据项就会越多。

磁盘读取数据是以磁盘块为基本单位,

二 磁盘IO与预读

考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

三、索引的数据结构

任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。

如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

###b+树的查找过程
如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

B+树和B树重要区别

  • B树的变种;
  • 分支结点只存索引,不存具体数据;
  • 叶子结点包含所有数据,并且包含叶子结点本身按着关键字大小自小而大顺序连接;

  • B+-tree 的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

 

 

 

 

附常用索引详细解释:

比如订单表m_payrecord_2018_m06   字段  paytime,status 是索引

mysql> explain select count(*) from m_payrecord_2018_m06  where  paytime  <  '2018-06-26' and  itemid = 10000 and subitemid = 2812 and status = 1 ;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    2450008846
Current database: member


+----+-------------+----------------------+------+--------------------------------+----------------+---------+-------+---------+-------------+
| id | select_type | table                | type | possible_keys                  | key            | key_len | ref   | rows    | Extra       |
+----+-------------+----------------------+------+--------------------------------+----------------+---------+-------+---------+-------------+
|  1 | SIMPLE      | m_payrecord_2018_m06 | ref  | i_payrd_status,i_payrd_paytime | i_payrd_status | 1       | const | 3805473 | Using where | 
+----+-------------+----------------------+------+--------------------------------+----------------+---------+-------+---------+-------------+
1 row in set (4.74 sec)

是只用到了索引status。

 

 

mysql> explain select count(*) from m_payrecord_2018_m06  where  paytime <  '2018-06-26' and  itemid = 10000 and subitemid = 2812 ;
+----+-------------+----------------------+------+-----------------+------+---------+------+---------+-------------+
| id | select_type | table                | type | possible_keys   | key  | key_len | ref  | rows    | Extra       |
+----+-------------+----------------------+------+-----------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | m_payrecord_2018_m06 | ALL  | i_payrd_paytime | NULL | NULL    | NULL | 7610946 | Using where | 

未用任何索引。

 

mysql> explain select count(*) from m_payrecord_2018_m06  where  paytime  >  '2018-06-26' and  itemid = 10000 and subitemid = 2812 and status = 1 ;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    2449923513
Current database: member


+----+-------------+----------------------+-------+--------------------------------+-----------------+---------+------+--------+-------------+
| id | select_type | table                | type  | possible_keys                  | key             | key_len | ref  | rows   | Extra       |
+----+-------------+----------------------+-------+--------------------------------+-----------------+---------+------+--------+-------------+
|  1 | SIMPLE      | m_payrecord_2018_m06 | range | i_payrd_status,i_payrd_paytime | i_payrd_paytime | 4       | NULL | 911088 | Using where | 
+----+-------------+----------------------+-------+--------------------------------+-----------------+---------+------+--------+-------------+

1 row in set (4.72 sec)

 

便会用到索引paytime。

是因为mysql优化器会自动优化,当索引检索条件超过1/3就不会使用索引, 且多个索引使用查询数最小的索引。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值