mysql的binlog 被用来做主从, 实时备份等, 可谓非常重要(redo log你在干嘛....)
如果你经常使用Binlog的话, 你可能会遇到如下报错
ERROR: Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: 61, event_type: 44
ERROR: Could not read entry at offset 1195: Error in log format or read error.
咋办呢? 如果是从库的话, 重建从库就行(就是比较麻烦). 如果是实时备份或者需要分析Binlog的时候 咋办呢?
我们只需要修改event_header的某些值, 那么这个binlog文件就会被认为已经损坏了.
关于binlog的结构, 可以看我之前写的 BINLOG文件解析 这里就不再介绍了
老规矩, 本文提供的脚本在文末
本文是模拟环境, 所以不要去修改真实环境, 就拷贝一个文件意思意思
cp -ra /data/mysql_3308/mysqllog/binlog/m3308.001014 .
本次只修改event_type即可, 因为多数报错都是报这个错
import binlog_tool
aa = binlog_tool.listevent('m3308.001014') #参数为binlog文件路径
aa[14] #查看第14个event信息
binlog_tool.mevent_type('m3308.001014',1676,66) #binlog文件名 起始pos号 修改的新的event_type值
aa = binlog_tool.listevent('m3308.001014')
aa[14] #再次确认是否修改成功
说明我们确实破坏了binlog, 那么现在咋办呢?
既然这个event坏了, 那我们就不要这个event了. (壮士断腕.jpg)
如果你使用了我提供的 binlog_tool.listevent
去解析binlog的话, 你就能看到下一个event的地址(end_pos), 直接指定从下一个地址开始即可
mysqlbinlog --start-position=1737 m3308.001014 >/dev/null #本次实验是1737
然后把两次解析的结果拼起来就行. (之前报错的时候, 前面部分是解析正常的)
如果你没有使用我给的工具的话, 你就的自己一个个pos号往后试了 -_-
binlog损坏的场景并不常见(sync_binlog=1的环境,binlog写失败事务就回滚了), 但是遇到了还是得有解决的办法才行.
1. 跳过有问题的event 可能会丢数据. 不过本次有问题的event是gtid, 所以不会丢数据
2. sync_binlog设置为1很重要. 起码你最多就丢一个事务.
3. 会一门编程语言,对dba运维好处很大. 能解决很多问题.
其实我还给了个read_event的函数, 方便你去分析有问题的那个event. 只不过本文未演示而已
import struct
import os
def listevent(filename='m3308.001014',):
el = []
with open(filename,'rb') as f:
magic = f.read(4)
if magic != b'\xfebin':
return []
while True:
header = f.read(19)
if len(header) == 19:
timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack("<LBLLLh",header[0:19])
el.append({'timestamp':timestamp,'start_pos':f.tell()-19,'end_pos':log_pos,'size':event_size,'event_type':event_type})
event_body = f.read(event_size-19)
else:
print('endsize:',f.tell(),'event_count',len(el))
break
return el
def mevent_type(filename='m3308.001014',start_pos=4,new_value=250):
f = os.open(filename, os.O_RDWR|os.O_CREAT, 0o644)
os.lseek(f, start_pos+4, 0)
os.write(f, struct.pack('<B',new_value))
os.close(f)
return True
def read_event(filename='m3308.001014',start_pos=4,max_size=100000):
with open(filename,'rb') as f:
f.seek(start_pos,0)
header = f.read(19)
timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack("<LBLLLh",header[0:19])
if event_size < max_size:
event_body = f.read(event_size-19)
return {'timestamp':timestamp,'event_type':event_type,'server_id':server_id,'event_size':event_size,'log_pos':log_pos,'flags':flags,'event_body':event_body}
else:
return None