前几天做个了慢查询日志切割的脚本部署,记录一下。
用户需求
每周一对数据库的慢查询日志进行切割,切割后的日志以日期结尾,保留一份历史记录
技术前提
要实现这个需求,首先需对原来的慢查询日志进行重命名,然后创建一个新的日志,并且为了在新日志中继续写入内容,需要在数据库中执行flush logs
命令。
Linux
系统自带的logrotate
就可以对日志进行切割。
可以在MySQL的安装目录的support-files
下查看切割日志的示例文件mysql-log-rotate
。
账号和密码的处理
由于需要执行flush logs
命令,需要保存账号和密码信息,有以下三种方案
- 直接在脚本中保存明文密码
- 将账号密码保存到
/root/.my.cnf
文件中,并将该文件设置为root
账号只读 - 将账号密码配置为
login-path
方案1和方案2都可能暴露明文密码,所以最后决定选用方案3
缺点是由于配置login-path
不能直接在命令行中输入密码,需要交互操作,目前我个人还找到批量执行操作的方法。
logrotate
logrotate参数
-
missingok
如果日志丢失,不报错继续滚动下一个日志 -
notifempty
当日志文件为空时,不进行轮转 -
sharedscripts
在所有日志都轮转后统一执行一次脚本,如果没有配置这个,每个日志轮转之后都会执行一次脚本 -
postotate
在logrotate
转储之后需要执行的指令 -
prerotate
在logrotate
转储之前需要执行的指令 -
daily
转储周期为每天 -
weekly
指定转储周期为每周 -
monthly
指定转储周期为每月 -
rotate count
执行日志文件删除之前转储的次数 5表示保留5个备份 -
dateext
后缀为日期 -
dateformat .%s
定义日期格式 -
size log-size
当日期>=log-size
时才对文件进行转储
logrotate配置
全局配置
/etc/logrotate.conf
独立配置
也可以在/etc/logrotate.d/
目录下创建独立的文件,单独配置转储文件及周期等
logrotate命令
检查脚本是否正常
logrotate -d mysql-log-rotate
强制转换日志文件
logrotate -f mysql-log-rotate
显示详细信息
logrotate -v mysql-log-rotate
logrotate的执行时间
在/etc/logrotate.conf
或者/etc/logrotate.d
中配置后不需要设置计划任务。
因为在logrotate
在/etc/cron.daily
文件下,每天都会自动执行
可以通过/etc/anacrontab
来查看计划任务是何时进行执行的
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
实际操作过程
非标准变量的处理
由于涉及的数据库数量较多,并且每台数据库的配置信息也有可能不一致,这里先用python
生成了下mysql-log-rotate
的脚本
import pymysql
from Scripts.dependents.databaseInfo import databaseInfo
from Scripts.dependents.mysqlCheck import mysqlCheck
db = databaseInfo()
dbinfo = db.getInfoFromTxt("D:\\mysqlinfo.txt")
checkList = {
"rotate":"select @@basedir,@@datadir,@@socket,@@slow_query_log_file,@@version"
}
a = mysqlCheck()
result = a.CheckMutilMySQL(dbinfo, checkList)
db_dir = {}
for i in result["rotate"]:
#print(i)
ip = i["ServerHost"]
port = i["ServerPort"]
basedir = i["@@basedir"]
#判断,如果slow_query_log_file不是绝对路径,加上datadir构成绝对路径
if "/" not in i["@@slow_query_log_file"]:
slow_query_log_file = i["@@datadir"] + i["@@slow_query_log_file"]
else:
slow_query_log_file = i["@@slow_query_log_file"]
version = i["@@version"][0:3]
if ip in db_dir.keys():
db_dir[ip].append([ip, port, basedir, slow_query_log_file, version])
else:
db_dir[ip] = [[ip, port, basedir, slow_query_log_file, version]]
for i in db_dir.keys():
if len(db_dir[i]) > 0:
slow = ""
testinfo = ""
for info in db_dir[i]:
filename = info[3]
basedir = info[2] + "bin/mysqladmin"
loginpath = "rotate" + str(info[1])
slow = slow + filename + "\n"
testinfo = testinfo + """\n if test -x {} && \\
{} --login-path={} ping &>/dev/null
then""".format(basedir, basedir, loginpath)
# 判断一下数据库版本,MySQL5.6不支持单独刷新慢查询日志
if i[4] == '5.6':
testinfo = testinfo + """
{} --login-path={} flush-logs
fi\n""".format(basedir, loginpath)
else:
testinfo = testinfo + """
{} --login-path={} flush-logs slow
fi\n""".format(basedir, loginpath)
text = slow + """{
notifempty
weekly
rotate 5
dateext
missingok
sharedscripts
postrotate \n""" + testinfo + """ endscript
}"""
#保存为以IP地址为名称的txt文件
f = open("d:\\logrotate\\" + db_dir[i][0][0] + ".txt",
'w',
encoding='utf8')
f.write(text)
f.close()
推送到服务器上
用ansible
推送到服务器上,由于沟通过程中用户又提出了新要求,要每周一1点进行日志的切割,使用默认位置的配置文件无法满足用户需求,更改为利用计划任务来执行脚本
ansible
推送脚本如下:
---
- hosts: all
remote_user: root
become_user: root
tasks:
- name: copy file
templates:
source: ../templates/{{ inventory_hostname }}.txt
dest: /home/mysql/mysql-log-rotate
- name: create crontab
cron:
name: mysql-log-rotate
job: /usr/sbin/logrotate -f /home/mysql/mysql-log-rotate
minute: 0
hour: 1
weekday: 1
遇到的坑和注意事项
多实例服务器日志刷新多次
原因
默认情况下,在每次日志轮转之后都执行一次postrotate
中的命令
解决方法
添加sharedscripts
选项,在所有日志都轮转后统一执行一次脚本
执行日志切割脚本后提示unkown command slow
原因
MySQL5.6版本中,flush-logs
不支持单独刷新慢查询日志
解决方法
增加对于数据库版本的判断
对于MySQL5.6版本的数据库,执行mysqladmin flush-logs
其他版本的数据库,执行mysqladmin flush-logs slow