在某大神童靴的强烈安利下最近阅读了深入浅出MySQL一书,这本书的第三部分,介绍了MySQL数据库的一些优化方法,非常值得一读,推荐大家如果有时间都可以阅读一下,下面博客的主要内容实际是个人的读书笔记。主要内容包括以下方面:
本系列博文的主要针对对象是开发人员,而非DBA,有些内容不会进行详细介绍。
我们的的实践主要是在MySQL提供的测试数据sakila上,测试数据的下载地址
下载地址,下载好数据后解压,进入目录通过MySQL运行命令source sakila-mv-schema.sql
和source sakila-mv-data.sql
,就可以将数据导入到MySQL数据库中。
这里一定要在文件夹下进行数据导入,否则会有报错。
说起优化SQL语句,首先我们需要知道哪些SQL语句的效率是有问题的,我们要能够在生产环境排查出有问题的SQL语句,这样才能对SQL进行优化,所以,我们先来介绍几个查看SQL语句执行效率方面的命令。
我们可以通过show global status like "Com%"
命令来查看这个数据库从启动到现在为止所有SQL类型的执行频率。同时可以将global更改为local,来查询当前session的SQL执行频率。
MySQL提供了一种日志叫做慢查询日志,通过这个日志,我们可以定位那些执行效率较低的SQL,这种方式一般由DBA管理,这里不做详细介绍,如果想要了解可以自行上网查找相关介绍。
当我们怀疑某个SQL有低效的问题,我们就可以通过explain命令来分析其执行计划,找出低效的原因。
下面解释一下每列的含义:
1) select_type : 表示select类型,simple(简单表,不需要表连接或子查询),primary(主查询,即外层的查询,主要指的是嵌套查询中的最外层,表连接中最前面的select语句),union(表连接中的第二个或后面的select语句),subquery(子查询中的第一个select)等。
2) table : 输出结果集的表
3) type : 表示表的连接类型,是一个重要的部分,性能由好到差分别为system、const、eq_ref、ref、 ref_or_null、index_merge、unique_subquery、index_subquery、range、index、all
4) possible_keys : 查询时可能用到的索引
5) key : 最终实际使用的索引
6) key_len : 索引字段的长度
7) rows : 扫描行的数量,越少越好
8) extra : 执行情况的额外说明
通过sql的执行计划我们可以等到SQL实际的执行顺序,使用的索引,当我们的查询没有使用到索引时,我们的查询就会很慢,所以我们可以通过执行计划来适当修改或新建索引。
这部分我们来介绍索引的一些知识,包括建立索引需要考虑的一些点以及索引的一些基础知识。
MySQL中的索引主要有四种,分别B树索引、哈希索引、全文索引和R-tree索引,所有的存储引擎都支持B树索引,哈希索引只有MEMORY引擎支持,全文索引只有MyISAM引擎支持,MyISAM引擎支持R-tree索引,但使用较少
MyISAM和InnoDB默认创建的都是BTREE索引,索引实际上也是需要占用存储,所以,如果我们在一个很大的表上建立联合索引,那么索引数据会增长的十分快,这个时候我们可以考虑使用前缀索引,只对索引字段的前N个字符创建索引,这样可以有效减少索引的大小。
前缀索引同样存在缺点,在排序ORDER BY和分组GROUP BY时无法使用,因此需要根据实际情况处理。
在默认情况下,Memory存储引擎默认使用HASH索引,但也支持BTREE索引。
两种不同的索引有其不同的适用范围,HASH索引的适用范围较小,需要注意。
下面给出例子,首先创建一个Memory引擎的表。
范围搜索实际不能适用hash索引,见下图
将hash索引改为btree索引再查看,发现搜索过程中成功使用索引。
对于InnoDB引擎的表,最好指定主键,并且尽量选择较短的数据作为主键。因为InnoDB表的记录默认会按照一定顺序存储,如果有明确主键,则会安札主键顺序保存,如果没有主键,但是有唯一索引,那么会按照唯一索引顺序保存,如果两者都没有,那么表会自动生成一个内部列,按照这个顺序保存,按照主键或内部列进行访问时最快的。
匹配全值,对索引中所有列都指定具体值。type字段为const,表示是常量。
MySQL的ICP特性,优化了查询,将某些情况下的过滤操作下放到存储引擎,也就是说当我们在低版本的MySQL上有些查询最终可能是using where,但是到高版本上,相同的查询,因为ICP特性就变成了using index condition,这也是MySQL的优化,降低了不必要的IO操作。
下面介绍几种优化器不会使用索引的情况,我们在写SQL语句和设计索引的时候应该尽量避免这些情况,提高效率。
有关查询的SQL通常的优化方法就是通过索引来进行优化,这些在上面已经介绍的差不多了,那么还有一些其他常用SQL的优化将在下面介绍。
当使用InnoDB引擎时,尽量关闭唯一性校验,当导入结束后再打开,同时插入的数据应尽量按照主键顺序排列,并且关闭自动提交,当导入数据结束后再打开。
相关命名如下
set UNIQUE_CHECK=0;
set SUTOCOMMIT=0;
当使用MyISAM引擎时,我们应该尽量关闭索引更新。
命令如下
alter table tbl_name disable keys;
首先先了解一下customer表上的索引,方便后面的介绍。
首先介绍第一种,就是using index,通过查询有序索引返回有序结果。
第二种就是using filesort,这种情况表示进行了额外的排序工作,这种情况并不表示使用了磁盘文件进行排序操作,这只是表示进行了额外的排序操作,至于排序操作时如何进行的,则取决于MySQL的决定。
下面这个例子可以清晰的表明这种情况,即使所有的数据都在索引中,依然有using filesort
filesort是通过排序算法,将数据在sort_buffer_size系统变量设置的内存排序区中进行排序,如果排序区放不下,那么会将磁盘上的数据进行分块,再分别进行排序,最后合并为有序的结果集。这个排序区是线程独占的,同一时刻,MySQL中可能存在多个。
那么,现在优化主要有两个方法,一种是减少额外的排序,通过索引直接返回有序数据,一种是优化filesort。
首先介绍第一种。WHERE条件和ORDER BY条件使用的索引是同一个,并且索引顺序和ORDER BY的顺序是相同的时,并且ORDER BY 字段都是升序或降序。如果不满足以上三个条件,那么肯定要进行额外的排序,就会出现filesort。因此,我们写ORDER BY的时候尽量满足。
有时,就算完全满足条件,也不能确保不出现filesort,例如:
因此,这里又涉及到filesort的优化。
filesort有两种排序算法。
两种算法的选择是通过max_length_for_sort_data的大小和query语句取出字段总大小来决定的。因此,通过增大max_length_for_sort_data的值,可以让更多的排序可以采用一次扫描排序,但是,需要注意,这个值也不能过大,否则反而会造成效率降低。
同时,我们也可以增大排序区sort_buffer_size的大小,让排序更多的在内存中完成,提高效率,同样,也不可以过大,因为这个区域是线程独占。
同样的,我们也可以通过减少query中的字段来达到加快排序的目的。因此select语句尽量不适用select *,直接写出需要的字段。
GROUP BY 语句实际上会对后面的字段进行排序,隐含操作ORDER BY,因此,当我们不需要进行排序时,我们可以增加字句ORDER BY NULL来禁止排序。
SQL的子查询可以一次性完成很多逻辑上很多步骤才能完成的SQL操作,同时可以避免事物或表死锁,写起来也十分容易。但是,有些时候其效率较连接查询更低。所以一般使用join来优化子查询操作。
实际上子查询的过程中产生了临时表,因此相比join操作多了临时变的建立和销毁,同时临时变中的数据无法使用索引,更进一步降低了速度,所以,当我们的SQL可以使用join时,尽量不要使用子查询。
下面的例子给出同一个查询数据的不同实现
select * from customer where customer_id not in (select customer_id from payment)
select * from customer c left join payment p on c.customer_id = p.customer_id where p.customer_id is null
其实,OR条件的优化,在前面已经提到过了,只有当OR连接的每一列都有索引时,才会使用索引,因此,在写SQL的时候尽量让每列都有索引,这样速度会大大加快。
一般分页查询的时候,我们都会通过创建覆盖索引的方式提高性能。但是有些时候,我们的数据较多,会发生深分页,也就是limit 1000,10
,这种情况,此时查询了1010条记录,排序后却只需要返回1001到1010的10条记录,造成了很大的浪费。
针对这种情况,我们给出两种通用做法。
优化后我们按照索引分页改写SQL后,已经看不到全表扫描了。这里需要注意,我们的前提是已经建立了title上的索引。
给出优化前的sql
可以看出这个查询使用全表扫描。
优化后
这种优化方法比较简便,但是这种方法有一定的局限性。当排序字段存在重复元素时,这种方法会出现错误,遗漏数据。因此我们需要根据情况比较如何选择。
原文:https://www.cnblogs.com/qmlingxin/p/9094037.html