MySQLDBA修炼之道读书笔记

以下的命令将查看慢查询是否启用了,以及慢查询的日志路径。

mysql> show variables like'%query_log%';

 -------------show variables like'%query_log%' 

-------------+---------------------+-----------------------------------------+

 | Variable_name       | Value                                   | +---------------------+-----------------------------------------+ 

| slow_query_log      | ON                                      | 

| slow_query_log_file |/path/to/log3304/slowquery.log           | +---------------------+-----------------------------------------+ 2rows in set (0.00sec)

  由于连接的成本比较高,因此对于高并发的应用,应该尽量减少有连接的查询,连接的表的个数不能太多,连接的表建议 控制在4个以内。互联网应用比较常见的一种情况是,在数据量比较小的时候,连接的开销不大,这个时候一般不会有性能问

题,但当数据量变大之后,连接的低效率问题就暴露出来了,成为整个系统的瓶颈所在。所以对于数据库应用的设计,最好在
早期就确定未来可能会影响性能的一些查询,进行反范式设计减少连接的表,或者考虑在应用层进行连接。
优化连接的一些要点如下。
1)ON、USING子句中的列确认有索引。如果优化器选择了连接的顺序为B、A,那么我们只需要在A表的列上创建索引即 可。例如,对于查询“SELECT B.*,A.*FROM B JOIN A ON B.col1=A.col2;”语句MySQL会全表扫描B表,对B表的每一行记录探测 A表的记录(利用A表col2列上的索引)。
2)最好是能转化为INNER JOIN,LEFT JOIN的成本比INNER JOIN高很多。
3)使用EXPLAIN检查连接,留意EXPLAIN输出的rows列,如果rows列太高,比如几千,上万,那么就需要考虑是否索引
不佳或连接表的顺序不当。
4)反范式设计,这样可以减少连接表的个数,加快存取数据的速度。

5)考虑在应用层实现连接。

  对于一些复杂的连接查询,更值得推荐的做法是将它分解为几个简单的查询,可以先执行查询以获得一个较小的结果集,
然后再遍历此结果集,最后根据一定的条件去获取完整的数据,这样做往往是更高效的,因为我们把数据分离了,更不容易发

生变化,更方便缓存数据,数据也可以按照设计的需要从缓存或数据库中进行获取。

GROUP BY、DISTINCT、ORDER BY这几类子句比较类似,GROUP BY默认也是要进行ORDER BY排序的,笔者在本书中
把它们归为一类,优化的思路也是类似的。可以考虑的优化方式如下。
·尽量对较少的行进行排序。
·如果连接了多张表,ORDER BY的列应该属于连接顺序的第一张表。
·利用索引排序,如果不能利用索引排序,那么EXPLAIN查询语句将会看到有filesort。
·GROUP BY、ORDER BY语句参考的列应该尽量在一个表中,如果不在同一个表中,那么可以考虑冗余一些列,或者合并
表。
·需要保证索引列和ORDER BY的列相同,且各列均按相同的方向进行排序。
·增加sort_buffer_size。
sort_buffer_size是为每个排序线程分配的缓冲区的大小。增加该值可以加快ORDER BY或GROUP BY操作。但是,这是为每 个客户端分配的缓冲区,因此不要将全局变量设置为较大的值,因为每个需要排序的连接都会分配sort_buffer_size大小的内存。
·增加read_rnd_buffer_size。
当按照排序后的顺序读取行时,通过该缓冲区读取行,从而避免搜索硬盘。将该变量设置为较大的值可以大大改进ORDER BY的性能。但是,这是为每个客户端分配的缓冲区,因此你不应将全局变量设置为较大的值。相反,只用为需要运行大查询
的客户端更改会话变量即可。
·改变tmpdir变量指向基于内存的文件系统或其他更快的磁盘。
如果MySQL服务器正作为复制从服务器被使用,那么不应将“--tmpdir”设置为指向基于内存的文件系统的目录,或者当服务
器主机重启时将要被清空的目录。因为,对于复制从服务器,需要在机器重启时仍然保留一些临时文件,以便能够复制临时表 或执行LOAD DATA INFILE操作。如果在服务器重启时丢失了临时文件目录下的文件,那么复制将会失败。
·指定ORDER BY NULL。
默认情况下,MySQL将排序所有GROUP BY的查询,如果想要避免排序结果所产生的消耗,可以指定ORDER BY NULL。
例如:
SELECT count(*) cnt, cluster_id FROM stat GROUP BY cluster_id ORDER BY NULL LIMIT 10;
·优化GROUP BY WITH ROLLUP。
GROUP BY WITH ROLLUP可以方便地获得整体分组的聚合信息(super aggregation),但如果存在性能问题,可以考虑在应
用层实现这个功能,这样往往会更高效,伸缩性也更佳。
·使用非GROUP BY的列来代替GROUP BY的列。
比如,原来是“GROUP BY xx_name,yy_name”,如果GROUP BY xx_id可以得到一样的结果,那么使用GROUP BY xx_id也是可
行的。

    Web应用经常需要对查询的结果进行分页,分页算法经常需要用到“LIMIT offset,row_count ORDER BY col_id”之类的语句。 一旦offset的值很大,效率就会很差,因为MySQL必须检索大量的记录(offset+row_count),然后丢弃大部分记录。
