问:数据库中最多见的慢查询优化方式是什么? 答:加索引。 问:为何加索引能优化慢查询? 答1:...不知道 答2:由于索引其实就是一种优化查询的数据结构,好比Mysql中的索引是用B+树实现的,而B+树就是一种数据结构,能够优化查询速度,能够利用索引快速查找数据,因此能优化查询。 问:你知道哪些数据结构能够提升查询速度?(听到这个问题就感受此处有坑...) 答:哈希表、彻底平衡二叉树、B树、B+树等等。 问:那这些数据结构既然都能优化查询速度,那Mysql种为什么选择使用B+树? 答:...不知道mysql
SHOW INDEX FROM employees.titles
; 面试
select * from employees.titles where emp_no = 1
select * from employees.titles where title = '1'
select * from employees.titles where emp_no = '1' and title = 1;
select * from employees.titles where title = '1' and emp_no = 1;
复制代码
到底什么是索引(Index)? 大学老师是这么定义的:索引就像书的目录 Mysql官网是这么定义的:Indexes are used to find rows with specific column values quickly 我是这么定义的:索引是一种优化查询的数据结构算法
为何哈希表、彻底平衡二叉树、B树、B+树均可以优化查询,为什么Mysql独独喜欢B+树?sql
哈希表(Hash table,也叫散列表),是根据键值(Key value)而直接进行访问的数据结构。也就是说,它经过把键值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫作散列函数,存放记录的数组叫作散列表。数据库
哈希表的作法其实很简单,就是把Key经过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,而后就将该数字对数组长度进行取余,取余结果就看成数组的下标,将value存储在以该数字为下标的数组空间里。而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就能够充分利用到数组的定位性能进行数据定位。数组
哈希表的特色是什么? 假若有这么一张表(表名:sanguo):数据结构
如今对name字段创建哈希索引:函数
注意字段值所对应的数组下标是哈希算法随机算出来的,因此可能出现哈希冲突。 那么对于这样一个索引结构,如今来执行下面的sql语句:性能
select * from sanguo where name = '周瑜'
复制代码
能够直接对‘周瑜’按哈希算法算出来一个数组下标,而后能够直接从数据中取出数据并拿到锁对应那一行数据的地址,进而查询那一行数据。 那么若是如今执行下面的sql语句:优化
select * from sanguo where name > '周瑜'
复制代码
则无能为力,由于哈希表的特色就是能够快速的精确查询,可是不支持范围查询。
仍是上面的表数据用彻底平衡二叉树表示以下图(为了简单,数据对应的地址就不画在图中了。):
图中的每个节点实际上应该有四部分: 1.左指针,指向左子树 2.键值 3.键值所对应的数据的存储地址 4.右指针,指向右子树
另外须要提醒的是,二叉树是有顺序的,简单的说就是“左边的小于右边的” 假如咱们如今来查找‘周瑜’,须要找2次(第一次曹操,第二次周瑜),比哈希表要多一次。并且因为彻底平衡二叉树是有序的,因此也是支持范围查找的。
仍是上面的表数据用B树表示以下图(为了简单,数据对应的地址就不画在图中了。):
咱们能够发现一样的元素,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+树里的元素也是有序的。
这里有必要先来了解一下磁盘IO的原理。
机械硬盘
固态硬盘
从上面的原理咱们也能知道,固态硬盘比机械硬盘快的最根本最简单的缘由就是:固态硬盘使用的电路进行读写,而机械硬盘使用的机械运动。 其实不论是机械硬盘仍是固态硬盘都是存储介质,真正控制读写的是操做系统。
一个磁盘由大小相同且同轴的圆形盘片组成,磁盘能够转动(各个磁盘必须同步转动)。在磁盘的一侧有磁头支架,磁头支架固定了一组磁头,每一个磁头负责存取一个磁盘的内容。磁头不能转动,可是能够沿磁盘半径方向运动(实际是斜切向运动),每一个磁头同一时刻也必须是同轴的,即从正上方向下看,全部磁头任什么时候候都是重叠的(不过目前已经有多磁头独立技术,可不受此限制)。
磁盘片结构图
盘片被划分红一系列同心环,圆心是盘片中心,每一个同心环叫作一个磁道,全部半径相同的磁道组成一个柱面。磁道被沿半径线划分红一个个小的段,每一个段叫作一个扇区,每一个扇区是磁盘的最小存储单元,大小通常为521字节。
当须要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即肯定要读的数据在哪一个磁道,哪一个扇区。为了读取这个扇区的数据,须要将磁头放到这个扇区上方,为了实现这一点,磁头须要移动对准相应磁道,这个过程叫作寻道,所耗费时间叫作寻道时间,而后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫作旋转时间。
固态硬盘(Solid State Drives),用固态电子存储芯片阵列而制成的硬盘,由控制单元和存储单元(FLASH芯片、DRAM芯片)组成。固态硬盘在接口的规范和定义、功能及使用方法上与普通硬盘的彻底相同,在产品外形和尺寸上也彻底与普通硬盘一致。
每一个SSD都有一个控制器(controller)将存储单元链接到电脑,主控器能够经过若干个通道(channel)并行操做多块存储单元
一个Flash Page由两个或者多个Die(又称chips组成),这些Dies能够共享I/0数据总线和一些控制信号线。一个Die又能够分为多个Plane,而每一个Plane又包含多个多个Block,每一个Block又分为多个Page。以Samsung 4GB Flash为例,一个4GB的Flash Page由两个2GB的Die组成,共享8位I/0数据总线和一些控制信号线。每一个Die由4个Plane组成,每一个Plane包含2048个Block,每一个Block又包含64个4KB大小的Page
Host是经过LBA(Logical BlockAddress,逻辑地址块)访问SSD的,每一个LBA表明着一个Sector(通常为512B大小),操做系统通常以4K为单位访问SSD,咱们把Host访问SSD的基本单元叫用户页(Host Page)。而在SSD内部,SSD主控与Flash之间是Flash Page为基本单元访问Flash的,咱们称Flash Page为物理页(Physical Page)。Host每写入一个Host Page, SSD主控会找一个Physical Page把Host数据写入,SSD内部同时记录了这样一条映射(Map)。有了这样一个映射关系后,下次Host须要读某个Host Page 时,SSD就知道从Flash的哪一个位置把数据读取上来。
计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也一般会立刻被使用。 因此操做系统为了提升效率,读取数据时每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,操做系统也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这里的必定长度叫作页,也就是操做系统操做磁盘时的基本单位。通常操做系统中一页的大小是4Kb。
因此,回到咱们的问题,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 GLOBAL STATUS like 'Innodb_page_size';查看。而且还能够告诉你的是,一个节点为1页就够了。
为何一个节点为1页(16k)就够了? 解决这个问题,咱们先来看一下Mysql中利用B+树的具体实现。
一般咱们认为B+树的非叶子节点不存储数据,只有叶子节点才存储数据;而B树的非叶子和叶子节点都会存储数据,会致使非叶子节点存储的索引值会更少,树的高度相对会比B+树高,平均的I/O效率会比较低,因此使用B+树做为索引的数据结构,再加上B+树的叶子节点之间会有指针相连,也方便进行范围查找。 上图的data区域两个存储引擎会有不一样。
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;
复制代码
那么对应的B+树为
咱们判断一个查询条件能不能用到索引,咱们要分析这个查询条件能不能利用某个索引缩小查询范围
对于select * from employees.titles where emp_no = 1
是能用到索引的,由于它能利用上面的索引全部查询范围,首先和第一个节点“4-r-01”比较,1<4,因此能够直接肯定结果在左子树,同理,依次按顺序进行比较,逐步能够缩小查询范围。
对于select * from employees.titles where title = '1'
是不能用到索引的,由于它不能用到上面的因此,和第一节点进行比较时,没有emp_no这个字段的值,不能肯定到底该去左子树仍是右子树继续进行查询。 对于select * from employees.titles where title = '1' and emp_no = 1是能用到索引,按照咱们的上面的分析,先用title='1'这个条件和第一个节点进行比较,是没有结果的,可是mysql会对这个sql进行优化,优化以后会将emp_no=1这个条件放到第一位,从而能够利用索引。