MySQL 每天自行统计慢日志 脚本



标题:MySQL 每天自行统计慢日志 脚本

作者:lōττéry©版权所有[文章允许转载,但必须以链接方式注明源地址,否则追究法律责任.]


前言:
 MySQL中提供了一个慢查询的日志记录功能(有点类似 oracle的v$session_longops ),可以把查询SQL语句时间大于多少秒的语句写入慢查询日志,日常维护中可以通过慢查询日志的记录信息快速准确地判断问题所在;
 可以通过mysqldumpslow、mysqlsla、pt-query-digest ..等工具进行分析慢日志, mysqldumpslow、mysqlsla文本查看,而pt-query-digest工具可导入数据库,方便查看;
 可参考之前博客 MySQL slow query [慢查询] 资料整理  ;
本文主要通过pt-query-digest工具将所需内容导入到数据库(sql按照发起sql者区分),最终将各个用户下所有信息导入到一张总表.再进行分析..

   
脚本内容:


#!/bin/bash

# Descirption:mysql slow_log Statistics
# DATE: 2015年7月1日
# Author: 毛海晴 

#脚本目的:
#每天将慢查询sql及信息导入到mysql数据库表作分析(需要信息:执行sql用户{database}、sql文本、执行次数、总执行时间、平均时间、查询行数、返回行数)

#脚本编写思路
#pt-query-digest未发现直接按照database过滤自带参数(问题 当提供慢sql时需要知道sql在哪个库执行的 的情况该怎么办) 
#不过可以考虑使用pt-query-digest自带用户限制参数--filter '($event->{user} || "") =~ m/^用户/i' 可以简单用用户充当database再结合mysql.user再分析')
#通过pt-query-digest user 限制后导入数据库,但导入后并没有某字段记录
database_name信息
#所以考虑循环限制并导入database+date命名数据表后手动加database_name字段,默认值为 相应sql执行的用户名
#最终会生成多表(例如:slow_log_his_2015_07_01_retail_uc、slow_log_his_2015_07_01_retail_pms...)第一个字段是
database_name和相应的执行sql用户名
#表整理:将多表追加到一张slow_log_his_2015_07_01表,再通过mysql sql 取出我们需要字段。

# *********************************************步骤********************************************
## 变量设置 (注释:变量定义=等号前后不能出现空格,否则会被系统理解成命令)
#登陆名
user='root' 
#登陆密码 
slow_root='root'
slow_password=123456
slow_host=写入远程库IP
slow_port='3306'
slow_dbname='test'
#日期设置, 生成慢查询表命令YYYY_MM_DD  'train_public'后缀名避免多库导入同一远程数据库表名重复,数据覆盖问题。
pt_table=slow_log_his_`date +%Y_%m_%d`_train_public 

#IP获取
# ip=`ifconfig |grep "Bcast"|awk -F ':' '{print $2}'|awk -F ' ' '{print $1}'`
echo '------------- '`date +%Y-%m-%d' '%H:%M:%S`' 开始 "'`hostname`'"(IP...) 慢查询统计 -------------'
slow_name=`mysql -u$slow_root -p$slow_password -e "show variables like 'slow_query_log_file'"|grep log|awk '{print $2}'`

#--> 一、整合表'slow_log_his_'$date配置配置
# 表数据要保证为空,避免“ERROR 1062 (23000) at line 1: Duplicate entry 'XX' for key 'PRIMARY'”
#--》1 本次伪例限制--filter '($event->{user} || "") =~ m/^mhq/i'导入空表 mhq用户并不存在
#--》或2 方案,在insert之前先 delete from 表;  
#(1)删除已有并创建
mysql -h${slow_host} -u${slow_root} -p${slow_password} -D${slow_dbname} -B -N  -e " drop table if exists ${pt_table};"
pt-query-digest  --user=${slow_root} --password=${slow_password} --no-report  --history h=${slow_host},D=${slow_dbname},t=${pt_table} --create-history-table --filter '($event->{user} || "") =~ m/^mhq/i' $slow_name.`date +%Y%m%d`

#(2)加database_name 字段,保证各个表结构相同
mysql -h${slow_host} -u${slow_root} -p${slow_password} -D${slow_dbname} -B -N  -e "alter table ${pt_table} add  COLUMN database_name varchar(255) NOT NULL FIRST ; "  


