PYTHON模拟BINLOG文件损坏 以及 解决办法(python生成模拟数据)
 南窗  分类:IT技术  人气:115  回帖:0  发布于1年前 收藏

导读

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的时候 咋办呢?

PYTHON模拟binlog文件损坏

我们只需要修改event_header的某些值, 那么这个binlog文件就会被认为已经损坏了.

关于binlog的结构, 可以看我之前写的 BINLOG文件解析 这里就不再介绍了

老规矩, 本文提供的脚本在文末

拷贝一个Binlog过来

本文是模拟环境, 所以不要去修改真实环境, 就拷贝一个文件意思意思

cp -ra /data/mysql_3308/mysqllog/binlog/m3308.001014 .

执行脚本,破坏Binlog

本次只修改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] #再次确认是否修改成功
33代表GTID_LOG_EVENT

使用mysqlbinlog解析发现报错

说明我们确实破坏了binlog, 那么现在咋办呢?

event_type确实是我们刚才修改的值

解决办法

既然这个event坏了, 那我们就不要这个event了. (壮士断腕.jpg)

1. binlog_tool.listevent

如果你使用了我提供的 binlog_tool.listevent 去解析binlog的话, 你就能看到下一个event的地址(end_pos), 直接指定从下一个地址开始即可

mysqlbinlog --start-position=1737 m3308.001014 >/dev/null #本次实验是1737

然后把两次解析的结果拼起来就行. (之前报错的时候, 前面部分是解析正常的)

一个个POS号往后加

如果你没有使用我给的工具的话, 你就的自己一个个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

讨论这个帖子(0)垃圾回帖将一律封号处理……