前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >[MYSQL案例][014] mysql主从延迟增大排查

[MYSQL案例][014] mysql主从延迟增大排查

原创
作者头像
大大刺猬
发布2023-08-15 19:26:49
发布2023-08-15 19:26:49
46700
代码可运行
举报
文章被收录于专栏:大大刺猬大大刺猬
运行总次数:0
代码可运行

环境

mysql从库延迟一直增大

分析和解决

1. 延迟一直在增大, 说明mysql复制线程是正常的, 使用 show slave status 查看主从延迟相差多少

如果配置了gtid 就看 Executed_Gtid_Set

如果未配置gtid, 就看Master_Log_File 和 Read_Master_Log_Pos

2. 延迟不大的话, 一般就等就行, 如果很大的话, 可能就需要重建了.

但本文是讲找原因的.

通常我们使用binlog2sql 或者 my2sql来解析binlog得到相关的sql信息, 也可以使用官方的mysqlbinlog解析Binlog得到相关信息.

但解析出来的信息不太直观, 我们需要一些统计信息.

回顾一下我们之前解析的binlog文章, 我们小小的改动一下, 就能统计表的执行情况了.(脚本见文末)

比如:

本文是模拟的数据, 所以没得很夸张的断层, 实际上是有1-2张操作次数很夸张的表的
本文是模拟的数据, 所以没得很夸张的断层, 实际上是有1-2张操作次数很夸张的表的

看到哪些表操作次数多, 就i基本上能猜到原因了(得熟悉业务才行, 不熟悉业务就把这个截图发给开发,他们基本上秒懂)

总结

有些问题是没得直接的报错的, 比如这种延迟增大,并不会直接以报错的形式展示, 往往就不太好排查.

本文给出的脚本每次只能解析一个文件, 当然你也可以根据之前的文章修改为解析多个Binlog文件(正则表达式)

搭配sort使用效果更佳哦!

附源码

本脚本计数是从0开始的, 所以看到操作次数为0 也不要奇怪哈(我也懒得去改了)

anabinlog.py

代码语言:javascript
代码运行次数:0
复制
#!/usr/bin/env python
import struct
import sys
import os

def btoint(bdata,t='little'):
	return int.from_bytes(bdata,t)

def event_header(bdata):
	timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack("<LBLLLh",bdata[0:19])
	return {"timestamp":timestamp,'event_type':event_type,'server_id':server_id,'event_size':event_size,'log_pos':log_pos,'flags':flags,}


if len(sys.argv) != 2:
	print(f'Usage: python {sys.argv[0]} binlog')
filename=sys.argv[1]

if not os.path.exists(filename):
	print(f'file {filename} not exists')

def first_event(bdata):
	#FORMAT_DESCRIPTION_EVENT
	ethl = len(bdata) - 57 #2 50 4 1 var
	ff = f'<h50sLB{ethl}s'
	binlog_version, mysql_server_version, create_timestamp, event_header_length, event_type_header_length = struct.unpack(ff,bdata)
	mysql_server_version = mysql_server_version.decode('ascii').replace('\x00','') #美化一下
	event_type_header_length = [ int(x) for x in event_type_header_length ] #event specific header length. 比如TABLE_MAP_EVENT = 8 (table_id:6 + flag:2) #记录其它event的post header的长度
	return {'binlog_version':binlog_version, 'mysql_server_version':mysql_server_version, 'create_timestamp':create_timestamp, 'event_header_length':event_header_length, 'event_type_header_length':event_type_header_length,}


def table_map_event(bdata):
	post_header = {'table_id':btoint(bdata[0:6]), 'flags':btoint(bdata[6:8])} #flags保留字段
	offset = 8
	database_length = btoint(bdata[offset:offset+1])
	offset +=1
	database_name = bdata[offset:offset+database_length].decode() #0x00 结尾
	offset += database_length + 1
	table_length = btoint(bdata[offset:offset+1])
	offset +=1
	table_name = bdata[offset:offset+table_length].decode() #0x00 结尾, 但是我不读,计数的时候别忘了就行
	offset += table_length + 1
	column_count = btoint(bdata[offset:offset+1]) #Packed Integer 我只考虑0-250个字段. 也就是占用1字节 计算方式https://dev.mysql.com/doc/dev/mysql-server/latest/classbinary__log_1_1Binary__log__event.html#packed_integer
	offset += 1
	column_type_list = []
	for x in range(column_count):
		column_type_list.append(btoint(bdata[offset:offset+1])) #先不做转换了.具体类型参考https://dev.mysql.com/doc/dev/mysql-server/latest/classbinary__log_1_1Table__map__event.html
		offset += 1

	#metadata_length和column_count一样, 但是我不想写了
	#省略 metadata_length metadata null_bits optional metadata fields
	return {
		'post_header':post_header,
		'body':{
			'database_name':database_name,
			'table_name':table_name,
			'column_type_list':column_type_list,
		}
		}

if __name__ == '__main__':
	#只解析table_map event
	tabledict = {}
	with open(filename,'rb') as f:
		magic = f.read(4)
		if magic != b'\xfebin':
			print(f'{filename} is not binlog')
		while True:
			try:
				common_header = event_header(f.read(19))
			except:
				break
			if common_header == b'':
				break #读完了这个文件
			event_bdata = f.read(common_header['event_size']-19)
			if common_header['event_type'] == 19: #table_map event
				event_data = table_map_event(event_bdata)
				table_name = f"{event_data['body']['database_name']}.{event_data['body']['table_name']}"
				if table_name in tabledict:
					n = tabledict[table_name] + 1
					tabledict[table_name] = n
				else:
					tabledict[table_name] = 0

	for x in tabledict:
		print(f'表名: {x}  操作次数: {tabledict[x]}')

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境
  • 分析和解决
  • 总结
  • 附源码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档