#--> 二、查找database_name,开始循环导入database_name+date命名 数据库表
#(1)查找所有database_name 
for username in `mysql -uroot -hlocalhost -p123456  -N -B -e "select distinct user from mysql.user;"` 
do   
#(2)使用pt工具将通过--filter 过滤database_name循环导入database_name+date命名 数据库表(存在先删除..)
mysql -h${slow_host} -u${slow_root} -p${slow_password} -D${slow_dbname} -B -N  -e "drop table if exists ${pt_table}_${f};"
pt-query-digest  --user=${slow_root} --password=${slow_password} --filter '($event->{user} || "") =~ m/^'$username'/i'   --no-report  --history h=${slow_host},D=${slow_dbname},t=${pt_table}_${f} --create-history-table  $slow_name.`date +%Y%m%d`
# 由于后续sql过滤只用到history表,所以未加“--review   h=$host,D=$dbname,t='slow_log_'$date'_'$username   --create-review-table”参数

#--> 三、处理循环导入的database_name+date命名表 
mysql -h${slow_host} -u${slow_root} -p${slow_password} -D${slow_dbname} -B -N  -e "
#(1)向每张表加字段,默认值为database_name,用来查询使用
alter table ${pt_table}_${f} add  COLUMN database_name varchar(255) NOT NULL default '$username'  FIRST ;
#(2)循环将database_name+date命名表数据读到'slow_log_his_'$date表.
insert into ${pt_table} select * from  ${pt_table}_${f};
#(3)清理
database_name+date命名表
drop table ${pt_table}_${f};

## -e " 冒号需要写在同一行,否则会提示“mysql: option '-e' requires an argument”

#结束循环
done 

echo '------------- '`date +%Y-%m-%d' '%H:%M:%S`' 结束 "'`hostname`'"(IP...) 慢查询统计 -------------'


# pt-query-digest用到的参数注释:
# --user  mysql用户名
#--password  mysql用户密码
#--history 将分析结果保存到表中,分析结果比较详细,下次再使用--history时,如果存在相同的语句,且查询所在的时间区间和历史表中的不同,则会记录到数据表中,可以通过查询同一CHECKSUM来比较某类型查询的历史变化
# --no-report  不打印分析log
# --host  mysql服务器地址
# h  要导入mysql服务器地址
# D  要导入到的database
# t  要导入到的table
#--create-history-table     当使用--history参数把分析结果输出到表中时,如果没有表就自动创建。
# --filter  对输入的慢查询按指定的字符串进行匹配过滤后再进行分析
#--limit限制输出结果百分比或数量,默认值是20,即将最慢的20条语句输出,如果是50%则按总响应时间占比从大到小排序,输出到总和达到50%位置截止。
# 其他参数
#--since 从什么时间开始分析,值为字符串,可以是指定的某个”yyyy-mm-dd [hh:mm:ss]”格式的时间点,也可以是简单的一个时间值:s()h(小时)m(分钟)d(),如12h就表示从12小时前开始统计。
#--until 截止时间,配合—since可以分析一段时间内的慢查询。  

 

# 查询SQL 

#未使用表自带字段“*_*_pct_95”平均值,是因为 sum / ts_cnt  并不相等于*_pct_95字段
SELECT
     his.*
FROM
     ( SELECT
               database_name AS "sql发起者",
               sample sql_text,
               ts_cnt AS "执行次数",
               round(Query_time_sum, 2) AS "执行时间(总)",
               round(Query_time_sum / ts_cnt, 2) AS "执行时间(每)",
               round(Lock_time_sum, 3) AS "锁时间(总)",
               round(Lock_time_sum / ts_cnt, 3) AS "锁时间 (每)",
               round(Rows_examined_min / ts_cnt) AS "参加运算的记录平均行数",
               round(Rows_sent_sum / ts_cnt) AS "平均返回记录数"
          FROM
               slow_log_his_2015_07_03_train_public t
          #WHERE database_name LIKE 'retail_%' #database_name not LIKE 'retail_%' 
          ORDER BY
               4 DESC
     ) his
LIMIT 10;

显示结果:
MySQL 每天自行统计慢日志 脚本

