MySQL索引相关知识点及面试常问题

常见面试题

  首先引入索引相关的常见的面试题,让咱们更客观的学习索引相关的内容mysql

    • 问:数据库中最多见的慢查询优化方式是什么?
      • 同窗A:加索引。
    • 问:为何加索引能优化慢查询?
      • 同窗A:...不知道
      • 同窗B:由于索引其实就是一种优化查询的数据结构,好比Mysql中的索引是用B+树实现的,而B+树就是一种数据结构,能够优化查询速度,能够利用索引快速查找数据,因此能优化查询。
    • 问:你知道哪些数据结构能够提升查询速度?(听到这个问题就感受此处有坑...)
      • 同窗B:哈希表、彻底平衡二叉树、B树、B+树等等。
    • 问:那这些数据结构既然都能优化查询速度,那Mysql种为什么选择使用B+树?
      • 同窗B:...不知道
    • 问:有一个titles表,主键由empno,title,fromdate三个字段组成。那么如下几个语句会用到索引吗?
      select * from employees.titles where emp_no=1
      select * from employees.titles where title='1'
      select * from employees.titles where emp_no='1'andtitle=1
      select * from employees.titles where title='1'andemp_no=1
    • 问:为何哈希表、彻底平衡二叉树、B树、B+树均可以优化查询,为什么Mysql独独喜欢B+树?哈希表有什么特色?
    • 答:假若有这么一张表(表名:sanguo):                                                                                                                                                                        

    如今对name字段创建哈希索引,注意字段值所对应的数组下标是哈希算法随机算出来的,因此可能出现哈希冲突。那么对于这样一个索引结构,如今来执行下面的sql语句:
select * from sanguo where name='周瑜'

能够直接对‘周瑜’按哈希算法算出来一个数组下标,而后能够直接从数据中取出数据并拿到锁对应那一行数据的地址,进而查询那一行数据。那么若是如今执行下面的sql语句:面试

select * from sanguo where name>'周瑜'

则无能为力,由于哈希表的特色就是能够快速的精确查询,可是不支持范围查询。算法

若是用彻底平衡二叉树呢?sql

仍是上面的表数据用彻底平衡二叉树表示以下图(为了简单,数据对应的地址就不画在图中了。):数据库

图中的每个节点实际上应该有四部分:数组

  • 左指针,指向左子树
  • 键值
  • 键值所对应的数据的存储地址
  • 右指针,指向右子树

另外须要提醒的是,二叉树是有顺序的,简单的说就是“左边的小于右边的”假如咱们如今来查找‘周瑜’,须要找2次(第一次曹操,第二次周瑜),比哈希表要多一次。并且因为彻底平衡二叉树是有序的,因此也是支持范围查找的。数据结构

 

若是用B树呢?学习

仍是上面的表数据用B树表示以下图(为了简单,数据对应的地址就不画在图中了。):优化

 

能够发现一样的元素,B树的表示要比彻底平衡二叉树要“矮”,缘由在于B树中的一个节点能够存储多个元素。spa

 

若是用B+树呢?

仍是上面的表数据用B+树表示以下图(为了简单,数据对应的地址就不画在图中了。)

 咱们能够发现一样的元素,B+树的表示要比B树要“胖”,缘由在于B+树中的非叶子节点会冗余一份在叶子节点中,而且叶子节点之间用指针相连。

 

那么B+树到底有什么优点呢?

  这里咱们用“反证法”,假如咱们如今就用彻底平衡二叉树做为索引的数据结构,咱们来看一下有什么不妥的地方。实际上,索引也是很“大”的,由于索引也是存储元素的,咱们的一个表的数据行数越多,那么对应的索引文件其实也是会很大的,实际上也是须要存储在磁盘中的,而不能所有都放在内存中,因此咱们在考虑选用哪一种数据结构时,咱们能够换一个角度思考,哪一个数据结构更适合从磁盘中读取数据,或者哪一个数据结构可以提升磁盘的IO效率。回头看一下彻底平衡二叉树,当咱们须要查询“张飞”时,须要如下步骤:

  1. 从磁盘中取出“曹操”到内存,CPU从内存取出数据进行笔记,“张飞”<“曹操”,取左子树(产生了一次磁盘IO)
  2. 从磁盘中取出“周瑜”到内存,CPU从内存取出数据进行笔记,“张飞”>“周瑜”,取右子树(产生了一次磁盘IO)
  3. 从磁盘中取出“孙权”到内存,CPU从内存取出数据进行笔记,“张飞”>“孙权”,取右子树(产生了一次磁盘IO)
  4. 从磁盘中取出“黄忠”到内存,CPU从内存取出数据进行笔记,“张飞”=“张飞”,找到结果(产生了一次磁盘IO)

  同理,回头看一下B树,咱们发现只发送三次磁盘IO就能够找到“张飞”了,这就是B树的优势:一个节点能够存储多个元素,相对于彻底平衡二叉树因此整棵树的高度就下降了,磁盘IO效率提升了。

  而B+树是B树的升级版,只是把非叶子节点冗余一下,这么作的好处是为了提升范围查找的效率。

  到这里能够总结出来,Mysql选用B+树这种数据结构做为索引,能够提升查询索引时的磁盘IO效率,而且能够提升范围查询的效率,而且B+树里的元素也是有序的。