可供考虑的优化办法有如下4点。
1)限制页数,只显示前几页,超过了一定的页数后,直接显示“更多(more)”,一般来说,对于N页之后的结果,用户一
般不会关心。
2)要避免设置offset值,也就是避免丢弃记录。

例如以下的例子,按照id排序(id列上有索引),通过增加一个定位的列“id>990”,可以避免设置offset的值

查询第1条到第10条的数据的sql是:select * from table limit 0,10;   ->对应我们的需求就是查询第一页的数据:select * from table limit (1-1)*10,10;

查询第10条到第20条的数据的sql是:select * from table limit 10,20;  ->对应我们的需求就是查询第二页的数据:select * from table limit (2-1)*10,10;

查询第20条到第30条的数据的sql是:select * from table limit 20,30;  ->对应我们的需求就是查询第三页的数据:select * from table limit (3-1)*10,10;

MySQL的参数及运行状态
以下代码可查看MySQL实例的参数及运行状态。

SHOW VARIABLES LIKE '%parameter%' ; 

SHOW FULL PROCESSLIST ; 

SHOW INNODB STATUS \G;

下面来解释一些常用的状态。
·Sleep:线程正在等待来自客户端的新查询。
·Query:线程正在执行查询,或者正在发送结果给客户端。
·Locked:线程正在等待表锁。
·Analyzing和statistics:线程正在获取存储引擎的统计数据和优化查询。
·Copying to tmp table[on disk]:线程正在处理查询,复制数据到临时表中。如果后面有“on disk”字样,则表明MySQL正在将内存临时表转换为磁盘临时表。
·Sorting result:线程正在排序结果集。

·Sending data:这个状态有多种可能,可能是内部各步骤之间传递数据,生成结果集;或者是将结果集返回给客户端。

