前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >技术分享 | 如何通过 binlog 定位大事务?

技术分享 | 如何通过 binlog 定位大事务?

作者头像
爱可生开源社区
发布于 2024-03-07 09:08:28
发布于 2024-03-07 09:08:28
71700
代码可运行
举报
运行总次数:0
代码可运行

作者:李彬,爱可生 DBA 团队成员,负责项目日常问题处理及公司平台问题排查。爱好有亿点点多,吉他、旅行、打游戏…

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 1400 字,预计阅读需要 4 分钟。

1序

大事务想必大家都遇到过,既然要对大事务进行拆分,第一步就是要找到它。那么如何通过 binlog 来定位到大事务呢?

首先,可通过 binlog 文件的大小来判断是否存在大事务,当一个 binlog 文件快被写完时,突然出现大事务,会突破 max_binlog_size 的大小继续写入。

官方文档[1] 中是这样描述的:

A transaction is written in one chunk to the binary log, so it is never split between several binary logs. Therefore, if you have big transactions, you might see binary log files larger than max_binlog_size.

根据这个特点,只要进入 binlog 的存放目录,观察到文件大小异常的 binlog,那么你就可以去解析这个 binlog 获取大事务了。当然,需要注意的是,这只是一部分,文件大小正常的 binlog 中也藏着大事务。

2实践

既然要定位大事务的 SQL,针对已开启 GTID 的实例,只要定位到对应的 GTID 即可,下面我们开始对一个 binlog 进行解析:

环境

测试环境

binlog 格式

row

binlog 版本

v4

GTID

已开启

已验证的 MySQL 版本

MySQL 5.7.30、MySQL 8.0.28

首先,我们解析出一个 binlog 中按照事务大小排名前 N 的事务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 为了方便保存为脚本,这里定义几个基本的变量
BINLOG_FILE_NAME=$1                       # binlog文件名
TRANS_NUM=$2                              # 想要获取的事务数量
MYSQL_BIN_DIR='/data/mysql/3306/base/bin' # basedir

# 获取前TRANS_NUM个大事务
${MYSQL_BIN_DIR}/mysqlbinlog ${BINLOG_FILE_NAME} | 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);tmp=$1}' | sort -n -r -k 1 | head -n ${TRANS_NUM} > binlog_init.tmp

经过第一步对 binlog 的基本解析后,我们已经拿到了对应事务的大小和可供定位 GTID 的 POS 信息,接下来对上述输出的临时文件进行逐行解析,针对每一个事务获取到相应的信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
while read line
do
    # 事务大小这里取近似值,因为不是通过(TRANS_END_POS-TRANS_START_POS)计算出的
    TRANS_SIZE=$(echo ${line} | awk '{print $1}')
    logWriteWarning "TRANS_SIZE: $(echo | awk -v TRANS_SIZE=${TRANS_SIZE} '{ print (TRANS_SIZE/1024/1024) }')MB"
    FLAG_POS=$(echo ${line} | awk '{print $2}')
    # 获取GTID
    ${MYSQL_BIN_DIR}/mysqlbinlog -vvv --base64-output=decode-rows ${BINLOG_FILE_NAME} | grep -m 1 -A3 -Ei "^# at ${FLAG_POS}" > binlog_parse.tmp
    GTID=$(cat binlog_parse.tmp | grep -i 'SESSION.GTID_NEXT' | awk -F "'" '{print $2}')
    # 通过GTID解析出事务的详细信息
    ${MYSQL_BIN_DIR}/mysqlbinlog --base64-output=decode-rows -vvv --include-gtids="${GTID}" ${BINLOG_FILE_NAME} > binlog_gtid.tmp
    START_TIME=$(grep -Ei '^BEGIN' -m 1 -A 3 binlog_gtid.tmp | grep -i 'server id' | awk '{print $1,$2}' | sed 's/#//g')
    END_TIME=$(grep -Ei '^COMMIT' -m 1 -B 1 binlog_gtid.tmp | head -1 | awk '{print $1,$2}' | sed 's/#//g')
    TRANS_START_POS=$(grep -Ei 'SESSION.GTID_NEXT' -m 1 -A 1 binlog_gtid.tmp | tail -1 | awk '{print $3}')
    TRANS_END_POS=$(grep -Ei '^COMMIT' -m 1 -B 1 binlog_gtid.tmp | head -1 | awk '{print $7}')
    # 输出
    logWrite "GTID: ${GTID}"
    logWrite "START_TIME: $(date -d "${START_TIME}" '+%F %T')"
    logWrite "END_TIME: $(date -d "${END_TIME}" '+%F %T')"
    logWrite "TRANS_START_POS: ${TRANS_START_POS}"
    logWrite "TRANS_END_POS: ${TRANS_END_POS}"
    # 统计对应的DML语句数量
    logWrite "该事务的DML语句及相关表统计:"
    grep -Ei '^### insert' binlog_gtid.tmp | sort | uniq -c
    grep -Ei '^### delete' binlog_gtid.tmp | sort | uniq -c
    grep -Ei '^### update' binlog_gtid.tmp | sort | uniq -c

