MYSQL如何定位、分析并优化慢查询
一、如何定位慢查询
在web开发中,我们经常会写出一些SQL语句,一条糟糕的SQL语句可能让你的整个程序都非常慢,超过10秒一般用户就会选择关闭网页,如何优化SQL语句将那些运行时间比较长的SQL语句找出呢?MySQL给我们提供了一个很好的功能,那就是慢查询!所谓的慢查询就是通过设置来记录超过一定时间的SQL语句!那么如何应用慢查询呢?
开启MySQL的慢查询日志功能:默认情况下,MySQL是不会记录超过一定执行时间的SQL语句的。要开启这个功能,我们需要修改MySQL的配置文件,windows下修改my.ini,Linux下修改my.cnf文件,在[mysqld]最后增加如下命令:
slow_query_log= 1 long_query_time= 1 slow_query_log_file=c:/slow.log log_queries_not_using_indexes=on
解释:
1)slow_query_log= 1:表示开启慢查询日志功能,当然slow_query_log= 0就是关闭了(默认情况),要开启也可以直接这样写slow_query_log。
2)long_query_time =1:表示设置慢查询的时间为1S,默认为10S,即如果有查询超过了这个时间,将会被记录到慢查询日志中。当然在高版本的MYSQL中这个时间还可以设置成小数。
3)slow_query_log_file=c:/slow.log:表示慢查询日志存放的目录文件。当然这条可以不写,不写时,默认在mysql的data目录下生成“主机名_slow.log”的文件来存放日志。
4)log_queries_not_using_indexes : 这个参数设置为ON,可以捕获到所有未使用索引的SQL语句,尽管这个SQL语句有可能执行得挺快。
测试 1、在my.ini中加入以下配置,并重启动MYSQL服务 slow_query_log = 1 ong_query_time = 1 2、在MYSQL控制如输入命令select sleep(2) mysql> select sleep(2); +----------+ | sleep(2) | +----------+ | 0| +----------+ 1 row in set (2.00 sec) 3、再查看日志文件,会看到以下信息。 # Time: 141014 14:28:52 # User@Host: root[root] @ localhost[::1] Id: 1 # Query_time: 2.000114 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 SET timestamp=1413268132; select sleep(2);
二、如何分析慢查询
1、show processlist 命令
SHOW PROCESSLIST显示哪些线程正在运行。您也可以使用mysqladmin processlist语句得到此信息。以下是各列的含义和用途:
1)ID列:一个标识,你要kill一个语句的时候很有用,用命令杀掉此查询 /*/mysqladmin kill 进程号。
2)user列:显示单前用户,如果不是root,这个命令就只显示你权限范围内的sql语句。
3)host列:显示这个语句是从哪个ip的哪个端口上发出的。用于追踪出问题语句的用户。
4)db列:显示这个进程目前连接的是哪个数据库。
5)command列:显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。
6)time列:此这个状态持续的时间,单位是秒。
7)state列:显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,请注意,state只是语句执行中的某一个状态,一个 sql语句,以查询为例,可能需要经过copying to tmp table,Sorting result,Sending data等状态才可以完成
8)info列:显示这个sql语句,因为长度有限,所以长的sql语句就显示不全,但是一个判断问题语句的重要依据。
这个命令中最关键的就是state列,mysql列出的状态主要有以下几种:
1)Checking table:正在检查数据表(这是自动的)。
2)Closing tables:正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
3)Connect Out:复制从服务器正在连接主服务器。
4)Copying to tmp table on disk:由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
5)Creating tmp table:正在创建临时表以存放部分查询结果。
6)deleting from main table:服务器正在执行多表删除中的第一部分,刚删除第一个表。
7)deleting from reference tables:服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
8)Flushing tables:正在执行FLUSH TABLES,等待其他线程关闭数据表。
9)Killed:发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
10)Locked:被其他查询锁住了。
11)Sending data:正在处理SELECT查询的记录,同时正在把结果发送给客户端。
12)Sorting for group:正在为GROUP BY做排序。
13)Sorting for order:正在为ORDER BY做排序。
14)Opening tables:这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
15)Removing duplicates:正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
16)Reopen table:获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
17)Repair by sorting:修复指令正在排序以创建索引。
18)Repair with keycache:修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
19)Searching rows for update:正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。
20)Sleeping:正在等待客户端发送新请求.
21)System lock:正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加–skip-external-locking参数来禁止外部系统锁。
22)Upgrading lock:INSERT DELAYED正在尝试取得一个锁表以插入新记录。
23)Updating:正在搜索匹配的记录,并且修改它们。
24)User Lock:正在等待GET_LOCK()。
25)Waiting for tables:该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
26)waiting for handler insert
INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。
还有其他的状态没在上面中列出来,不过它们大部分只是在查看服务器是否有存在错误是才用得着。
例如如图:
2、explain来了解SQL执行的状态
explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在select语句前加上explain就可以了:
例如:explain select * from user where id < 30;【explain select * from user where id = 30;】
结果如图
EXPLAIN列的解释
1)table:显示这一行的数据是关于哪张表的
2)type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
3)possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句
4)key:实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句 中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引
5)key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好
6)ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
7)rows:MYSQL认为必须检查的用来返回请求数据的行数
8)Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢
extra列返回的描述的意义
1)Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
2)Not exists:MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
3)Range checked for each Record(index map:#):没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
4)Using filesort:看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
5)Using index:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
6)Using temporary:看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
7)Where used:使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)
8)const:表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待
9)eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用
10)ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好
11)range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况
12)index:这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)
13)ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免:
三、如何优化慢查询
1、查询如果慢的建立索引可以提升速度
相当于就是之前一个表数据量比较小,之后数据量大了查询就变慢,此时在经常用到的字段上加个索引,效率会翻倍很多的
2、建立索引是为了提升速度,所以避免对索引字段进行计算或类型转化
例如: where a * 5 = 10 可以 转化为 where a = 10/5,这样既可以保证业务逻辑也可以继续使用原索引去操作,所以要避免对索引字段进行计算或类型转化
3、表的设计尤为重要,避免使用LEFT JOIN 或 RIGHT JOIN去联表查询
如果业务逻辑允许,且表里几乎无脏数据,那就可以使用JOIN去联表查询,而不建议使用LEFT JOIN或RIGHT JOIN去查询,因为本身它们联表都是有区别的,建议使用JOIN去联表,尽量的去提升速度
4、查询字段时不要全部返回,忌讳使用*,最好是用什么字段返回什么地段,也可使得相关业务更加清晰
不建议 select * from table_name;因为查询,返回结果集,都会花费io,还有带宽一些资源,所以结果集内容少速度也会提升的
5、分解执行复杂sql,关注执行sql执行时间,关注lock粒度
如一下操作的数据量比较大,不管是DML、DDL语言都得考虑lock的粒度,不能影响线上业务操作,一般都是分布执行,不要为了省事,而导致业务挂掉,那就得不偿失了,操作前先查看操作数据量的级别
6、索引的维护(BDA操作范畴)
索引的维护,把一些不用的且占磁盘空间比较大,自然得删除,或者跟据业务逻辑对某字段的索引算法进行调整再或者使用新的索引去替代老的索引,注意处理同一个字段的两个索引的优化时,如果存在主从复制的机制在的话,维护索引注意不要影响生产业务
注意:以上优化都是基于不改变原有业务需求所做的优化,否则另换它法去优化
7、常见的几种索引算法
B-Tree、 Hash、Gist、SP-Gist、Gin、BRin,要用那种索引,跟据业务逻辑的特点或者字段的类型而定
附录
另外,我们还要了解解一些MYSQL数据库查看状态的命令,以便更详尽的掌控MYSQL。在控制如下执行这些命令查看mysql数据库一些运行的状态。
1)show [session|global]status 注:不写默认的为session..session只取出当前会话的状态,global会取出从启动到现在的状态。
2)showstatus 查看数据库状态,这里面的状态有很多,DBA应该了解大多数。
3)showstatus like ‘uptime’ 数据库启动时间(S)
4)showstatus like ‘com_select’ 数据库执行了多少次查询
5)showstatus like ‘com_update’ 数据库执行了多少次更新
6)showstatus like ‘com_delete’ 数据库执行了多少次删除
7)showstatus like ‘com_insert’ 数据库执行了多少次插入
8)showstatus like ‘connections’ 试图链接MYSQL数据库的次数;另外可用cmd下的netstat –an 查看是那些IP链接到3306上,即链接到MYSQL数据库上。
9)show status like ‘slow_queries’; 慢查询次数。默认情况下,超过10S的为慢查询。控制台下修改MYSQL默认的慢查询时间
10)show variableslike ‘long_query_time’ 查询默认的慢查询时间
11)set long_query_time = 1 设置默认的慢查询时间
来源:
1)http://blog.csdn.net/czfphper/article/details/46549667
2)http://www.studyofnet.com/news/494.html