MySQL优化: CPU高 处理脚本 pt-kill脚本

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
RDS MySQL DuckDB 分析主实例,集群系列 8核16GB
简介: MySQL优化: CPU高 处理脚本 pt-kill脚本

简介

生产环境中,经常会遇见 CPU 跑满的情况,一般是由慢 SQL 或业务逻辑导致的。为了避免程序无法访问,使用脚本监控 CPU,当达到阈值时,使用 pt-kill 杀死匹配的 sql,确保线上环境的正常运行。追加到脚本中的 SQL,可以给开发人员,作为处理故障的切入点。

使用时请根据自身情况修改脚本,本脚本仅供参考。

功能

  • CPU 达到 60% 时,通知到微信
  • CPU 达到 80% 时,通知到微信,kill 掉大于 20 秒的查询。
  • CPU 达到 90% 时,通知到微信,kill 掉大于 60 秒的更新和大于 20 秒的查询。
  • 微信通知间隔(默认 30 分钟)

流程图

以下为大致流程

CPU 监控脚本

vim /root/cpu_mon.sh
#!/bin/bash
# CPU 使用率大于 60 通知到微信,大于 80 通知并 kill 大于 20s 的查询,大于 90 kill 大于 20s 的查询及大于 60s 的更新
mysql_host=10.10.8.11
mysql_port=3306
mysql_user=test
mysql_pwd="test123"
# 企业微信脚本路径
script_dir="/root/wechat.py"
# pt工具日志路径
pt_tool_log='/tmp/pt_kill.log'
# 企业微信通知间隔(秒)
send_interval=1800
#############################################################################################################
cpu_usage=$(top -b -n 1 | grep "Cpu(s)" | awk -F':' '{print $2}' | awk '{print $1+$3+$5+$9+$11+$13+$15}' | bc)
if [ ! -e .YM5rfHdwc2R3PGRMsnVg8.txt ]; then
    touch ".YM5rfHdwc2R3PGRMsnVg8.txt"