done < binlog_init.tmp

至此,我们已经基本实现了通过解析一个 binlog 文件,从而拿到对应的 GTID、事务开始和结束时间、事务开始和结束的 POS、对应的 DML 语句数量统计。为了不重复执行解析命令,我们可以将其封装为脚本,作为日常运维工具使用。

最终效果展示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[root@localhost ~]$ sh parse_binlog.sh /opt/sandboxes/rsandbox_5_7_35/master/data/mysql-bin.000003 2
2023-12-12 15:15:40 [WARNING] 开始解析BINLOG: /opt/sandboxes/rsandbox_5_7_35/master/data/mysql-bin.000003 
2023-12-12 15:15:53 [WARNING] TRANS_SIZE: 0.00161743MB 
2023-12-12 15:16:06 [INFO] GTID: 00020236-1111-1111-1111-111111111111:362779 
2023-12-12 15:16:06 [INFO] START_TIME: 2023-12-12 15:14:35 
2023-12-12 15:16:06 [INFO] END_TIME: 2023-12-12 15:14:35 
2023-12-12 15:16:06 [INFO] TRANS_START_POS: 362096066 
2023-12-12 15:16:06 [INFO] TRANS_END_POS: 362097697 
2023-12-12 15:16:06 [INFO] 该事务的DML语句及相关表统计: 
      1 ### INSERT INTO `sbtest`.`sbtest100`
      1 ### DELETE FROM `sbtest`.`sbtest100`
      2 ### UPDATE `sbtest`.`sbtest100`
2023-12-12 15:16:06 [WARNING] TRANS_SIZE: 0.00161648MB 
2023-12-12 15:16:25 [INFO] GTID: 00020236-1111-1111-1111-111111111111:505503 
2023-12-12 15:16:25 [INFO] START_TIME: 2023-12-12 15:15:36 
2023-12-12 15:16:25 [INFO] END_TIME: 2023-12-12 15:15:36 
2023-12-12 15:16:25 [INFO] TRANS_START_POS: 603539112 
2023-12-12 15:16:25 [INFO] TRANS_END_POS: 603540742 
2023-12-12 15:16:25 [INFO] 该事务的DML语句及相关表统计: 
      1 ### INSERT INTO `sbtest`.`sbtest100`
      1 ### DELETE FROM `sbtest`.`sbtest100`
      1 ### UPDATE `sbtest`.`sbtest100`
      1 ### UPDATE `sbtest`.`sbtest87`

通过上述结果可以看到,这种解析方式是基于事务的大小进行排序的,有时我们还可能需要从时间维度进行排序,通过大致相同的思路写脚本也可以实现,这里提供一个开源的工具 my2sql[2]

my2sql 可指定 rows 和 time 进行过滤,在 mode 为 file 且 work-type 为 stats 时,连接任意一个 MySQL 实例(无需原库)均可对 binlog 中的事务进行解析。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 统计指定 binlog 中各个表的 DML 操作数量(不加 row 和 time 限制)
[root@localhost ~]$ mkdir tmpdir
[root@localhost ~]$ ./my2sql -user root -password xxx -host 127.0.0.1 -port 3306 -mode file -local-binlog-file ./mysql-bin.005375 -work-type stats -start-file mysql-bin.005375 -output-dir ./tmpdir

# 按照事务的行数倒序排序
[root@localhost ~]$ less tmpdir/biglong_trx.txt | sort -nr -k 6 | less

# 按照事务的执行时间倒序排序
[root@localhost ~]$ less tmpdir/biglong_trx.txt | sort -nr -k 7 | less

# 输出示例(binlog starttime stoptime startpos stoppos rows duration tables)
mysql-bin.005375 2023-12-12_16:04:06 2023-12-12_16:16:59 493014756 582840954 123336 53 [test.t1(inserts=61668, updates=0, deletes=0) test.t2(inserts=61668, updates=0, deletes=0)]

参考资料

[1]

sysvar_max_binlog_size: https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_max_binlog_size

[2]

my2sql: https://github.com/liuhr/my2sql

第15问:快速找到 binlog 中是否有大事务

