:调优必备索引知识
Posted 风清扬逍遥子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:调优必备索引知识相关的知识,希望对你有一定的参考价值。
1、Sql预热
常见的七种Join理论,看图就非常清晰
- 左连接:A独有+在A中的B部分
select * from A left join B on A.key = B.key
因为没有满足A的B,所以只能补Null
- 内连接:A和B的交集
select * from A inner join B on A.key = B.key
- 右连接:B独有+在B中的A部分
select * from A right join B on A.key = B.key
因为没有满足B的A,所以只能补Null
上面三个是非常常见且常用的Join,那么还有四类Join:
- A去掉B的部分,A的独有
对比左连接,其实是把中间属于A的B部分给干掉了
那么Sql我们这么写:
select * from A LEFT JOIN B on A.aid = B.bid 左连接
where B.bid is null B不在A的部分
因为本身左连接已经把所有A的值都包含出来了,同时多了的部分,就是B在A的部分,
只要拿到为空的部分,其实就是B不在A的部分!!这里有点绕,好好琢磨下。
- B去掉A的部分,B的独有
对比右连接,其实是把中间属于B的A部分给干掉了
那么Sql我们这么写:
select * from A RIGHT JOIN B on A.aid = B.bid 右连接
where A.aid is null A不在B的部分
- A和B去掉交集部分
实际上是不是上面两个的合体?
- 外连接:A和B的并集
select * from A outer join B on A.key = B.key
这个Sql是语法错误的,因为mysql不支持outer join,那么我们是不是可以用Sql来表示出outer join的核心?
我们知道Union是去重且排序的,而Union all是不去重且不排序的
所以其实在开发中常用的应该是左连接、内连接、外连接
那么怎么样方便记忆呢,其实很简单,A left join B,左连接这个时候往A这里靠拢;因为得到的是A的全部,当B不满足A所在列的时候,只能补Null;
A right join B,右连接往B靠拢,因为得到的B的全部,当A不满足B所在的列时候,只能补Null;
不论A和B怎么互换位置,以left join或者 right join为主,left往左偏,左边什么表往什么表偏;right往右偏,右边什么表往右边偏;是不是很好记忆?
2、索引简介
其实很多面试者面试的时候,这两个词都解释不清楚,一说索引,就上来抛个新华字典举例子,啰嗦而不真实,我总结为一句话:索引是数据结构,是一个排好序且快速查找的数据结构。
官方的那些比较难以理解的话我还是要贴出来:
MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质: 索引是数据结构,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
左边是数据表,一共有两列七条记录,最左边的是数据记录内存对应的物理地址。为了加快 Col2 的查找,可以维护一个 右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。
2.1、优点
- 提高数据检索的效率,降低数据库的IO成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
所以记住,索引功能是:搜索+排序
2.2、缺点
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件,每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
- 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
2.3、举例说明
假设有这个Sql语句:
select password from users where username ='mick';
这个sql很明显用到了查询,如果对username没有添加索引,那么Mysql是不知道怎么去定位到m的,所以要一个一个记录去搜,一直搜到m开头,再继续往后直到搜索到mick这个名字,这个情况,如果运气好,你第一个就定位到了,如果运气差,你只能等到最后一个看看会不会是你要的记录。索引不加索引就可能会导致全表扫!
如果我按照了a,b,c...字母字典排好序,即我在username这个字段上加了索引,那么Mysql就可以通过索引的关系,直接找到m,进而找到mick这个记录返回;
所以,既然索引影响了查找和排序,自然而然我们就可以联系到,索引会影响到where后面的查找,和order by后面的排序!这句话务必记住。
3、Mysql索引
在Mysql中数据在磁盘是这么存储的:
默认在windows下的D:\\ProgramData\\MySQL\\MySQL Server 5.7\\Data下,对应的文件夹就是表名,存在这么几个文件
.frm是描述了表的结构
.myd保存了表的数据记录
.myi则是记录表的索引
所以索引也会存储在磁盘上!
3.1、B+tree索引
MySQL 使用的是 B+tree 索引:在很多资料上写的Mysql引擎用的是B-树,B-树和B+树其实本质上是没有什么区别的,只是在数据的存储这块,升级成为了B+树,后面面试问到,一定要说是B+树。
一颗 B+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示)
如磁盘块 1 包含数据项 17 和 35,包含指针 P1、P2
P1表示小于 17 的磁盘块,P2表示在 17 和 35 之间的磁盘块,P3表示大于35的块;
真实的数据存在于叶子节点即 3、5、9、10、13、15、28、29、36、60、75、79、90、99,非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,17、35 并不真实存在于数据表,你可以理解为这些是数据查找的范围。
【查找过程】:如果要查找数据项 29,那么首先会把磁盘块 1 由磁盘加载到内存,此时发生一次 IO,在内存中用二分查找确定 29 在 17 和 35 之间,锁定磁盘块 1 的 P2 指针,内存时间因为非常短(相比磁盘的 IO)可以忽略不计,通过磁盘块 1 的 P2 指针的磁盘地址把磁盘块 3 由磁盘加载到内存,发生第二次 IO,29 在 26 和 30 之间,锁定磁盘块 3 的 P2 指针,通过指针加载磁盘块 8 到内存,发生第三次 IO,同时内存中做二分查找找到29,结束查询,总计3次IO。
真实的情况是,3层的 b+树可以表示上百万的数据,我可以把树放扁一点,如果上百万的数据查找只需要3次 IO,性能提高将是巨大的, 如果没有索引,每个数据项都要发生一次 IO,那么总共需要百万次的 IO,显然成本非常非常之高了!!
3.2、B-Tree索引
来看下B-Tree,实际上B+树是B-树的升级版,其他的不说,最大的区别就是B树的非叶子节点也会存数据,而B+树的非叶子节点不存数据,存储的是在建立B树的过程中,处于每个数据中间位置的冗余索引,比如:
如果自己感兴趣,可以拿下面的网址自己去画下就知道我说的意思了。
B-Tree Visualizationhttps://www.cs.usfca.edu/~galles/visualization/BTree.html
3.3、B-树和B+树的区别
在 B-树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而 B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。从这个角度看 B- 树的性能好像要比 B+树好,而在实际应用中却是 B+树的性能要好些。因为 B+树的非叶子节点不存放实际的数据, 这样每个节点可容纳的元素个数比 B-树多,树高比 B-树小,更扁,这样带来的好处是减少磁盘访问次数。尽管 B+树找到 一个记录所需的比较次数要比 B-树多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际中 B+树的性能可能还会好些,而且 B+树的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有文件,一个表中的所有记录等),这也是很多数据库和文件系统使用 B+树的缘故。
思考:为什么说 B+树比 B-树更适合实际应用中操作系统的文件索引和数据库索引?
- B+树的磁盘读写代价更低,B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对 B-树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就 越多。相对来说 IO 读写次数也就降低了。
- B+树的查询效率更加稳定,由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率都一样。
如果后面对B树和B+树具体的数据结构感兴趣,可以自行去了解,本文只介绍重点关于Mysql方面的知识。
总之记住,索引,类似大学图书馆建立书目的索引,提高检索查询效率,降低数据库IO成本;通过对数据进行排序,降低CPU消耗。索引=提高检索+数据排序
3.4、Mysql索引分类
3.4.1、单值索引
概念:即一个索引只包含单个列,一个表可以有多个单列索引
假设有个表customer,创建后建立单独索引
create index idx_customer_name on customer(customer_name);
CREATE TABLE customer ( id INT(10) UNSIGNED AUTO_INCREMENT, customer_no VARCHAR(200), customer_name VARCHAR(200), PRIMARY KEY(id) );
3.4.2、唯一索引
概念:索引列的值必须唯一,但允许有空值
这里有人容易把唯一索引和单值索引搞混,唯一索引比如说一个表users(id, username,id_card),username用户名肯定不能作为唯一索引,因为名字会重复,但是id_card身份证这个列,一定是唯一的,所以如果这个列加上了索引,就一定是唯一索引,且这个列字段值可以为空,假设黑户...开玩笑!
同样建立唯一索引:
create unique index idx_id_card on users(id_card);
3.4.3、主键索引
概念:设定为主键primary key后数据库会自动建立主键索引,innodb为聚簇索引,什么是聚簇索引呢,暂时你理解为,每个Mysql 的表只能有一个聚簇索引。一般情况下就是该表的主键。为了充分利用聚簇索引的聚簇的特性,所以 innodb 表的主键列尽量选用有序的顺序 id,而不建议用无序的id,比如 uuid 这种类型。
3.4.4、复合索引
概念:即一个索引包含多个列,这个也很好理解,开发中也很常见!
create index idx_no_name on customer(customer_no,customer_name);
4、索引的语法
操作 命令 创建 create [unique] index [indexName] on table_name(column)) 删除 drop index [indexName] on table_name 查看 show index from table_name 使 用 alter命令 alter table table_name add primary key (column_list) : 该语句添加一个主键,这意味着索引值必须是唯一 的,且不能为 NULL。 alter table table_name add primary key (column_list) alter table table_name add index index_name (column_list): 添加普通索引,索引值可出现多次。 alter table table_ name add fulltext index_name (column_list):该语句指定了索引为 fulltext,用于全文索引 5、索引的创建时机
5.1、适合创建索引的情况
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引
- 查询中与其它表关联的字段,外键关系建立索引
- 单键/组合索引的选择问题, 组合索引性价比更高
- 查询中排序的字段,如order by create_time,排序字段若通过索引去访问将大大提高排序速度
- 查询中统计或者分组字段
5.2、不适合创建索引的情况
- 表记录太少
- 经常增删改的表或者字段
- Where 条件里用不到的字段
- 过滤性不好的不适合建索引
好了先说到这吧,本章节深度剖析了索引的介绍,和具体使用场景,下一节我们正式开始Explain执行计划的分析和Sql调优案例,真刀真枪开始上经验!!
以上是关于:调优必备索引知识的主要内容,如果未能解决你的问题,请参考以下文章
MySQL优化必备之执行计划explain,索引基本知识,索引数据结构推演