fi
check_conn(){
# 检查 MySQL 连接
mysql -u${mysql_user} -p${mysql_pwd} -h${mysql_host} -P${mysql_port} -e "select 1" >/dev/null 2>&1
if [[ $? -ne 0 ]];then
  echo "$(date +"%Y-%m-%d %H:%M:%S") 无法连接 MySQL"
  echo "$(date +"%Y-%m-%d %H:%M:%S") 无法连接 MySQL" >> ${pt_tool_log}
fi
# 检查 pt 工具是否可用
pt-kill --help >/dev/null 2>&1
if [[ $? -ne 0 ]];then
  echo "$(date +"%Y-%m-%d %H:%M:%S") pt-kill 工具不存在"
  echo "$(date +"%Y-%m-%d %H:%M:%S") pt-kill 工具不存在" >> ${pt_tool_log}
fi
}
check_conn
# 获取CPU使用率
cpu_mon(){
    if [ $(echo "${cpu_usage} >= 60 && ${cpu_usage} < 80"|bc) -eq 1 ];then
        echo 60
    elif [ $(echo "${cpu_usage} >= 80 && ${cpu_usage} < 90"|bc) -eq 1 ];then
        echo 80
    elif [ $(echo "${cpu_usage} >= 90"|bc) -eq 1 ];then
        echo 90
    else
        echo 0
    fi
}
text_80="实例信息:${mysql_host}:${mysql_port}
操作:kill 20s 以上查询
详细信息请登录数据库查看"
text_90="实例信息:${mysql_host}:${mysql_port}
操作:kill 60s 以上sql
详细信息请登录数据库查看"
update_sql(){
/usr/bin/pt-kill  \
--victim all \
--busy-time 20 \
--match-info='^SELECT|^select' \
u=${mysql_user},p=${mysql_pwd},h=${mysql_host},P=${mysql_port} \
--kill \
--run-time=10s \
--print   >> ${pt_tool_log}
        if [[ $? -ne 0 ]];then
                echo "pt-kill 执行失败,请检查具体原因" >>${pt_tool_log}
                echo "pt-kill 执行失败,请检查具体原因"
                return 1
        fi
/usr/bin/pt-kill \
--victim all \
--busy-time 60 \
--match-info='^update|^UPDATE' \
u=${mysql_user},p=${mysql_pwd},h=${mysql_host},P=${mysql_port} \
--kill \
--run-time=10s \
--print  >> ${pt_tool_log}
        if [[ $? -ne 0 ]];then
                echo "pt-kill 执行失败,请检查具体原因" >>${pt_tool_log}
                echo "pt-kill 执行失败,请检查具体原因"
                return 1
        fi
}
select_sql(){
/usr/bin/pt-kill  \
--victim all \
--busy-time 20 \
--match-info='^SELECT|^select' \
u=${mysql_user},p=${mysql_pwd},h=${mysql_host},P=${mysql_port} \
--kill \
--run-time=10s \
--print   >> ${pt_tool_log}
        if [[ $? -ne 0 ]];then
                echo "pt-kill 执行失败,请检查具体原因" >>${pt_tool_log}
                echo "pt-kill 执行失败,请检查具体原因"
                return 1
        fi
}
# 返回通知结果
reporting_conditions(){
    if [ $1 -eq 60 ];then
        ${script_dir} ""  "通知:${mysql_host}:${mysql_port} CPU超过$(cpu_mon)%"  ""
        if [[ $? -ne 0 ]];then
                echo "python 脚本执行失败" >>${pt_tool_log}
        fi
        echo "$(date +%s) 60" > .YM5rfHdwc2R3PGRMsnVg8.txt
        return 0
    elif [ $1 -eq 80 ];then
        ${script_dir} ""  "故障:${mysql_host}:${mysql_port} CPU超过$(cpu_mon)%"  "${text_80}"
        if [[ $? -ne 0 ]];then
                echo "python 脚本执行失败" >>${pt_tool_log}
        fi
        echo "$(date +%s) 80" > .YM5rfHdwc2R3PGRMsnVg8.txt
                # 执行kill
                select_sql
        return 0
    elif [ $1 -eq 90 ];then
        ${script_dir} ""  "故障:${mysql_host}:${mysql_port} CPU超过$(cpu_mon)%"  "${text_90}"
        echo "$(date +%s) 90" > .YM5rfHdwc2R3PGRMsnVg8.txt
        # 执行kill
        update_sql
        return 0
    fi
}
# 发送通知
notifications(){
    old_time=$(awk '{print $1}' .YM5rfHdwc2R3PGRMsnVg8.txt)
    old_info=$(awk '{print $2}' .YM5rfHdwc2R3PGRMsnVg8.txt)
    wait_time=$(echo "$(date +%s) - $old_time"|bc)
    # 如果文件不为空
    if [ -s .YM5rfHdwc2R3PGRMsnVg8.txt ];then
        # 如果文件信息相同
        if [ "$old_info" -eq  "$1" ];then
            # 如果不满足等待时间
            if [ $(echo "${wait_time} >= ${send_interval}"|bc) -eq 0 ];then
                # 跳过通知
                return 0
            # 满足时间等待,执行通知
            else
                # 执行通知
                reporting_conditions "$(cpu_mon)"
            fi
        # 文件信息不相同,执行通知
        else
            # 执行通知
            reporting_conditions "$(cpu_mon)"
        fi
    # 如果文件为空,执行通知
    else
        # 执行通知
        reporting_conditions "$(cpu_mon)"
    fi
}
# 设置探测次数
num_attempts=3
# 设置标志变量,表示是否执行通知
execute_notification=false
# 循环进行探测
for ((i=1; i<=$num_attempts; i++)); do
    if [ $(cpu_mon) -ne 0 ]; then
        execute_notification=true
    else
        execute_notification=false
        break  # 如果有一次等于0,立即退出循环
    fi
    sleep 1  # 等待1秒再进行下一次探测
done
# 如果连续三次都不等于0,则执行通知
if [ "$execute_notification" = true ]; then
    notifications "$(cpu_mon)"
fi

企业微信脚本

cat /root/wechat.py
#!/usr/bin/python
#_*_coding:utf-8 _*_
import urllib,urllib2
import json
import sys
import simplejson
reload(sys)
sys.setdefaultencoding('utf-8')
def gettoken(corpid,corpsecret):
    gettoken_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + corpid + '&corpsecret=' + corpsecret
#    print  gettoken_url
    try:
        token_file = urllib2.urlopen(gettoken_url)
    except urllib2.HTTPError as e:
        print e.code
        print e.read().decode("utf8")
        sys.exit()
    token_data = token_file.read().decode('utf-8')
    token_json = json.loads(token_data)
    token_json.keys()
    token = token_json['access_token']
    return token
def senddata(access_token,user,subject,content):
    send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + access_token
    send_values = {
        "touser":'test',    #企业号中的用户帐号,如果配置不正常,将按部门发送。
        "toparty":"1",      #企业号中的部门id。
        "msgtype":"text",   #消息类型。
        "agentid":"10000123",    #企业号中的应用id。
        "text":{
            "content":subject + '\n' + content
           },
        "safe":"0"
        }
