MySQL优化
一、为什么要学习MySql优化
二、查询过慢原因:
有规律速度过慢:用户访问量激增–mysql集群
无规律速度过慢:跟表中数据量变化有关系。查询语句优化
三、判断查询语句是否需要优化依赖工具
慢查询记录
explain执行计划
相关日志文件
四、MySq1查询语句中七个查询命令特征(根据七个查询执行优先级)
1. from:
[作用]: 1)将硬盘上的表文件加载到内存中,生成一个全新的临时表
2)定位内存中已经存在的临时表
[注意] :
1)在一个查询语句中,第一个执行命令永远都是from
2)from定位的是内存中一个临时表,这个临时表必须手动指定表名.
2.where:
[作用]:
1)where命令操作的由from命令生成的临时表
2)where命令循环遍历当前临时表中每一个数据行将满足条件的数据行保存到一个全新的临时表
[注意] :
由于where命令每次操作只是一个数据行,因此在where使用过程中,是无法使用聚合函数作为判断条件
3. group by
[作用]:
1)首先对临时表中数据进行一次排序处理
2)然后将具有相同特征的数据行保存到同一个临时表
[注意] :
1)多字段分组
首先,分组字段执行顺序对于查询结果来说没有任何影响的,然后,从第二个分组字段开始,操作临时表是上一个分组字段生成的临时表
2)如果select操作临时表是由group by提供。
此时select将遍历group by生成的每一个临时表,在操作具体临时表时,select只会读取指定字段中第一个数据行内容
4. having:
[作用]:负责将group by生成的临时表中不满足条件的临时表进行删除
[注意]:
1)having命令不能独立出现的,I只能出现在group by命令后面
2)having命令每次操作的是一个临时表,因此选择判断条件应该来自于聚合函数
5. select:
[作用]:
1) select操作的临时表,由From或则where命令来提供的。select将制定字段中所有内容读取出来。将读取的内容组成一个全新的临时表
2) select操作的临时表,由group by或则having命令提供的此时select将遍历生成的每一个临时表,在操作具体临时表时,
select只会读取指定字段中第一个数据行内容
6. order by:
[作用] : 专门针对select生成的临时表中数据行进行排序,将排序后内容组成一个全新的临时表.
[注意]: 如果order by使用字段名进行排序时,字段名可以不出现在select生成的临时表
如果order by使用字段顺序进行排序时,索引位置必须在select查询语句中出现
7.limit:
[作用] :对临时表中数据行进行截取
五。查询语句特征:
1.七个查询命令中,除了having命令之外。剩下的六个查询命令执行完毕后都会生成全新的临时表
2.七个查询命令中,除了from命令之外,剩下的六个查询命令操作的临时表都是上一个查询命令生成临时表
3.在当前查询命令执行完毕后,mysql自动的将上一个查询命令生成的临时表进行销毁处理,所以在一个查询语句执行完毕后,
用户只能看到最后一个查询命令生成的临时表。
4.在进行多字段分组查询时(group by xxx,ooo,yyy),从第二个分组字段开始,操作的临时表是上一个分组字段生成的临时表
六。七个查询命令中,哪些命令执行效率过慢:
1.执行速度最慢的:group by
[原因] : group by执行时首先将临时表中数据进行[排序] 然后在进行分组
2. order by:
[原因]:需要对select生成的临时表中数据行进行一次[排序],然后将排好顺序的数据行组成一一个全新的临时表
3.where:
[原因]:需要对表中所有数据行进行遍历。如果临时表中数据行过多话,导致where执行效率非常慢.
4.limit:
[原因]如果指定起始行数过大,导致查询速度变慢
5.对于多表查询来说,没有指定表文件加载顺序,也会导致查询速度过慢
七。查询命令执行过慢的原因:
1.执行时需要对所有的数据进行排序
2.执行时需要对表中所有数据行进行遍历
3.I/O流加载表文件次数
八。索引介绍:
1. [定义] :索引存在于对应的索引文件中,索引本质就是一组排好了顺序的一组数据
2.[作用]:
1)在索引数据中进行排序时,避免再次排序
2)在索引数据中对数据行进行定位时,避免全表扫描
3. [索引文件位置]
1)如果当前表依赖于INNODB存储引擎,索引存在FRM文件
2)如果当前表依赖于MYISAM存户引擎,索引存在MYI文件
4. [索引基本操作] :
1)查看表文件中已经存在的索引
SHOW INDEX FROM 表
2)创建索引
CREATE INDEX 索引名 ON 表名(字段)
3)删除索引
DROP INDEX 索引名 on 表名
九。索引分类:
1.聚簇索引与非聚簇索引
2.主键索引,唯一性索引
3.单个索引,复合索引
十。聚簇索引与非聚簇索引
1.聚簇索引:
1)只能来自于采用INNODB存储引擎表的数据
2)MySq1自动将采用了INNODB存储引擎表中主键建立索引,这个索引就是聚簇索引。
3)如果当前表中没有主键,MySql将会选择一个添加唯一性约束的字段作为聚簇索引。
4)如果当前表中既没有主键字段,也没有添加了唯一性约束字段 MySq1将随机选取一个字段作为聚簇索引。
5)在采用INNODB存储引擎的表文件中,必然会存在一个聚簇索引。
6)在采用INNODB存储引擎的表文件中,只能有一个聚簇索引。
7)在表文件中其他字段上建立的索引都是非聚簇索引。
2.非聚簇索引:
1)是由开发人员自行创建.
2)对于采用了INNODB存储引擎表,除了一个聚簇索引之外,其他字段上创建的索引都是非聚簇索引
3)在采用MyIsam存储引擎的表中,创建的所有索引都是非聚簇索引
3.聚簇索引与非聚簇索引区别:
1)聚簇索引:数据节点存储的[当前数据所在行数]以及[当前数据所在数据行内容]
因为聚簇索引的这种结构,所以在查询时可以直接在定位数据节点上,读取当前数据所在数据行中字段信息不需要使用I/O流
到硬盘上表文上进行读取。因此执行效率较快。
2)非聚簇索引:数据节点存储的[当前数据所在行数],由于数据节点存储的当前数据坐在行数,没有其相关内容,所以定位之后,
需要使用I/O流到硬盘上表文件中定位数据行其他字段内容。因此执行效率相对较慢
十一。主键索引与唯一性索引
1.主键索引:
如果当前表文件中字段添加了主键约束,mySql主动的将当前字段上数据进行排序,其生成的索引被称为主键索引
2.唯一性索引:
如果当前表文件中字段添加了唯一性约束,mysq1主动的将当前字段上数据进行排序,其生成的索引被称为唯一性索引。唯一性索引不包含Null
3.普通索引:
如果当前当前表文件中字段上没有添加任何约束,此时在这个字段上创建的索引就是普通索引。
*******执行效率
主键索引>唯一性索引>普通索引
十二。单字段索引与复合索引
1.单字段索引创建
CREATE INDEX索引名on表名(字段名)
2.复合索引创建.
CREATE INDEX 索引名 on 表名(字段名1,字段名2,字段名2)
十三。二分查找算法:
将一组数组中平均值作为[根节点],将小于[平均值]数据放在[根节点]左边,将大于[平均值]数据放在[根节点]右边,定位数据时,避免整体数据扫描,只需要对一半的数据进行比较,如果数据量较大时,虽然可以节省将近一半的遍历数据量,但是依然会消耗较多时间
十四。平衡二叉树算法:
1.是二分查找算法的升级版。
2.[特点]:
1)产生[数据节点]与[叶子节点]概念
2)每一个[数据节点]只能有两个指针。一个指针指向小于当前[数据节点]的数据。另一个指针指向大于当前[数据节点]的数据。
3.[缺点]:
1)如果索引相关的字段中数据发生变化时,导致平衡二叉树中内容失效,需要重新创建这个平衡二叉树。因此维护成本较高。
2)如果根据多个条件进行查询时,对二叉树进行多次遍历,同时也会产生多个I/O流
十五。BTREE算法:
1.[介绍]:
1)是平衡二叉树算法升级版
2)是目前各种数据数据在索引上使用的主要算法
2. [特点] :
1)使用区间数据来代替数据节点上具体的数据,降低维护难度
2)在连续叶子节点之间使用指针来进行连接,降低遍历次数
十六。Explain执行计划详解
1.执行计划介绍通过explain查看查询语句执行的效率。在进行SQL优化的主要手段通过explain可以查看如下信息
1) 查看表的加载顺序
2) 查看 sql 的查询类型
3) 哪些索引可能被使用,哪些索引又被实际使用了
4) 表之间的引用关系
5) 一个表中有多少行被优化器查询。
6)其他额外的辅助信息
2.EXPLAIN中id属性:
1).[作用]:通过id属性指出当前查询语句中相关表文件的加载顺序
2).[含义]:
情况1:如果被加载的表文件的ID属性内容是相同的,则根据自上而下的顺序进行加载的.
情况2:如果被加载的表文件的ID属性内容是不相同的。则ID较大的表,就现被加载。ID较小的表,则后被加载.
情况3:如果被夹在的表文件的ID属性内容既有相同有存在不同时。则ID较大的表,就现被加载。ID相同的表则按照自上而下顺序进行加载。
mysql> explain select ename,dname from emp join dept on emp.deptno = dept.deptno;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 1 | SIMPLE | dept | ALL | PRIMARY | NULL | NULL | NULL | 5 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
2 rows in set (0.04 sec)
id都为1,所以是自上而下进行加载的
mysql> explain select empno,ename,(select dname from dept where emp.deptno=dept.deptno) as dname from emp;
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 2 | DEPENDENT SUBQUERY | dept | eq_ref | PRIMARY | PRIMARY | 4 | bjpowernode.emp.DEPTNO | 1 | NULL |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
2 rows in set (0.09 sec)
id分别为1和2,所以先加载id大的表--dept表,再加载id小的表--emp表
mysql> explain select ename,dname from emp join (select deptno,dname from dept group by dname) d on emp.deptno = d.deptno;
+----+-------------+------------+------+---------------+------+---------+------+------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows |
+----+-------------+------------+------+---------------+------+---------+------+------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 |
| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 5 |
| 2 | DERIVED | dept | ALL | NULL | NULL | NULL | NULL | 5 |
+----+-------------+------------+------+---------------+------+---------+------+------+
3 rows in set (0.10 sec)
id有大有小,而且还具有相同的,先加载id大的--id为2的表,再加载id为1的表,有两个所以是自上而下进行加载。
3.执行计划之select_type
对当前查询语句中的查询类型进行判断
1) Simple表示当前查询语句是一个简单查询语句。不包含子查询,不包含联合查询(union)不包含连接查询(join)
mysql> explain select ename,sal,job from emp;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
2) Primary如果执行的是一个包含子查询的查询,或者是一个联合查询。Primary指向的外部查询语句或者是联合查询中的第一个子查询语句
子查询:Primary指向的是外部查询语句
mysql> explain select ename,sal,(select dname from dept where emp.deptno = dept.deptno) as dname from emp;
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 2 | DEPENDENT SUBQUERY | dept | eq_ref | PRIMARY | PRIMARY | 4 | bjpowernode.emp.DEPTNO | 1 | NULL |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+-------+
2 rows in set (0.00 sec)
联合查询:Primary指向的是联合查询中的第一个子查询语句
mysql> explain select ename from emp union select dname from dept;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 2 | UNION | dept | ALL | NULL | NULL | NULL | NULL | 5 | NULL |
| NULL | UNION RESULT | | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
3 rows in set (0.03 sec)
连接查询:没有Primary
mysql> explain select ename,dname from emp join dept on emp.deptno = dept.deptno;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 1 | SIMPLE | dept | ALL | PRIMARY | NULL | NULL | NULL | 5 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
2 rows in set (0.00 sec)
3) DEPENDENT SUBQUERY表示当前查询语句是一个子查询。并且执行条件依赖与外部查询提供的条件(依赖子查询)
mysql> explain select ename,job,sal,(select max(sal) from emp e2 where e2.job = emp.job) from emp;
+----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 2 | DEPENDENT SUBQUERY | e2 | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+--------------------+-------+------+---------------+------+---------+------+------+-------------+
2 rows in set (0.02 sec)
4) SUBQUERY表示当前查询是一个子查询。并且这个子查询在执行时不需要得到外部查询的帮助(独立子查询)
mysql> explain select ename,job,sal,(select count(*) from dept) from emp;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
| 2 | SUBQUERY | dept | index | NULL | PRIMARY | 4 | NULL | 5 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.02 sec)
4.执行计划之possible_keys
表示当前查询语句执行时可能用到的索引有哪些,在possible keys可能出现多个索引,但是这些索引未必在本次查询使用到
5.执行计划之key
表示当前查询语句真实使用的索引名称如果这个字段为null. 则有两中可能一个是当前表中没有索引。
二是当前表有索引但是失效了
6.EXPLAIN中type属性:
type内容分类:
1) all: 全表扫描
mysql> explain select ename from emp;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)
2) index:定位数据行时,没有使用到索引(全表扫描),但是在已经定位的数据行中读取字段时候。却是使用索引,
也就是说使用select命令时,根据索弓|读取数据。(执行效率较低)
mysql> explain select sal from emp;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | emp | index | NULL | sal_index | 9 | NULL | 14 | Using index |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
1 row in set (0.00 sec)
3) range: 定位数据行,使用到索引。使用区间定位方式where age<=23
这种状态实际上是一种不稳定的状态。如果定位的数据行超过了表中总行数30%以上。导致mysq|放弃使用索引定位。
mysql> explain select * from emp where sal >3000;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
| 1 | SIMPLE | emp | range | sal_index | sal_index | 9 | NULL | 3 | Using index condition |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
4) ref: 定位数据行,使用到索引,采用直等方式where ename='mike'
同时满足定位的条件的数据行只有一行。
mysql> explain select ename from emp where sal = 950;
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------+
| 1 | SIMPLE | emp | ref | sal_index | sal_index | 9 | const | 1 | NULL |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-------+
1 row in set (0.00 sec)
5) const: 定位数据行,使用到索引,采用主键索引直等方式where empno= 10
mysql> explain select ename from emp where empno = 7369;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | emp | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
6) system.:操作表只有一行数据, 并且数据内容是固定不变的。
十七。索引失效:
# 1.如果索引字段上使用了函数,导致索引失效
mysql> explain select * from emp where upper(ename) = 'SIMTH';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.33 sec)
# 2.如果索引字段上使用运算,导致索引失效
mysql> explain select * from emp where sal+200=1000;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.09 sec)
# 3.如果索引字段上产生了(隐式类型转换),导致索引失效
mysql> explain select * from emp where ename=200;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | ename_index | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
# 4.如果索引字段上进行了模糊查询,只支持前置条件模糊查询
mysql> explain select * from emp where ename like 'S%';
+----+-------------+-------+-------+---------------+-------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------------+---------+------+------+-----------------------+
| 1 | SIMPLE | emp | range | ename_index | ename_index | 33 | NULL | 2 | Using index condition |
+----+-------------+-------+-------+---------------+-------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
失效:
mysql> explain select * from emp where ename like '%a';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from emp where ename like '%a%';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)