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文章, 我们小小的改动一下, 就能统计表的执行情况了.(脚本见文末)
比如:
看到哪些表操作次数多, 就i基本上能猜到原因了(得熟悉业务才行, 不熟悉业务就把这个截图发给开发,他们基本上秒懂)
有些问题是没得直接的报错的, 比如这种延迟增大,并不会直接以报错的形式展示, 往往就不太好排查.
本文给出的脚本每次只能解析一个文件, 当然你也可以根据之前的文章修改为解析多个Binlog文件(正则表达式)
搭配sort使用效果更佳哦!
本脚本计数是从0开始的, 所以看到操作次数为0 也不要奇怪哈(我也懒得去改了)
anabinlog.py
#!/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 删除。