MySQL性能调优(1)理解底层B+tree机制

索引是谁实现的

索引是存储引擎实现的:
本文章主要对MySQL常用的MyISAM与InnoDB这两个存储引擎做分析。

索引是什么

索引是为了加速对表中的数据行的检索而创建的一种分散存储的数据结构

为什么要用索引

  1. 索引能极大的减少存储引擎需要扫描的数据量。(比如全表扫描就是在找数据)
  2. 索引可以把随机IO变成顺序IO。(因为索引是有序的这样就能保证找数据的时候稳定性,在程序中不允许有不稳定因素。)

为什么MySQL要用b+tree来实现索引

在这里先推荐一个网址来学习二叉树由来地址

  1. 先来看看二叉查找树 Binary Search Tree

  2. 在来看看平衡二叉查找树(所有节点数高度不会超过1)AVL Trees (Balanced binary search trees)记住图上的磁盘块上存储了数据区的磁盘地址。

  3. 说说为什么MySQL没有选择这些算法而去选择B+Tree

    • 它太深了,数据存在的(高)深度决定着他的IO操作次数,IO操作耗时大这个大家都是知道的。
    • 他太小了,IO操作是很耗时,他一次IO也只能加载一个关键字;保存的东西太少了。
    • 没有很好的利用操作磁盘IO的数据交换特性(操作系统通过硬盘读取数据一次IO操作读取的大小是4k(页为单位)) 这就是为什么SSD在分区时选择4k对其的原因,能使他大大提升读写性能。 二叉树每个节点只有一个关键字,他是存满不了4k的,这样会浪费资源。
    • 也没有利用好磁盘IO的预读能力(空间局部性原理),从而带来频繁的IO操作。空间局部性原理就是: 操作系统每次IO操作读取一页,他会有预读能力,把下一页或者后面几页的数据读取。(注:MySQL定义的一页为16k)

多路平衡查找树B-Tree

1. 上图是一个二三树(没画完 画不下了)。2路以上为多路。关键字个数为路数-1。 2. 从IO太深了来看,他就比平衡二叉树好的多,他一个节点就可以存储更多的数据,这里设置的是3路,我们可以改成更多路。一个节点存储更多关键字。 3. 还可以把一个节点大小设置为16k这样 ``` 用id做索引 int大小 4byte+冗余4byte 节点16k*1024byte 那么我可以保存16*1024/8 个关键字 这就是为什么我们字段大小要设计精确,不要有太多的冗余 ```
  1. 这里的关键字多了,也就解决了空间局部性原理,读取更多数据。

加强版多路平衡查找树B+Tree

1. B+非叶节点不保存数据相关信息,只保存关键字和子节点的引用(扫库扫表能力更强,磁盘读写能力更强)。 2. B+关键字对应的数据保存在叶子节点中(查询更稳定)。 3. B+对排序有天然的优势,叶子结点是顺序排列(排序能力更强)。 4. 采用的是左闭合区间,这样就是关键字于区间就是相同个数。

在存储引擎MyISAM中B+Tree的体现形式

1.用单独文件(后缀myd文件)来存放索引数据,是通过页子节点保存数据地址

单个索引

多个索引 (对应的数据是同一条就是指向一个位置)

在存储引擎Innodb中B+Tree的体现形式

1. Innodb中的索引是与数据存放在一起的,是以主键为索引来组织数据的存储。 如果没有主键,那么他会默认生成一个int长度为6 byte的隐藏主键。 2. Innodb中辅助索引,他会存放主键索引,找辅助数据时,他会再索引一遍主键索引。 3. 聚集索引只在innodb中会出现,因为他是基于主键的索引,有序的。

Innodb与MyISAM对比索引流程

补充知识

列的离散性:

离散性越高、选择性就越好(列数据差别越大,离散性越好)。

最左匹配原则:

对索引中关键字进行计算(对比),一定是从左往右依次进行,且不可跳过。

联合索引
//单列索引是特殊的联合索引
单列索引:节点中关键字[name]
联合索引:节点中关键字[name,phoneNum]
  • 联合索引列选择原则
经常用的列优先 【最左匹配原则】
选择性(离散度)高的列优先【离散度高原则】
宽度小的列优先【最少空间原则】
常用解决方案误区
经排查发现最常用的sql语句:
Select * from users where name = ? ;
Select * from users where name = ? and phoneNum = ?;
机灵的李二狗的解决方案:
create index idx_name on users(name);×
create index idx_name_phoneNum on users(name,phoneNum);

这上面的两种查询:只有第二种方案才能都命中索引。

覆盖索引
  • 如果查询列可通过索引节点中的关键字直接返回,则该索引称之为覆盖索引。
  • 覆盖索引可减少数据库IO,将随机IO变为顺序IO,可提高查询性能
  • 如果我们建立了一个联合索引:name+phone
select name,phone from user where name=?;
  • 这就叫覆盖索引。他就不需要遍历到页子节点,直接就返回关键字数据了。这也就是很多公司不让用select* 的原因。 因为索引的过程是一个顺序的IO 全表扫描是一个随机IO。这一点就体现了索引的效率。

理解一下

  • 索引列的数据长度能少则少。

长度决定磁盘块关键字多少,一次IO操作得到的数据量。

  • 索引一定不是越多越好,越全越好,一定是建合适的。

因为索引在增删改的时候,数据库都会去维护索引。

  • 匹配列前缀可用到索引 like 9999%,like %9999%、like
    %9999用不到索引;

like 9999% 他是可能用到索引可能用不到索引,因为列离散性太差的话,数据量大的话。数据库觉得你索引用上与没用上效率差不多,他就不会用索引。其余两个最左匹配原则索引会失效。

  • Where 条件中 not in 和 <>操作无法使用索引;

因为在索引查找中B+Tree他是根据区间查找数据,如果是不等于:他是不知道去哪个区间的。

  • 匹配范围值,order by 也可用到索引;

MYISAM本身是具有排序功能的,还有就是索引是根据区间找数据,所以匹配范围值可以用到索引(单个索引)。
多用指定列查询,只返回自己想到的数据列,少用select *;

  • 联合索引中如果不是按照索引最左列开始查找,无法使用索引;

最左匹配原则。

  • 联合索引中精确匹配最左前列并范围匹配另外一列可以用到索引;

最左匹配原则。精确就直接指向那条路,然后再范围。

  • 联合索引中如果查询中有某个列的范围查询,则其右边的所有列都无法使用索引;

因为他毕竟是个机器 (mysql的索引机制,第一步就是要确立,搜索的顺序) age>18 他可以通过关键的比对确认走那边子树,但是通过age>18 又加上name=张三这样的条件,他无法确认你要搜索的顺序。

  • 索引列少计算

索引列不能参与计算,保持列“干净”。比如from_unixtime(create_time) = ’2017-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。
为了解决索引列上计算引起的索引失效问题,将计算放到索引列外的表达式上,所以语句应该写成create_time = unix_timestamp(’2017-05-29’)

来一首打油诗

全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;

猜你喜欢

转载自blog.csdn.net/yakax/article/details/104400507
今日推荐