MySQL调优

目录

1.3.MySQL调优

1.3.1.MySQL调优金字塔

1.3.2.查询性能优化

1.3.2.1.什么是慢查询

1.3.2.1.1慢查询基础-优化数据访问

1.3.2.1.2请求了不需要的数据?

1.3.2.1.3.是否在扫描额外的记录

1.3.3.慢查询

1.3.3.1慢查询配置

小结

1.3.4.Explain执行计划

1.3.4.1.什么是执行计划

1.3.4.2.执行计划的语法

1.3.4.3.执行计划详解

id

单SELECT关键字

连接查询

包含子查询

包含UNION子句

table

partitions

type

system

const

eq_ref

ref

range

index

all

possible_keys与key

key_len

rows

filtered

Extra

1.3.5.查询优化器

1.3.6.高性能的索引使用策略

1.3.6.1.不在索引列上做任何操作

1.3.6.2.尽量全值匹配

1.3.6.3.最佳左前缀法则

1.3.6.4.范围条件放最后

1.3.6.5.覆盖索引尽量用

1.3.6.6.不等于要慎用

1.3.6.7.Null/Not 有影响

1.3.6.8.Like查询要当心

1.3.6.9.字符类型加引号

1.3.6.10.使用or关键字时要注意

1.3.6.11.使用索引扫描来做排序和分组

1.3.6.12.排序要当心

1.3.6.13.尽可能按主键顺序插入行

1.3.6.14.优化Count查询

1.3.6.15.优化limit分页

1.3.6.16.关于Null的特别说明


1.3.MySQL调优

1.3.1.MySQL调优金字塔

image.png

很明显从图上可以看出,越往上走,难度越来越高,收益却是越来越小的。

对于架构调优,在系统设计时首先需要充分考虑业务的实际情况,是否可以把不适合数据库做的事情放到数据仓库、搜索引擎或者缓存中去做;然后考虑写的并发量有多大,是否需要采用分布式;最后考虑读的压力是否很大,是否需要读写分离。对于核心应用或者金融类的应用,需要额外考虑数据安全因素,数据是否不允许丢失。所以在进行优化时,首先需要关注和优化的应该是架构,如果架构不合理,即使是DBA能做的事情其实是也是比较有限的。

对于MySQL调优,需要确认业务表结构设计是否合理,SQL语句优化是否足够,该添加的索引是否都添加了,是否可以剔除多余的索引等等

比如硬件和OS调优,需要对硬件和OS有着非常深刻的了解,仅仅就磁盘一项来说,一般非DBA能想到的调整就是SSD盘比用机械硬盘更好。DBA级别考虑的至少包括了,使用什么样的磁盘阵列(RAID)级别、是否可以分散磁盘IO、是否使用裸设备存放数据,使用哪种文件系统(目前比较推荐的是XFS),操作系统的磁盘调度算法选择,是否需要调整操作系统文件管理方面比如atime属性等等。

所以本章我们重点关注MySQL方面的调优,特别是索引。SQL/索引调优要求对业务和数据流非常清楚。在阿里巴巴内部,有三分之二的DBA是业务DBA,从业务需求讨论到表结构审核、SQL语句审核、上线、索引更新、版本迭代升级,甚至哪些数据应该放到非关系型数据库中,哪些数据放到数据仓库、搜索引擎或者缓存中,都需要这些DBA跟踪和复审。他们甚至可以称为数据架构师(Data Architecher)。

1.3.2.查询性能优化

前面的章节我们知道如何设计最优的库表结构、如何建立最好的索引,这些对于高性能来说是必不可少的。但这些还不够—还需要合理的设计查询。如果查询写得很糟糕,即使库表结构再合理、索引再合适,也无法实现高性能。

1.3.2.1.什么是慢查询

慢查询日志,顾名思义,就是查询花费大量时间的日志,是指mysql记录所有执行超过long_query_time参数设定的时间阈值的SQL语句的日志。该日志能为SQL语句的优化带来很好的帮助。默认情况下,慢查询日志是关闭的,要使用慢查询日志功能,首先要开启慢查询日志功能。如何开启,我们稍后再说。

1.3.2.1.1慢查询基础-优化数据访问

查询性能低下最基本的原因是访问的数据太多。大部分性能低下的查询都可以通过减少访问的数据量的方式进行优化。对于低效的查询,一般通过下面两个步骤来分析总是很有效:

1.确认应用程序是否在检索大量超过需要的数据。这通常意味着访问了太多的行,但有时候也可能是访问了太多的列。

2.确认MySQL服务器层是否在分析大量超过需要的数据行。

1.3.2.1.2请求了不需要的数据?

有些查询会请求超过实际需要的数据,然后这些多余的数据会被应用程序丢弃。这会给MySQL服务器带来额外的负担,并增加网络开销,另外也会消耗应用服务器的CPU和内存资源。比如:

查询不需要的记录

一个常见的错误是常常会误以为MySQL会只返回需要的数据,实际上MySQL却是先返回全部结果集再进行计算。我们经常会看到一些了解其他数据库系统的人会设计出这类应用程序。

image.png

image.png

以上SQL你认为MySQL会执行查询,并只返回他们需要的20条数据,然后停止查询。实际情况是MySQL会查询出全部的结果集,客户端的应用程序会接收全部的结果集数据,然后抛弃其中大部分数据。

总是取出全部列

每次看到SELECT*的时候都需要用怀疑的眼光审视,是不是真的需要返回全部的列?很可能不是必需的。取出全部列,会让优化器无法完成索引覆盖扫描这类优化,还会为服务器带来额外的I/O、内存和CPU的消耗。因此,一些DBA是严格禁止SELECT *的写法的,这样做有时候还能避免某些列被修改带来的问题。

尤其是使用二级索引,使用*的方式会导致回表,导致性能低下。

什么时候可以使用SELECT*如果应用程序使用了某种缓存机制,或者有其他考虑,获取超过需要的数据也可能有其好处,但不要忘记这样做的代价是什么。获取并缓存所有的列的查询,相比多个独立的只获取部分列的查询可能就更有好处。

重复查询相同的数据

不断地重复执行相同的查询,然后每次都返回完全相同的数据。比较好的方案是,当初次查询的时候将这个数据缓存起来,需要的时候从缓存中取出,这样性能显然会更好。

1.3.2.1.3.是否在扫描额外的记录

在确定查询只返回需要的数据以后,接下来应该看看查询为了返回结果是否扫描了过多的数据。对于MySQL,最简单的衡量查询开销的三个指标如下:

响应时间、扫描的行数、返回的行数

没有哪个指标能够完美地衡量查询的开销,但它们大致反映了MySQL在内部执行查询时需要访问多少数据,并可以大概推算出查询运行的时间。这三个指标都会记录到MySQL的慢日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法。

响应时间

响应时间是两个部分之和:服务时间和排队时间。

服务时间是指数据库处理这个查询真正花了多长时间。

排队时间是指服务器因为等待某些资源而没有真正执行查询的时间—-可能是等I/O操作完成,也可能是等待行锁,等等。

扫描的行数和返回的行数

分析查询时,查看该查询扫描的行数是非常有帮助的。这在一定程度上能够说明该查询找到需要的数据的效率高不高。

理想情况下扫描的行数和返回的行数应该是相同的。但实际情况中这种“美事”并不多。例如在做一个关联查询时,服务器必须要扫描多行才能生成结果集中的一行。扫描的行数对返回的行数的比率通常很小,一般在1:1和10:1之间,不过有时候这个值也可能非常非常大。

扫描的行数和访问类型

在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式可以查找并返回一行结果。有些访问方式可能需要扫描很多行才能返回一行结果,也有些访问方式可能无须扫描就能返回结果。

在EXPLAIN语句中的type列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。这里列的这些,速度是从慢到快,扫描的行数也是从小到大。你不需要记住这些访问类型,但需要明白扫描表、扫描索引、范围访问和单值访问的概念。

如果查询没有办法找到合适的访问类型,那么解决的最好办法通常就是增加一个合适的索引,为什么索引对于查询优化如此重要了。索引让 MySQL以最高效、扫描行数最少的方式找到需要的记录。

一般 MySQL能够使用如下三种方式应用WHERE条件,从好到坏依次为:

1、在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。

select .... from where a>100 and a <200

2、使用覆盖索引扫描来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。这是在 MySQL服务器层完成的,但无须再回表查询记录。

3、从数据表中返回数据(存在回表),然后过滤不满足条件的记录。这在 MySQL服务器层完成,MySQL需要先从数据表读出记录然后过滤。

好的索引可以让查询使用合适的访问类型,尽可能地只扫描需要的数据行。

如果发现查询需要扫描大量的数据但只返回少数的行,那么通常可以尝试下面的技巧去优化它:

1、使用索引覆盖扫描,把所有需要用的列都放到索引中,这样存储引擎无须回表获取对应行就可以返回结果了

2、改变库表结构。例如使用单独的汇总表。

3、重写这个复杂的查询,让MySQL优化器能够以更优化的方式执行这个查询。

1.3.3.慢查询

1.3.3.1慢查询配置

我们已经知道慢查询日志可以帮助定位可能存在问题的SQL语句,从而进行SQL语句层面的优化。但是默认值为关闭的,需要我们手动开启。

show VARIABLES like 'slow_query_log';

image.png

set GLOBAL slow_query_log=1;

开启1,关闭0