MySQL需要关注的参数及状态变量
以下的一些状态变量,是监控系统需要着重关注的,由于篇幅所限,这里并没有列出所有值得关注的状态变量。
(1)open_files_limit
操作系统允许mysqld打开的文件数量。这个值可以设置得比较大,比如50000,最好在系统初始化安装时就设置了一个较大的值。可修改文件/etc/security/limits.conf
来实现,命令如下。
vi /etc/security/limits.conf * - nofile 50000
(2)max_connect_errors
此值应设置得比较大,如大于5000,以避免因为连接出错而超过出错阈值,导致MySQL阻止该主机连接。如被阻塞,则须手动执行flush-hosts进行复位。
(3)max_connections
允许并行的客户端连接数目。默认值100太小,一般会不够用。
生产环境中建议设置为2000~5000。注意,对于32位的MySQL由于有内存限制,连接数不能过大(建议小于800),否则可能会由于连接过多,造成MySQL实例
崩溃。
(4)max_used_connections
MySQL Server启动后曾经到达的最大连接数。如果该值达到max_connections,那么某个时刻存在突然的高峰连接时,可能会有性能问题。
(5)threads_connected
当前打开的连接数量。这个值不能超过设置的max_connections*80%。需要注意及时调整max_connections的值。一旦连接数超过了max_connections,就会出现客户端
连接不上的错误。
(6)aborted_connects
试图连接到MySQL服务器而失败的连接数。正常情况下,该值不会持续增加,出现连接失败的原因主要有如下几点。
·客户端程序在退出之前未调用mysql_close()。
·客户端的空闲时间超过了wait_timeout或interactive_timeout秒,未向服务器发出任何请求。
·客户端在数据传输中途突然结束。
(7)Aborted_clients
由于客户端没有正确关闭连接导致客户端终止而中断的连接数。
出现下述情况时,服务器将增加“Aborted_clients”(放弃客户端)的状态变量。
·客户端不具有连接至数据库的权限。
·客户端采用了不正确的密码。
·连接信息包含不正确的信息。
·获取连接信息包的时间超过了connect_timeout秒。
我们可以使用如下的命令发现异常。
mysqladmin -uroot -p -S /path/to/tmp//3306/mysql.sock ext | grep Abort
也可以使用tcpdump来判断是什么原因导致了异常。
tcpdump -s 1500 -w tcp.out port 3306 strings tcpdump.out
(8)thread_cache_size
服务器应缓存多少线程以便重新使用?当客户端断开连接时,如果线程少于thread_cache_size,则客户端的线程将被放入缓存。如果有新连接请求分配线程则可
以从缓存中重新利用线程,只有当缓存空了时才会创建新线程。如果新连接很多,则可以增加该变量以提高性能。如果是大量并发的短连接,则可能会因为 thread_cache_size不够而导致性能问题。生产环境中一般将其设置为100~200。
由于线程可以缓存,所以线程持有的内存不会被轻易释放。
(9)Threads_created
创建用来处理连接的线程数。应该监视Threads_created的增量,如果较多,则需要增加thread_cache_size的值。
以上对thread_cache_size的设置在高并发的时候会很有效。高并发时大量并发短连接对CPU的冲击不容忽视。
(10)threads_running
指同时运行的线程数目。这个值一般不会大于逻辑CPU的个数,如果经常有过多的线程同时运行,那么可能就意味着有性能问题。这个指标很重要,往往表明
了一个系统的繁忙程度,它在系统爆发性能问题之前,会有一个上升的趋势,此时收集的性能信息,将有助于我们诊断复杂的性能问题。
(11)slow_launch_threads

如果这个值比较大,则意味着创建线程太慢了,可能是系统出现了性能问题,存在资源瓶颈,从而导致操作系统没有安排足够的CPU时间给新创建的线程。

批量KILL连接
  有时生产环境突然出现性能恶化,登录MySQL,运行SHOW PROCESSLIST命令,发现有大量查询正在执行,这时你打算手动KILL掉应用程序中过来的运行时 间超过200s的所有的数据库连接。
mysql> SELECT CONCAT('KILL ',id,';') FROM information_schema.processlist
WHERE user<>'root' AND Command='Query' AND db='db_name'  AND time > 200 INTO OUTFILE '/tmp/a.txt'; mysql> SOURCE /tmp/a.txt;
你可以添加更多的筛选条件。
如下是一个KILL掉被阻塞的连接的例子,这是一个临时的解决方案,彻底解决问题需要尽快找到导致阻塞的原因。
for id in `mysqladmin processlist|grep -i locked|awk '{print $1}'` do  mysqladmin kill ${id} done
14.1.13 记录运行时间长的查询
如下命令将记录运行时间超过120s的查询。

mysql -uroot -p -e "show full processlist" |grep "Query" |grep "select" |egrep -v "root|Sleep|Locked|INSERT|DELETE|UPDATE" |  gawk '{if(strtonum($6)>120){print $0;}}' | grep db_name > /tmp/long_running_process.lst

信息包过大错误
  通信信息包是发送至MySQL服务器的单个SQL语句,或者发送至客户端的单一行。在MySQL 5.1服务器和客户端之间最大能发送的信息包为1GB。
  当MySQL客户端或mysqld服务器收到大于max_allowed_packet字节的信息包时,将发出“(ER_NET_PACKET_TOO_LARGE)”,错误,并关闭连接,错误如下。
  Error: 1153 SQLSTATE: 08S01 (ER_NET_PACKET_TOO_LARGE) Message: Got a packet bigger than ‘ max_allowed_packet' bytes有一些客户端还会在包过大时,提示“Lost connection to MySQL server during query”的错误。
客户端和服务器均有自己的max_allowed_packet变量,因此,如你打算处理大的信息包,则必须增加客户端和服务器上的该变量。
  如果你正在使用mysql客户端程序,这时要想将max_allowed_packet变量设置为较大的值32M,可用下述方式进行修改。
mysql> set global  max_allowed_packet=32*1024*1024;

你可能感兴趣的:(每天学习两小时)