用到的mysqlbinlog的相关参数
–base64-output参数用来控制binlog部分是否显示出来的,指定为decode-rows表示不显示binglog部分
加了-v参数比不加-v的多了sql部分
-v和-vv的区别在于-vv增加了备注部分
问题现象
前端工程师反映做mysql数据库恢复的时候,有一个binlog,滚了3天都没动静!
我把这个binlog取过来,对其进行解析:
mysqlbinlog binlog.344605 -v --base64-output=decode-rows > ./binlog1 [root@localhost mysql]# ls -lh total 3.2G -rw-r--r--. 1 root root 1.8G 7月 8 15:17 binlog1 -rw-r--r--. 1 oracle oinstall 1.4G 7月 8 14:47 binlog.344605
分析
一个binlog有1.4G,按照默认设置通常最大不超过1G。怀疑里面有大事务,因为mysql不能把一个大事务截断放到两个binlog中。
写了个python小程序找出binlog中的大事务。
[root@localhost mysql]# cat ./CheckEventLength.py #! /usr/bin/python3 file=open('binlog1','r+',encoding='UTF-8') i=0 begin_num=0 commit_num=0 while True: line = file.readline() # 只读取一行内容 # 判断是否读取到内容 if not line: break # print("%d %s" % (i,line[: 2])) i=i+1 if line[: 5]=='BEGIN': begin_num=i # print('begin line is %s' % (begin_num)) if line[: 6]=='COMMIT': commit_num=i # print('commit line is %s' % (commit_num)) if commit_num-begin_num > 2000: print("Big event starts at %d ends at %d"%(begin_num,commit_num)) file.close() [root@localhost mysql]# ./CheckEventLength.py Big event starts at 352870 ends at 31693367
从运行结果看,这个binlog中基本就一个事务,一个事务1.4G,厉害呀!看看里面执行的SQL,是update语句改了若干万条记录,从SQL语句看好像在做网站迁移。
[oracle@localhost mysql]$ grep "UPDATE \`gdjcyht\`.\`official_posts\`" binlog1|wc 658013 1974039 25004494 [oracle@localhost mysql]$ 这个update执行了65万次
这个update执行了65万次!
mysqlbinlog binlog.344605 | grep "GTID$(printf '\t')last_committed" -B 1 \ > | grep -E '^# at' | awk '{print $3}' \ > | awk 'NR==1 {tmp=$1} NR>1 {print ($1-tmp);tmp=$1}' \ > | sort -n -r | head -n 10 1468183562 3802 3596 3586 3584 3520 3490 3404 3382 3380
换一种检查大事务的方法,也是1.4G!
解决办法
尝试用–binlog-row-event-max-size来解决,关于这个参数mysql的说明如下:
--binlog-row-event-max-size=# The maximum size of a row-based binary log event in bytes. Rows will be grouped into events smaller than this size if possible. This value must be a multiple of 256. |
||
把这个参数设置成512M试试:
mysqlbinlog binlog.344605 -v --base64-output=decode-rows --binlog-row-event-max-size=536870912> ./binlog2
结果不行,效果和之前一样,注意这个参数说明里有“ if possible,如果可能”。
结论是这个不能用binlog滚,因为这个binlog row event超过了1G,而max_allowed_packet默认64MB,最大也只能设置1G。只能用全量恢复把这个binlog跳过去。这里可能有人问,那当时这个语句怎么产生的,一个合理的解释是,当时是用的 SQL statement,例如一个update 语句,类似 “update 新网站数据=旧网站数据 ” ,这样这个 SQL statement并不大。但binlog的格式设置为ROW,写到binlog里,每一行都要写一个update语句,总共update多少行,就有多少行update语句,这样就产生了一个巨无霸的事务。