但是多慢算慢?MySQL中可以设定一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询日志中。long_query_time参数就是这个阈值。默认值为10,代表10秒。

show VARIABLES like '%long_query_time%';

当然也可以设置

set global long_query_time=0;

默认10秒,这里为了演示方便设置为0

同时对于运行的SQL语句没有使用索引,则MySQL数据库也可以将这条SQL语句记录到慢查询日志文件,控制参数是:

show VARIABLES like '%log_queries_not_using_indexes%';

image.png

开启1,关闭0(默认)

image.png

show VARIABLES like '%slow_query_log_file%';
小结

l slow_query_log 启动停止慢查询日志

l slow_query_log_file 指定慢查询日志得存储路径及文件(默认和数据文件放一起)

l long_query_time 指定记录慢查询日志SQL执行时间得伐值(单位:秒,默认10秒)

l log_queries_not_using_indexes 是否记录未使用索引的SQL

l log_output 日志存放的地方可以是TABLE[FILE,TABLE]

1.3.4.Explain执行计划

1.3.4.1.什么是执行计划

有了慢查询语句后,就要对语句进行分析。一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等。EXPLAIN语句来帮助我们查看某个查询语句的具体执行计划,我们需要搞懂EPLATNEXPLAIN的各个输出项都是干嘛使的,从而可以有针对性的提升我们查询语句的性能。

通过使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析查询语句或是表结构的性能瓶颈,总的来说通过EXPLAIN我们可以:

l 表的读取顺序

l 数据读取操作的操作类型

l 哪些索引可以使用

l 哪些索引被实际使用

l 表之间的引用

l 每张表有多少行被优化器查询

1.3.4.2.执行计划的语法

执行计划的语法其实非常简单: 在SQL查询的前面加上EXPLAIN关键字就行。比如:EXPLAIN select * from table1

重点的就是EXPLAIN后面你要分析的SQL语句

除了以SELECT开头的查询语句,其余的DELETE、INSERT、REPLACE以及UPOATE语句前边都可以加上EXPLAIN,用来查看这些语句的执行计划,不过我们这里对SELECT语句更感兴趣,所以后边只会以SELECT语句为例来描述EsxPLAIN语句的用法。

1.3.4.3.执行计划详解

为了让大家先有一个感性的认识,我们把EXPLAIN语句输出的各个列的作用先大致罗列一下:

explain select * from order_exp;

id : **在一个大的查询语句中每个SELECT关键字都对应一个唯一的id**

select_type : SELECT**关键字对应的那个查询的类型**

table :表名

partitions :匹配的分区信息

type :针对单表的访问方法

possible_keys :可能用到的索引

key :实际上使用的索引

key_len :实际使用到的索引长度

ref :当使用索引列等值查询时,与索引列进行等值匹配的对象信息

rows :预估的需要读取的记录条数

filtered :某个表经过搜索条件过滤后剩余记录条数的百分比

Extra :—些额外的信息

id

我们知道我们写的查询语句一般都以SELECT关键字开头,比较简单的查询语句里只有一个SELECT关键字,

稍微复杂一点的连接查询中也只有一个SELECT关键字,比如:

SELECT *FROM s1
INNER J0IN s2 ON s1.id = s2.id
WHERE s1.order_status = 0 ;

但是下边两种情况下在一条查询语句中会出现多个SELECT关键字:

1、查询中包含子查询的情况

比如下边这个查询语句中就包含2个SELECT关键字:

SELECT* FROM s1 WHERE id IN ( SELECT * FROM s2);

2、查询中包含UNION语句的情况

比如下边这个查询语句中也包含2个SELECT关键字:

SELECT * FROM s1
UNION SELECT * FROM s2 ;

查询语句中每出现一个SELECT关键字,MySQL就会为它分配一个唯一的id值。这个id值就是EXPLAIN语句的第一个列。

单SELECT关键字

比如下边这个查询中只有一个SELECT关键字,所以EXPLAIN的结果中也就只有一条id列为1的记录∶

EXPLAIN SELECT * FROM s1 WHERE order_no = 'a';
连接查询

对于连接查询来说,一个SELECT关键字后边的FROM子句中可以跟随多个表,所以在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的id值都是相同的,比如:

 

可以看到,上述连接查询中参与连接的s1和s2表分别对应一条记录,但是这两条记录对应的id值都是1。这里需要大家记住的是,在连接查询的执行计划中,每个表都会对应一条记录,这些记录的id列的值是相同的。

这是因为 ,mysql 查询优化器将上面的sql 做了优化,这条会优化为:

select s1.* from s1,s2 where s1.id =s2.id ;

这就形成了连接查询,所以多个select 对应1个id;

更新一下SQL:

 此时,加了or s1.order_note ='a' ,查询优化器没法优化这条sql ,两个select 就会有两个id

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值