问题描述:

同事使用mysqlbinlog工具的--read-from-remote-server --raw选项,从远程实例实时拉取二进制日志时,发现得到的二进制日志文件大小与远程实例上的源文件大小不相同,并且使用mysqlbinlog解析时会报错。

测试环境版本信息如下:

MySQL版本:5.7.17 log MySQL Community Server (GPL)  通用tar包安装

Mysqlbinlog版本:5.7.17 自带版本,mysqlbinlog Ver 3.4 for linux-glibc2.5 at x86_64

操作系统版本:CentOS Linux release 7.6.1810 (Core)

分析过程:

1、对比拉取到的二进制日志文件、源文件的大小,发现拉取到的二进制日志文件比源文件小,并且该文件大小:7315456/4096,刚好可以整除,源库上进行多次更新后,多次观察,发现该数值都可以被4096整除。

因为Linux块大小是4096 Bytes,所以,我先做个假设:mysqlbinlog工具以4096字节为单位进行写入

[root@192_168_129_128 tmp]# ll /usr/local/mysql-5.7.-linux-glibc2.-x86_64/data/mybinlog. ; ll mybinlog.
-rw-r----- mysql mysql May : /usr/local/mysql-5.7.-linux-glibc2.-x86_64/data/mybinlog.
-rw-r----- root root May : mybinlog.

2、我在8.0.19版本中测试,问题却不能复现。

3、写一个简单的shell脚本,将mysqlbinlog拉取进程放到后台执行,获取到上一步的pid之后,使用pstack命令和strace命令,分别查看该进程的函数调用和系统调用

#!/bin/bash 

nohup mysqlbinlog --read-from-remote-server --host=192.168.129.128 --user=dba --password= --raw --to-last-log binlog. --stop-never &

pid=`ps -ef | grep mysqlbinlog | grep -v 'grep' | awk '{print $2}'`

pstack $pid > pstack.log

strace -f -t -p $pid -o strace.log

pstack命令的输出结果如下,虽然没有直接的线索,但是后续可以利用这些函数栈名,去源码中找线索:

#  0x00007faf58f60a2d in recv () from /lib64/libpthread.so.
# 0x000000000045e9f9 in inline_mysql_socket_recv (flags=, n=, buf=0x1d959d1, mysql_socket=..., src_line=, src_file=0x550918 "/export/home/pb2/build/sb_0-21378219-1480347226.17/mysql-5.7.17/vio/viosocket.c") at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./include/mysql/psi/mysql_socket.h:
# vio_read (vio=0x1d90e60, buf=0x1d959d1 "27399199-10542799900-96491182343-85303866742-80196460272-89060617578-51177070778-10421134155;80308169113-21291571753-18876715410-91134905277-85771492482\360j\003", size=) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./vio/viosocket.c:
# 0x0000000000434873 in net_read_raw_loop (count=, net=0x1d8e550) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./sql/net_serv.cc:
# net_read_packet (net=0x1d8e550, complen=0x7fff6d023f88) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./sql/net_serv.cc:
# 0x0000000000434a7c in my_net_read (net=0x1d8e550) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./sql/net_serv.cc:
# 0x000000000043985b in cli_safe_read_with_ok (mysql=0x1d8e550, parse_ok= '\000', is_data_packet=0x0) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./sql-common/client.c:
# 0x0000000000426b36 in dump_remote_log_entries (logname=0x7fff6d0277e9 "binlog.000002", print_event_info=0x7fff6d0250f0) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./client/mysqlbinlog.cc:
# dump_single_log (logname=0x7fff6d0277e9 "binlog.000002", print_event_info=0x7fff6d0250f0) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./client/mysqlbinlog.cc:
# dump_multiple_logs (argc=, argv=<optimized out>) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./client/mysqlbinlog.cc:
# 0x0000000000427821 in main (argc=, argv=0x1d58468) at /export/home/pb2/build/sb_0--1480347226.17/mysql-5.7./client/mysqlbinlog.cc:

strace命令的输出结果如下:

....................省略若干行....................
:: recvfrom(, "6813690-49945547265-44609719076-"..., , , NULL, NULL) =
:: lseek(, , SEEK_CUR) =
:: write(, "553290165-79173089006-0778194955"..., ) =
:: write(, "2\360E\t\0\0p\3\0\0w71247594556-464035229"..., ) =
:: recvfrom(, " \0\0\211\0K\200\266^\20/\201\0\0\37\0\0\0\320d4\0\0\0\21\1\0\0\0\0\0\0"..., , , NULL, NULL) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: lseek(, , SEEK_CUR) =
:: recvfrom(,

strace到这里就没有后续的输出了。

其中,recvfrom系统调用,是经socket接收数据;lseek是一个用于改变读写一个文件时读写指针位置的一个系统调用;write把buf中nbyte写入文件描述符handle所指的文档,成功时返回写的字节数,错误时返回-1.

分析strace日志中的多条write记录,发现每次write的写入字节数都是4096,看起来与前面的假设吻合。

但是,每达到4096字节才写入磁盘,那么拉取到的binlog几乎不可能与源库一致。而mysqlbinlog的help显示,--read-from-remote-server参数,实质上是调用MySQL Server的BINLOG-DUMP-NON-GTIDS接口,远程实例应该是实时发送binlog events才对。

-R, --read-from-remote-server
Read binary logs from a MySQL server. This is an alias
for read-from-remote-master=BINLOG-DUMP-NON-GTIDS.

好吧,从pstack日志中得到的函数名,我们去源码中逐个查看,最终与本问题相关的是client/mysqlbinlog.cc中的dump_remote_log_entries()函数,如下:

/**
Requests binlog dump from a remote server and prints the events it
receives. @param[in,out] print_event_info Parameters and context state
determining how to print.
@param[in] logname Name of input binlog. @retval ERROR_STOP An error occurred - the program should terminate.
@retval OK_CONTINUE No error, the program should continue.
@retval OK_STOP No error, but the end of the specified range of
events to process has been reached and the program should terminate.
*/
static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
const char* logname)
{
....................省略若干行....................
for (;;)
{
const char *error_msg= NULL;
Log_event *ev= NULL;
Log_event_type type= binary_log::UNKNOWN_EVENT; len= cli_safe_read(mysql, NULL);
if (len == packet_error)
{
error("Got error reading packet from server: %s", mysql_error(mysql));
DBUG_RETURN(ERROR_STOP);
}
if (len < && net->read_pos[] == )
break; // end of data
DBUG_PRINT("info",( "len: %lu net->read_pos[5]: %d\n",
len, net->read_pos[]));
/*
In raw mode We only need the full event details if it is a
ROTATE_EVENT or FORMAT_DESCRIPTION_EVENT
*/ ....................省略若干行....................
if (raw_mode || (type != binary_log::LOAD_EVENT))
{ ....................省略若干行....................
if (raw_mode)
{
DBUG_EXECUTE_IF("simulate_result_file_write_error",
DBUG_SET("+d,simulate_fwrite_error"););
if (my_fwrite(result_file, net->read_pos + , len - , MYF(MY_NABP)))
/*可以看到,cli_safe_read()方法读取到的binlog event,都会调用my_fwrite函数进行写入,my_fwrite是对fwrite()函数的封装
这里并没有以4096字节为单位写入,而是读多少就写入多少
这就无法解释了,代码逻辑显示每次拿到数据之后,都会写入磁盘,为什么实际上却不是呢?*/
{
error("Could not write into log file '%s'", log_file_name);
retval= ERROR_STOP;
}
if (ev)
reset_temp_buf_and_delete(ev);
}
....................省略若干行.................... if (retval != OK_CONTINUE)
DBUG_RETURN(retval);
}
else
{
....................省略若干行....................
old_off+= len-;
} DBUG_RETURN(OK_CONTINUE);
}

请看我用中文注释在上述源码中的分析。

代码逻辑没有问题,那就有可能是BUG了。。。

结论:

通过上述分析,怀疑遇到了BUG,于是去官方的Release Notes中查找,最终在https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-19.html 中找到如下说明:

Replication:

mysqlbinlog, if invoked with the --raw option, does not flush the output file until the process terminates. But if also invoked with the --stop-never option,

the process never terminates, thus nothing is ever written to the output file. Now the output is flushed after each event. (Bug #24609402)

但是这个BUG在BUG system中找不到,应该是需要有权限的MOS账号才能查看。

5.7.17版本mysqlbinlog实时拉取的二进制日志不完整的原因分析的更多相关文章

  1. 【学习随手记】kubeadm 查看创建集群需要的镜像版本,附拉取镜像脚本

    查看创建集群需要的镜像版本 kubeadm config images list [--kubernetes-version <version>] 国内拉取镜像脚本 一般而言,直接使用ku ...

  2. 用setTimeout 代替 setInterval实时拉取数据

    在开发中,我们常常碰到需要定时拉取网站数据,如: setInterval(function(){ $.ajax({ url: 'xx', success: function( response ){ ...

  3. git day01笔记 常用操作命令 快照 推送 拉取

    ansible 批量在远程主机上执行命令或者脚本 git   做版本控制的一个工具 ## git操作命令: 工作区:当前编辑的区域 缓存区:add 之后的区域 本地仓库:commit之后的区域 远程仓 ...

  4. 【Gitlab】从Gitlab拉取项目+往Gitlab发布项目 【GitLab自定义端口】

    1>GIt需要提前安装在本地,本机,自己的电脑,开发环境电脑,IDEA所在的电脑 2>代码仓库:gitlab 3>开发工具:IDEA 4>内网搭建gitlab,访问url: h ...

  5. Git如何强制拉取一个远程分支到本地分支(转载)

    有时候,我们在使用git pull指令想把一个远程分支拉取到本地分支的时候,老是会拉取失败,这一般是因为某种原因,本地分支和远程分支的内容差异无法被git成功识别出来,所以git pull指令什么都不 ...

  6. git拉取远程分支并创建本地分支和Git中从远程的分支获取最新的版本到本地

    git拉取远程分支并创建本地分支 一.查看远程分支 使用如下Git命令查看所有远程分支: git branch -r 二.拉取远程分支并创建本地分支 方法一 使用如下命令: git checkout ...

  7. git常用操作 配置用户信息、拉取项目、提交代码、分支操作、版本回退...

    git常用操作 配置用户信息.拉取项目.提交代码.分支操作.版本回退... /********git 配置用户信息************/ git config --global user.name ...

  8. 【docker】查看docker镜像的版本号TAG,从远程仓库拉取自己想要版本的镜像

    要想查看镜像的版本好TAG,需要在docker hub查看 地址如下:https://hub.docker.com/r/library/ 进入之后,在页面左上角搜索框搜索, 例如搜索redis 搜索完 ...

  9. git上拉取tag,识别最新tag在此版本上新增tag

    通过shell 脚本自动获取最新tag,并输入最新版本后,推到git上 # 拉取分支上现有的tags git fetch --tags echo -e "所有tag列表" git ...

随机推荐

  1. [linux][mysql] 命令更改表结构:添加、删除、修改字段、调整字段顺序

    原文出处:http://www.phpernote.com/MySQL/1120.html 查看表结构: desc tabl_name; show columns fromtable_name: 常用 ...

  2. 【题解】P2024 [NOI2001]食物链 - 数据结构 - 并查集

    P2024 [NOI2001]食物链 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 动物王国中有三类动物 \(A,B ...

  3. 使用dynamic 和MEF实现轻量级的 AOP 组件 (1)

    转载https://www.cnblogs.com/niceWk/archive/2010/07/19/1780843.html AOP魔法 今天你AOP了吗?谈到AOP,总有一种神秘的感觉,人类对于 ...

  4. EasyPoi 导入导出Excel时使用GroupName的踩坑解决过程

    一.开发功能介绍: 简单的一个excel导入功能 二.Excel导入模板(大致模板没写全): 姓名 性别 生日 客户分类 联系人姓名 联系人部门 备注 材料 综合 采购 张三 男 1994/05/25 ...

  5. springmvc配置数据源方式

    1 阿里巴巴的druid数据源 <!-- 配置数据源 使用的是Druid数据源 -->-<bean destroy-method="close" init-met ...

  6. Javascript-异步详解

  7. Scala教程之:深入理解协变和逆变

    文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...

  8. KVM基本功能管理

    一.KVM基础功能管理 1.查看命令帮助 virsh -h 2.查看 KVM 的配置文件存放目录(CENTOS7.0.xml是虚拟系统实例的配置文件) ls /etc/libvirt/qemu     ...

  9. 天大福利!世界第一科技出版公司 Springer 免费开放 400 多本电子书!

    前几天,世界著名的科技期刊/图书出版公司施普林格(Springer)宣布:免费向公众开放 400 多本正版的电子书!! Springer 即施普林格出版社,于1842 年在德国柏林创立,20 世纪60 ...

  10. matlab画图(一)

    例1.画出函数图像 >> x=-pi/2:0.01:pi/2; >> y=x+sin(x)+exp(x); >> plot(x,y,'r','Linewidth', ...