本文关键字:#MySQL# #事务# #binlog#

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱可生开源社区 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
大佬,你这个有点问题
大佬,你这个有点问题
回复回复点赞举报
你这不能用啊
你这不能用啊
回复回复点赞举报
推荐阅读
Sql2005过程分页
ALTER PROCEDURE [dbo].[procPageChangeForSql2005] ( @fromSql varchar(4000), @querySql varchar(4000), @whereSql varchar(max), @orderBySql varchar(200), @keyField varchar(100), @PageSize int, --每页多少条记录 @PageIndex int,
跟着阿笨一起玩NET
2021/09/26
7580
通用分页存储过程,干货无污染
---判断是否存在该存储过程 IF EXISTS (SELECT * FROM DBO.SYSOBJECTS WHERE ID = OBJECT_ID(N'[dbo].[WEB_PageView]') and OBJECTPROPERTY(ID, N'IsProcedure') = 1) DROP PROCEDURE [dbo].[WEB_PageView] GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO CREATE PROCEDURE
追逐时光者
2022/02/15
3020
据说很快的数据库分页存储过程
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/100555.html原文链接:
全栈程序员站长
2021/05/06
5220
sql 2008 分页存储过程
set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go -- [SelectBase] 1,1,'Users','username=''test''' ALTER procedure [dbo].[SelectBase] @PageIndex int, @PageSize int, @TableName nvarchar(max), @Where
javascript.shop
2019/09/04
1.2K0
silverlight + wcf(json格式) + sqlserver存储过程分页
silverlight并没有提供现成的分页控件,百度了一圈,也没有发现aspx中好用的类似AspNetPager成熟控件,网上现有的一些分页代码,很多也是基于1.0版本的,silverlight2.0的并不多,自个儿琢磨了一下,发现自己弄一个也并非难事,思路和主要代码分享如下: 1.通用的“海量”数据分页存储过程 在做aspx开发时我已经用存储过程分页多年,这个东东是通用的(不管前端用什么语言来做),而且性能也不错,所以这里就直接套过来用了,百度一下"分页存储过程"会有N多版本,这里也给出我自己写的一个版本
菩提树下的杨过
2018/01/22
1.9K0
silverlight + wcf(json格式) + sqlserver存储过程分页
SQL 存储过程分页
CREATE PROC p_Team_GetTemaList @pageindex INT , @pagesize INT , @keywords VARCHAR(200) , --模糊查询 名称 标签 @citycode VARCHAR(100) , @verifyStatus INT =0,-- -1所有的 0 没有通过 1 审核通过 @orderby INT, @TotalCount INT OUTPUT AS BEGIN
纯粹是糖
2018/03/14
1.6K0
重写了MSSQL分页存储过程,附完整sql脚本
晚上花了1小时重写了MSSQL数据库中的分页存储过程,采用ROW_NUMBER的方式,需要MSSQL2005及以上版本支持。
崔文远TroyCui
2019/02/26
1.2K0
通用分页存储过程
/*通用分页存储过程*/ USE HotelManagementSystem GO IF EXISTS(SELECT * FROM sys.objects WHERE NAME='cndoup_GetPageOfRecords') DROP PROCEDURE cndoup_GetPageOfRecords GO --创建存储过程 CREATE PROCEDURE cndoup_GetPageOfRecords @pageSize int = 20,
欢醉
2018/01/22
8840
关于数据库存储过程分页DatagridView BindingNavigator 控件的详细实现
BindingNavigator: 就是DataGridView控件上面的那个,在工程里名字: bindngrDemo
全栈程序员站长
2022/09/14
5100
关于数据库存储过程分页DatagridView BindingNavigator 控件的详细实现
针对Ext js的分页存储过程-适用于sqlserver2008
USE [UTMP_RC_121213] GO /****** Object: StoredProcedure [dbo].[PageProc] Script Date: 01/05/2013 08:45:56 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: 刘晓伦 -- Create date: 12
liulun
2022/05/09
5590
MSSQL之二十一 存储过程案例
张哥编程
2024/12/17
1330
使用ROW_NUMBER()的分页存储过程
分页存储过程,可以直接通过传入表名进行分页的,用到了ROW_NUMBER()函数,所以只适用于MSSQL2005以上. Sql代码 -- Description: 分页,用到了ROW_NUMBER() -- ============================================= create PROCEDURE [dbo].[proc_ShowPage] @tblName varchar(255), -- 表名 @strGetFields varchar(1000
用户8983410
2021/11/02
4810
高效分页存储过程
存储过程与页面调用如下:CREATEPROCEDUREsearch_sp@tblNamevar
Java架构师必看
2021/03/22
5130
祺佑三层开发框架(猫框)2023.11.16发新
加菲猫的VFP
2023/11/16
2290
祺佑三层开发框架(猫框)2023.11.16发新
解密encrypt的存储过程
--破解函数,过程,触发器,视图.仅限于SQLSERVER2000 IF EXISTS (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[SP_DECRYPT]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[SP_DECRYPT] GO CREATE PROCEDURE sp_decrypt(@objectname varchar(50)) AS begin set nocount on --破解字节不受限制,适用于SQLSERVER2000存储过程,函数,视图,触发器 --修正上一版视图触发器不能正确解密错误 begin tran declare @objectname1 varchar(100),@orgvarbin varbinary(8000) declare @sql1 nvarchar(4000),@sql2 varchar(8000),@sql3 nvarchar(4000),@sql4 nvarchar(4000) DECLARE @OrigSpText1 nvarchar(4000), @OrigSpText2 nvarchar(4000) , @OrigSpText3 nvarchar(4000), @resultsp nvarchar(4000) declare @i int,@status int,@type varchar(10),@parentid int declare @colid int,@n int,@q int,@j int,@k int,@encrypted int,@number int select @type=xtype,@parentid=parent_obj from sysobjects where id=object_id(@objectname) create table #temp(number int,colid int,ctext varbinary(8000),encrypted int,status int) insert #temp SELECT number,colid,ctext,encrypted,status FROM syscomments WHERE id = object_id(@objectname) select @number=max(number) from #temp set @k=0 while @k<=@number begin if exists(select 1 from syscomments where id=object_id(@objectname) and number=@k) begin if @type='P' set @sql1=(case when @number>1 then 'ALTER PROCEDURE '+ @objectname +';'+rtrim(@k)+' WITH ENCRYPTION AS ' else 'ALTER PROCEDURE '+ @objectname+' WITH ENCRYPTION AS ' end) if @type='TR' begin declare @parent_obj varchar(255),@tr_parent_xtype varchar(10) select @parent_obj=parent_obj from sysobjects where id=object_id(@objectname) select @tr_parent_xtype=xtype from sysobjects where id=@parent_obj if @tr_parent_xtype='V' begin set @sql1='ALTER TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION INSTERD OF INSERT AS PRINT 1 ' end else begin set @sql1='ALTER TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION FOR INSERT AS PRINT 1 ' end end if @type='FN' or @type='TF' or @type='IF' set @sql1=(case
jack.yang
2025/04/05
490
SQL基础分页存储过程(案例一)
1 --分页 存储过程 案例 2 3 -- 所执行的存储过程 4 create proc pageForUsers 5 @currPage int, --当前页数 6 @pageSize int, --每页多少条记录 7 @count int output --总记录数 8 as 9 declare @firstIndex int 10 declare @lastIndex i
用户1112962
2018/07/04
5210
曾今的代码系列——自己的分页控件+存储过程实现分页
项目里面的测试代码,仅供参考 LoginByAjax <title>Ajax登陆</title> <script src="Scripts/common.js" type="text/javascript"></script> <script type="text/javascript"> var xhr; window.onload = function () { xhr = new createXmlHttp();
用户1161731
2018/01/11
7630
曾今的代码系列——自己的分页控件+存储过程实现分页
SQLSERVER存储过程语法详解
@parameter 过程中的参数。在 Create PROCEDURE 语句中可以声明一个或多个参数。用户必须在执行过程时提供每个所声明参数的值(除非定义了该参数的默认值)。
全栈程序员站长
2022/09/13
1.8K0
关于SQL Server 镜像数据库快照的创建及使用
从SQL Server 2005 SP1 起,SQL 开始支持数据库镜像。它的设计目的是试图为SQL Server 提供一个具有实时性数据同步的灾难恢复技术,即能够提供数据冗余备份,切换起来比较方便。每个主体数据库只能有一个镜像数据库。镜像数据库作为主体数据库的一个副本,在主体数据库发生故障、不可访问时能够迅速恢复数据库访问,提供故障恢复功能。镜像数据库一直处于“恢复”状态,因此不能被直接访问。
东山絮柳仔
2021/03/20
2.4K0
走向面试之数据库基础:三、SQL进阶之变量、事务、存储过程与触发器
  SELECT 以表格的方式输出,可以同时输出多个变量;而PRINT 则是以文本的方式输出,一次只能输出一个变量的值。
Edison Zhou
2018/08/20
1.3K0
走向面试之数据库基础:三、SQL进阶之变量、事务、存储过程与触发器
相关推荐
Sql2005过程分页
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档