注意:
1、需要在慢启动日志切割后执行。
2/*!40001 SQL_NO_CACHE */ 表示: 不把查询结果存储在查询缓存中 (类似oracle direct path read) ...本环境是由mysqldump引起/*!40001 SQL_NO_CACHE */
3pt-query- digest小瑕疵:
选中部分SQL SELECT /*!40001 SQL_NO_CACHE */ * FROM `item_sku` 执行次数 =9 
而实际上是select * from 三个分别三次不同的表 ... (reviewhistory 表都记录到一条数据,执行次数为 9)
     mysqldumpslow 工具结果如下:
     Count: 3  Time=9.89s (29s)  Lock=0.00s (0s)  Rows=2453000.0 (7359000), root[root]@localhost
       SELECT /*!N SQL_NO_CACHE */ * FROM `item_sku`
     Count: 3  Time=6.68s (20s)  Lock=0.00s (0s)  Rows=1545142.0 (4635426), root[root]@localhost
       SELECT /*!N SQL_NO_CACHE */ * FROM `item_extension`
     Count: 3  Time=4.01s (12s)  Lock=0.00s (0s)  Rows=328886.0 (986658), root[root]@localhost
       SELECT /*!N SQL_NO_CACHE */ * FROM `item`




MYSQL慢日志分析参数的涵义  


  ` ts_min datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最早执行时间' ,
  ` ts_maxdatetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最晚执行时间' ,
  ` ts_cntfloat DEFAULT NULL COMMENT '总共执行次数' ,
 
   ` Query_time_sumfloat DEFAULT NULL COMMENT '总查询时间' ,
   ` Query_time_minfloat DEFAULT NULL COMMENT '最小查询时间' ,
   ` Query_time_maxfloat DEFAULT NULL COMMENT '最大查询时间' ,
   ` Query_time_pct_95float DEFAULT NULL COMMENT '平均查询时间' ,
   ` Query_time_stddevfloat DEFAULT NULL COMMENT '查询时间标准差' ,
   ` Query_time_medianfloat DEFAULT NULL COMMENT '查询时间中位数' ,
 
   ` Lock_time_sumfloat DEFAULT NULL COMMENT '总锁定时间' ,,
   ` Lock_time_minfloat DEFAULT NULL COMMENT '最小锁定时间' ,
   ` Lock_time_maxfloat DEFAULT NULL COMMENT '最大锁定时间' ,
   ` Lock_time_pct_95float DEFAULT NULL  COMMENT '平均锁定时间' ,
   ` Lock_time_stddevfloat DEFAULT NULL COMMENT '锁定时间标准差' ,
   ` Lock_time_medianfloat DEFAULT NULL COMMENT '锁定时间中位数' ,
 
   ` Rows_sent_sumfloat DEFAULT NULL COMMENT '总返回记录行数' ,
   ` Rows_sent_minfloat DEFAULT NULL COMMENT '最小返回记录数' ,
   ` Rows_sent_maxfloat DEFAULT NULL COMMENT '最大返回记录数' ,
   ` Rows_sent_pct_95float DEFAULT NULL COMMENT '平均返回记录数' ,
   ` Rows_sent_stddevfloat DEFAULT NULL COMMENT '发送返回数标准差' ,
   ` Rows_sent_medianfloat DEFAULT NULL  COMMENT '返回记录数中位数' ,
 
   ` Rows_examined_sumfloat DEFAULT NULL COMMENT '参加运算的记录总行数' ,
   ` Rows_examined_minfloat DEFAULT NULL COMMENT '最少参加运算的记录行数' ,
   ` Rows_examined_maxfloat DEFAULT NULL COMMENT '最多参加运算的记录行数' ,
   ` Rows_examined_pct_95float DEFAULT NULL COMMENT '平均参加运算的记录行数' ,
   ` Rows_examined_stddevfloat DEFAULT NULL COMMENT '参加运算的记录行数标准差' ,
   ` Rows_examined_medianfloat DEFAULT NULL COMMENT '参加运算的记录行数中位数' ,
 
   ` Rows_affected_sumfloat DEFAULT NULL COMMENT '受影响记录总行数' ,
   ` Rows_affected_minfloat DEFAULT NULL COMMENT '最少受影响记录行数' ,
   ` Rows_affected_maxfloat DEFAULT NULL COMMENT '最大受影响记录行数' ,
   ` Rows_affected_pct_95float DEFAULT NULL COMMENT '平均受影响记录行数' ,
   ` Rows_affected_stddevfloat DEFAULT NULL COMMENT '受影响记录行数标准差' ,
   ` Rows_affected_medianfloat DEFAULT NULL COMMENT '受影响记录行数中位数' ,
 
   ` Rows_read_sumfloat DEFAULT NULL COMMENT '读入记录行总数' ,
   ` Rows_read_minfloat DEFAULT NULL COMMENT '读入记录行最小数' ,
   ` Rows_read_maxfloat DEFAULT NULL COMMENT '读入记录行最大数' ,
   ` Rows_read_pct_95float DEFAULT NULL COMMENT '读入记录行平均数' ,
   ` Rows_read_stddevfloat DEFAULT NULL COMMENT '读入记录行数标准差' ,
   ` Rows_read_medianfloat DEFAULT NULL COMMENT '读入记录行数中位数' ,
 
   ` Merge_passes_sumfloat DEFAULT NULL COMMENT '数据表合并总数' ,
   ` Merge_passes_minfloat DEFAULT NULL COMMENT '数据表合并最小数' ,
   ` Merge_passes_maxfloat DEFAULT NULL COMMENT '数据表合并最大数' ,
   ` Merge_passes_pct_95float DEFAULT NULL COMMENT '数据表合并平均数' ,
   ` Merge_passes_stddevfloat DEFAULT NULL COMMENT '数据表合并数标准差' ,
   ` Merge_passes_medianfloat DEFAULT NULL COMMENT '数据表合并数中位数' ,
 
   ` InnoDB_IO_r_ops_minfloat DEFAULT NULL COMMENT '统计计划需要读取的最小页数' ,
   ` InnoDB_IO_r_ops_maxfloat DEFAULT NULL COMMENT '统计计划需要读取的最大页数' ,
   ` InnoDB_IO_r_ops_pct_95float DEFAULT NULL COMMENT '统计计划需要读取的平均页数' ,
   ` InnoDB_IO_r_ops_stddevfloat DEFAULT NULL COMMENT '统计计划需要读取的页数标准差' ,
   ` InnoDB_IO_r_ops_medianfloat DEFAULT NULL COMMENT '统计计划需要读取的页数中位数' ,
 
   ` InnoDB_IO_r_bytes_minfloat DEFAULT NULL COMMENT '统计计划需要读取的最小字节数' ,
   ` InnoDB_IO_r_bytes_maxfloat DEFAULT NULL COMMENT '统计计划需要读取的最大字节数' ,
   ` InnoDB_IO_r_bytes_pct_95float DEFAULT NULL COMMENT '统计计划需要读取的平均字节数' ,
   ` InnoDB_IO_r_bytes_stddevfloat DEFAULT NULL COMMENT '统计计划需要读取的字节数标准差' ,
   ` InnoDB_IO_r_bytes_medianfloat DEFAULT NULL COMMENT '统计计划需要读取的字节数中位数' ,
 
   ` InnoDB_IO_r_wait_minfloat DEFAULT NULL COMMENT '读取记录时产生锁等待的最小时间' ,
   ` InnoDB_IO_r_wait_maxfloat DEFAULT NULL COMMENT '读取记录时产生锁等待的最大时间' ,
   ` InnoDB_IO_r_wait_pct_95float DEFAULT NULL COMMENT '读取记录时产生锁等待的平均时间' ,
   ` InnoDB_IO_r_wait_stddevfloat DEFAULT NULL COMMENT '读取记录时产生锁等待时间标准差' ,
   ` InnoDB_IO_r_wait_medianfloat DEFAULT NULL COMMENT '读取记录时产生锁等待时间中位数' ,
 
   ` InnoDB_rec_lock_wait_minfloat DEFAULT NULL COMMENT '读取记录时产生行锁等待的最小时间' ,
   ` InnoDB_rec_lock_wait_maxfloat DEFAULT NULL COMMENT '读取记录时产生行锁等待的最大时间' ,
   ` InnoDB_rec_lock_wait_pct_95float DEFAULT NULL COMMENT '读取记录时产生行锁等待的平均时间' ,
   ` InnoDB_rec_lock_wait_stddevfloat DEFAULT NULL COMMENT '读取记录时产生行锁等待时间标准差' ,
   ` InnoDB_rec_lock_wait_medianfloat DEFAULT NULL COMMENT '读取记录时产生行锁等待时间中位数' ,
 
   ` InnoDB_queue_wait_minfloat DEFAULT NULL COMMENT '等待进入 InnoDB 队列或在队列中等待执行所消耗的最小时间' ,
   ` InnoDB_queue_wait_maxfloat DEFAULT NULL COMMENT '等待进入 InnoDB 队列或在队列中等待执行所消耗的最大时间' ,
   ` InnoDB_queue_wait_pct_95float DEFAULT NULL COMMENT '等待进入 InnoDB 队列或在队列中等待执行所消耗的平均时间' ,
   ` InnoDB_queue_wait_stddevfloat DEFAULT NULL COMMENT '等待进入 InnoDB 队列或在队列中等待执行所消耗时间标准差' ,
   ` InnoDB_queue_wait_medianfloat DEFAULT NULL COMMENT '等待进入 InnoDB 队列或在队列中等待执行所消耗时间中位数' ,
 
   ` InnoDB_pages_distinct_minfloat DEFAULT NULL COMMENT '读取的独立页最小个数' ,
   ` InnoDB_pages_distinct_maxfloat DEFAULT NULL COMMENT '读取的独立页最大个数' ,
   ` InnoDB_pages_distinct_pct_95float DEFAULT NULL COMMENT '读取的独立页平均个数' ,
   ` InnoDB_pages_distinct_stddevfloat DEFAULT NULL COMMENT '读取的独立页个数标准差' ,
   ` InnoDB_pages_distinct_medianfloat DEFAULT NULL COMMENT '读取的独立页个数中位数' ,
 
   ` QC_Hit_cntfloat DEFAULT NULL COMMENT 'QUERY缓冲区的命中次数' ,
   ` QC_Hit_sumfloat DEFAULT NULL COMMENT 'QUERY缓冲区的命中总数' ,
 
   ` Full_scan_cntfloat DEFAULT NULL COMMENT '全表扫描次数' ,
   ` Full_scan_sumfloat DEFAULT NULL COMMENT '全表扫描总数' ,
 
   ` Full_join_cntfloat DEFAULT NULL COMMENT '数据表全连接次数' ,
   ` Full_join_sumfloat DEFAULT NULL COMMENT '数据表全连接总数' ,
 
   ` Tmp_table_cntfloat DEFAULT NULL COMMENT '内存中使用临时表次数' ,
   ` Tmp_table_sumfloat DEFAULT NULL COMMENT '内存中使用临时表总数' ,
 
   ` Disk_tmp_table_cntfloat DEFAULT NULL COMMENT '磁盘上使用临时表总数' ,
   ` Disk_tmp_table_sumfloat DEFAULT NULL COMMENT '磁盘上使用临时表总数' ,
 
   ` Filesort_cntfloat DEFAULT NULL COMMENT '内存排序次数' ,
   ` Filesort_sumfloat DEFAULT NULL COMMENT '内存排序总数' ,
 
   ` Disk_filesort_cntfloat DEFAULT NULL COMMENT '磁盘排序次数' ,
   ` Disk_filesort_sumfloat DEFAULT NULL COMMENT '磁盘排序总数' 

     mysql   version = 5.6.19 字段差异
    -->增加:
    checksum         => 校验值
    sample           => sql 样本

    -->差异:
        原字段                  改后字段
    Disk_tmp_table_cnt => Tmp_table_on_disk_cnt
    Disk_tmp_table_sum => Tmp_table_on_disk_sum
    Disk_filesort_cnt  => Filesort_on_disk_cnt
    Disk_filesort_sum  => Filesort_on_disk_sum


 【源于本人笔记】 若有书写错误,表达错误,请指正... 
上一篇:PowerDesigner教程系列(五)概念数据模型


下一篇:《CCNP TSHOOT 300-135认证考试指南》——2.4节利用Cisco IOS收集信息