#    send_data = json.dumps(send_values, ensure_ascii=False)
    send_data = simplejson.dumps(send_values, ensure_ascii=False).encode('utf-8')
    print(send_data)
    send_request = urllib2.Request(send_url, send_data)
    response = json.loads(urllib2.urlopen(send_request).read())
    print str(response)
if __name__ == '__main__':
    user = str(sys.argv[1])     # 第一个参数
    subject = str(sys.argv[2])  # 第二个参数
    content = str(sys.argv[3])  # 第三个参数
    corpid =  'ww2edb88243fd434522'   #CorpID是企业号的标识
    corpsecret = '_dsdf_8OPzNTxJWOlzcdfdfggtAEdsds3434dZs'  #corpsecretSecret是管理组凭证密钥
    accesstoken = gettoken(corpid,corpsecret)
    senddata(accesstoken,user,subject,content)

python 脚本基于 python2 的(linux 自带)

需要安装 simplejson模块

yum install -y python-pip # 安装 pip

pip install simplejson # 安装 simplejson 模块

⚠️使用微信报警需要在企业微信配置报警机器人,详细配置请自行百度,网上很多,可以搜索 “zabbix 配置微信报警”

配置时可能遇见 企业微信自定义应用 企业可信IP配置 的问题,点击链接解决即可。

添加定时任务

crontab -e
# MySQL cpu 监控,超过阈值 kill 相关进程
*/2 * * * * /bin/sh /root/cpu_mon.sh

使用时请根据自身情况修改脚本,本脚本仅供参考。


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2月前
|
SQL 监控 关系型数据库
【紧急救援】MySQL CPU 100%!一套组合拳教你快速定位并解决!
凌晨三点MySQL CPU飙至100%,业务瘫痪!本文亲历30分钟应急排障全过程:从紧急止血、定位慢查询、分析锁争用,到优化SQL与索引,最终恢复服务。总结一套可复用的排查路径与预防方案,助你告别深夜救火。
|
4月前
|
存储 弹性计算 安全
阿里云轻量服务器通用型、CPU优化型、多公网IP型、国际型、容量型不同实例区别与选择参考
阿里云轻量应用服务器实例类型分为通用型、CPU优化型、多公网IP型、国际型、容量型,不同规格族的适用场景和特点不同,收费标准也不一样。本文为大家介绍轻量应用服务器通用型、多公网IP型、容量型有何区别?以及选择参考。
|
2月前
|
存储 缓存 安全
阿里云轻量应用服务器实例:通用型、多公网IP型、CPU优化、国际及容量型区别对比
阿里云轻量服务器分通用型、CPU优化型、多公网IP型、国际型和容量型。通用型适合网站与应用;CPU优化型提供稳定高性能计算;多公网IP型支持2-3个IP,适用于账号管理;国际型覆盖海外地域,助力出海业务;容量型提供大存储,适配网盘与实训场景。
234 1
|
6月前
|
SQL 缓存 关系型数据库
MySQL 慢查询是怎样优化的
本文深入解析了MySQL查询速度变慢的原因及优化策略,涵盖查询缓存、执行流程、SQL优化、执行计划分析(如EXPLAIN)、查询状态查看等内容,帮助开发者快速定位并解决慢查询问题。
252 0
|
3月前
|
存储 缓存 数据挖掘
阿里云轻量应用服务器“CPU优化型”配置介绍、费用价格说明
阿里云轻量应用服务器推出CPU优化型,提供更强计算性能,2核4GB起,最高16核64GB,全系支持200Mbps带宽。适用于企业级应用、数据库、游戏服务器等高算力场景,保障稳定高效运行。
413 1
|
4月前
|
缓存 关系型数据库 MySQL
降低MySQL高CPU使用率的优化策略。
通过上述方法不断地迭代改进,在实际操作中需要根据具体场景做出相对合理判断。每一步改进都需谨慎评估其变动可能导致其他方面问题,在做任何变动前建议先在测试环境验证其效果后再部署到生产环境中去。
207 6
|
5月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
134 2
|
5月前
|
存储 SQL 关系型数据库
MySQL 动态分区管理:自动化与优化实践
本文介绍了如何利用 MySQL 的存储过程与事件调度器实现动态分区管理,自动化应对数据增长,提升查询性能与数据管理效率,并详细解析了分区创建、冲突避免及实际应用中的关键注意事项。
201 0
|
3月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
140 3
|
3月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。

推荐镜像

更多