那么,一个B+树的节点中到底存多少个元素合适呢?

  其实也能够换个角度来思考B+树中一个节点到底多大合适?

  答案是:B+树中一个节点为一页或页的倍数最为合适。由于若是一个节点的大小小于1页,那么读取这个节点的时候其实也会读出1页,形成资源的浪费;若是一个节点的大小大于1页,好比1.2页,那么读取这个节点的时候会读出2页,也会形成资源的浪费;因此为了避免形成浪费,因此最后把一个节点的大小控制在1页、2页、3页、4页等倍数页大小最为合适。

那么,Mysql中B+树的一个节点大小为多大呢?

  这个问题的答案是“1页”,这里说的“页”是Mysql自定义的单位(其实和操做系统相似),Mysql的Innodb引擎中一页的默认大小是16k(若是操做系统中一页大小是4k,那么Mysql中1页=操做系统中4页),可使用命令SHOW GLOBALSTATUS like 'Innodbpagesize'; 查看。而且还能够告诉你的是,一个节点为1页就够了。

为何一个节点为1页(16k)就够了?

  解决这个问题,咱们先来看一下Mysql中利用B+树的具体实现。

  Mysql中MyISAM和innodb使用B+树

  一般咱们认为B+树的非叶子节点不存储数据,只有叶子节点才存储数据;而B树的非叶子和叶子节点都会存储数据,会致使非叶子节点存储的索引值会更少,树的高度相对会比B+树高,平均的I/O效率会比较低,因此使用B+树做为索引的数据结构,再加上B+树的叶子节点之间会有指针相连,也方便进行范围查找。上图的data区域两个存储引擎会有不一样。

 

 MyISAM中的B+树

  MYISAM中叶子节点的数据区域存储的是数据记录的地址,MyISAM存储引擎在使用索引查询数据时,会先根据索引查找到数据地址,再根据地址查询到具体的数据。而且主键索引和辅助索引没有太多区别。

 主键索引

 

 

辅助索引

  

 

 InnoDB中的B+树

   InnoDB中主键索引的叶子节点的数据区域存储的是数据记录,辅助索引存储的是主键值

主键索引

辅助索引

 

 Innodb中的主键索引和实际数据时绑定在一块儿的,也就是说Innodb的一个表必定要有主键索引,若是一个表没有手动创建主键索引,Innodb会查看有没有惟一索引,若是有则选用惟一索引做为主键索引,若是连惟一索引也没有,则会默认创建一个隐藏的主键索引(用户不可见)。另外,Innodb的主键索引要比MyISAM的主键索引查询效率要高(少一次磁盘IO),而且比辅助索引也要高不少。因此,咱们在使用Innodb做为存储引擎时,咱们最好:

  1. 手动创建主键索引
  2. 尽可能利用主键索引查询

 

回到咱们的问题:为何一个节点为1页(16k)就够了?

  对着上面Mysql中Innodb中对B+树的实际应用(主要看主键索引),能够发现B+树中的一个节点存储的内容是:

  • § 非叶子节点:主键+指针
  • § 叶子节点:数据

 

  假设咱们一行数据大小为1K,那么一页就能存16条数据,也就是一个叶子节点能存16条数据;再看非叶子节点,假设主键ID为bigint类型,那么长度为8B,指针大小在Innodb源码中为6B,一共就是14B,那么一页里就能够存储16K/14=1170个(主键+指针),那么一颗高度为2的B+树能存储的数据为:117016=18720条,一颗高度为3的B+树能存储的数据为:11701170*16=21902400(千万级条)。因此在InnoDB中B+树高度通常为1-3层,它就能知足千万级的数据存储。在查找数据时一次页的查找表明一次IO,因此经过主键索引查询一般只须要1-3次IO操做便可查找到数据。因此也就回答了咱们的问题,1页=16k这么设置是比较合适的,是适用大多数的企业的,固然这个值是能够修改的,因此也能根据业务的时间状况进行调整。

 

最左前缀原则

  咱们模拟数据创建一个联合索引 select *,concat(right(emp_no,1),"-",right(title,1),"-",right(from_date,2)) from employees.titles limit 10;

 

咱们判断一个查询条件能不能用到索引,咱们要分析这个查询条件能不能利用某个索引缩小查询范围

对于 select from employees.titles where emp_no=1是能用到索引的,由于它能利用上面的索引全部查询范围,首先和第一个节点“4-r-01”比较,1<4,因此能够直接肯定结果在左子树,同理,依次按顺序进行比较,逐步能够缩小查询范围。对于select from employees.titles where title='1'是不能用到索引的,由于它不能用到上面的因此,和第一节点进行比较时,没有empno这个字段的值,不能肯定到底该去左子树仍是右子树继续进行查询。对于 select * from employees.titles where title='1' and emp_no=1是能用到索引,按照咱们的上面的分析,先用title='1'这个条件和第一个节点进行比较,是没有结果的,可是mysql会对这个sql进行优化,优化以后会将empno=1这个条件放到第一位,从而能够利用索引。

相关文章
相关标签/搜索