使用 binlog2sql 要求 日志是打开的 ON ,并且 binlog_format 要为 ROW 类型,如果不满足,则不能使用 binlog2sql 恢复数据,后面就不用看了。
查询日志命令如下:
show variables like 'log_bin';
查询当前MySQL服务器的二进制日志格式,命令如下:
show variables like 'binlog_format';
binlog_format:
是MySQL二进制日志的一个系统变量,它定义了二进制日志的格式。
binlog_format 可以设置为如下值:
STATEMENT:
在这个模式下,二进制日志记录的是SQL语句本身,而不是数据的更改。这种格式是最简单的,但可能存在一些限制和问题,比如对于同一个表的多个更新操作,可能会产生重复的日志记录。
ROW:
在这个模式下,二进制日志记录的是每一行数据的变化。这种格式提供了更高的精度和灵活性,但会产生更多的日志数据。
MIXED:
在这个模式下,MySQL会根据执行的SQL语句的类型和参数,选择使用STATEMENT或ROW格式来记录二进制日志。这种格式旨在平衡精度和性能。
binlog2sql:
从MySQL binlog解析出你要的SQL。根据不同选项,你可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。
作用:
数据快速回滚(闪回)
主从切换后新master丢数据的修复
从binlog生成标准SQL,带来的衍生功能
项目状态:
正常维护。应用于部分公司线上环境。
已测试环境
Python 2.7, 3.4+
MySQL 5.6, 5.7
这是官方文档的地址:
https://github.com/danfengcao/binlog2sql
个人觉得可以写的再详细点…
安装 binlog2sql 需要 git ,安装也折腾了好久,这里做个记录。
下载地址:
https://github.com/git/git/tags
我下载的是 2.43.0 版本,下载后上传到服务器目录,这里上传到 /usr/local/git 目录
解压,命令如下:
tar -zxvf 安装包名称
这是我的:
tar -zxvf git-2.43.0.tar.gz
进入git根目录
cd git-2.43.0/
编译安装,需要一点时间,注意 prefix 后面不是安装目录,直接复制黏贴即可。
make prefix=/usr/local/git all
中间太长就没截图
然后再执行如下命令:
make prefix=/usr/local/git install
查看git安装成功没有,命令如下:
git --version
安装成功!!
如果安装报错:
git-curl-compat.h:3:10: fatal error: curl/curl.h: No such file or directory
原因:
这个错误是说没有找到 curl/curl.h 这个文件。这通常意味着你的系统上没有安装 libcurl,或者安装了但是其头文件没有在你的系统路径中。
尝试安装对应 libcurl ,命令如下:
sudo apt-get install libcurl4-openssl-dev
如果报错:
sudo: apt-get: command not found
原因:
这通常意味着 apt-get 没有在你的系统路径中,或者你的系统可能不是基于 Debian 或 Ubuntu 的。
查看自己的系统版本,命令如下:
uname -a
嗯。。确实不是 Debian 或 Ubuntu 。
那就先下载curl
下载地址:
https://curl.se/download.html
我下载的是 8.4.0 版本,下载后上传到服务器目录,这里上传到 /usr/local/curl
解压,命令如下:
tar -zxvf 安装包
这是我的:
tar -zxvf curl-8.4.0.tar.gz
解压完成后进入根目录
增加如下配置指定安装目录,直接复制黏贴,命令如下:
./configure --prefix=/usr/local/curl
执行完毕在进行安装:
make
如果执行 make 命令报如下错:
如果执行结束时报错,这时执行如下命令:
./configure --prefix=/usr/local/curl-8.4.0/ --with-wolfssl
如果报如下错:
试着用如下命令:
./configure --with-openssl
成功!!
感谢这个老哥的博客,如果还没成功,只能说这个办法不适合所有人。
然后再进行 make ,需要点时间,命令如下:
make
make 成功!!
再使用如下命令:
make install
如果报错如下:
make: *** No rule to make target 'install'. Stop.
更新一下 yum ,先执行如下命令:
yum update
需要点时间
然后再执行
make install
检测是否安装成功,就是查看 curl 版本号,命令如下:
curl --version
成功!!
测试是否可以使用,以测试百度为例,命令如下:
curl www.baidu.com
应该是没问题了。
http-push.c:27:10: fatal error: expat.h: No such file or directory
原因:
这通常是因为你的系统缺少一个名为expat的库。expat是一个C语言库,用于解析XML数据。git需要这个库来正常工作。
安装expat库,命令如下:
yum install expat-devel
安装pip之前,必须已经安装Python,并且版本必须为 Python 2.7, 3.4+,
查看没有安装Python,命令如下:
python --version
可以看到已经安装Python,版本为 3.6.8 ,符合 binlog2sql 对 Python 的版本要求。
然后安装pip,命令如下:
yum install python3-pip
这里也提示安装过了。
这里安装的是Python3,命令如下:
yum install python3
我这里已经安装过了,所以提示已安装。
继续安装 binlog2sql ,命令如下:
pip install -r requirements.txt
但是使用时报错
-bash: pip: command not found
可能是命令不叫 pip ,查一下 pip ,命令如下:
whereis pip
前往 /usr/bin 目录看看:
cd /usr/bin
确实不叫 pip ,那用 pip-3.6 试试,对应命令为:
pip-3.6 --version
或者 pip3.6 ,对应命令为:
pip3.6 --version
可以看到版本号,那应该就是这样用了。
前面已经安装好了git好pip,这里安装 binlog2sql ,选好自己的安装目录,我是安装在 /usr/local 目录,安装命令如下:
git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql
官方的命令为:
pip install -r requirements.txt
在一、2里面已经提到我的pip命令为 pip-3.6 或者 pip3.6 ,这里用 pip-3.6,所以命令如下:
pip-3.6 install -r requirements.txt
进入MySQL的配置文件,命令如下:
vim /etc/my.cnf
在MySQL的配置文件 [mysqld] 中加入官方给出的配置,配置如下:
server_id = 1
log_bin = /var/log/mysql/mysql-bin.log
max_binlog_size = 1G
binlog_format = row
binlog_row_image = full
我将 log_bin 改为 /opt/jeecg-boot/log/mysql/mysql-bin.log ,这是我的:
# binlog2sql需要
server_id = 1
log_bin = /opt/jeecg-boot/log/mysql/mysql-bin.index
max_binlog_size = 1G
binlog_format = row
binlog_row_image = full
加入之后保存退出,重启MySQL:
systemctl restart mysql
报错:
Job for mysql.service failed because the control process exited with error code.
See "systemctl status mysql.service" and "journalctl -xe" for details.
按照提示输入命令:
systemctl status mysql.service
报错日志在 /usr/local/mysql/mysql-8.0.35/data 目录下名为 iZf8z8qpzl0oqs4a6mc897Z 的文件中,打开查看:
说找不到 /opt/jeecg-boot/log/mysql/mysql-bin.index 文件,难道这个文件不能自己创建的?好吧,我到 /opt/jeecg-boot/log/mysql/ 这个目录手动创建一下,再次重启:
systemctl restart mysql
还是报错,打卡日志文件一看,跟前面的错一模一样,会不会是没有读写权限?那就给这个目录读写权限,命令如下:
chmod -R 777 /opt/jeecg-boot/log/mysql
再次重启
systemctl restart mysql
成功!!
这时再想:现在有了读写权限了,把文件删除了再次重启能不能启动成功?
试试吧,就把文件删除了
再次重启
看来是能自己创建的,不过就是不知道为什么没有 mysql-bin.log 文件,明明在 log_bin 上指定的文件名为 mysql-bin.log 。
官方文档中提到:
user需要的最小权限集合:select, super/replication client, replication slave
建议授权:GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON . TO
权限说明:
select:需要读取server端information_schema.COLUMNS表,获取表结构的元信息,拼接成可视化的sql语句
super/replication client:两个权限都可以,需要执行’SHOW MASTER STATUS’, 获取server端的binlog列表
replication slave:通过BINLOG_DUMP协议获取binlog内容的权限
这里创建一个名为 binlog2sql 的用户。
登录MySQL:
然后创建用户:
create user 'binlog2sql'@'%' identified by '123456';
注意这里
如果是云服务器就不要使用 127.0.0.1 或者 localhost ,我试了一下新创建的用户会登录不了,用 % 则可以登录成功。
假如创建用户的过程中报错:
ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.
这其实是因为之前设置过 wait_timeout 和 interactive_timeout ,之前设置为 120 秒,超过了这个时间就报错了
但虽然报这个错,但用户还是创建成功的,查看一下用户表,命令如下:
select Host, User from user;
可以适当增大 wait_timeout 和 interactive_timeout 参数的值,或者做一次查询唤醒连接,再次操作数据库就不会报这个错了。
假如创建用户报错:
ERROR 1396 (HY000): Operation CREATE USER failed for 'binlog2sql'@'127.0.0.1'
可能之前已经创建过同名用户,查询一下用户表,先切换到 mysql 数据库
use mysql;
再查询用户表
select Host, User from user;
可以看到确实已经创建了名为 binlog2sql 的用户,如果已经确定 binlog2sql 用户是之前误创的,可以先删除
drop user binlog2sql@'127.0.0.1';
再创建
create user 'binlog2sql'@'%' identified by '123456';
执行如下sql:
grant select, insert, update on *.* to 'binlog2sql'@'%';
解释:
.:“.”前面代表数据库名,“.”后面代表表名,“.”代表所有数据库的所有表。
授权之后再刷新一下权限,命令如下:
flush privileges;
然后使用新创建的用户 binlog2sql 试试能不能登录
可以登录!!
补充一下,登录成功可以不看。
假如使用 binlog2sql 用户登录报错
这可能是创建用户时 @ 后使用的是 127.0.0.1 或者 localhost ,并且是本地连接远程的服务器。
解决办法:删了 binlog2sql 用户,在创建用户时 @ 后使用 % 重新创建即可。
这里多啰嗦一下查看日志的过程,查询日志是否打开,命令如下:
show variables like 'log_bin';
查询当前MySQL服务器的二进制日志格式,命令如下:
show variables like 'binlog_format';
binlog_format:
是MySQL二进制日志的一个系统变量,它定义了二进制日志的格式。
binlog_format 可以设置为如下值:
STATEMENT:
在这个模式下,二进制日志记录的是SQL语句本身,而不是数据的更改。这种格式是最简单的,但可能存在一些限制和问题,比如对于同一个表的多个更新操作,可能会产生重复的日志记录。
ROW:
在这个模式下,二进制日志记录的是每一行数据的变化。这种格式提供了更高的精度和灵活性,但会产生更多的日志数据。
MIXED:
在这个模式下,MySQL会根据执行的SQL语句的类型和参数,选择使用STATEMENT或ROW格式来记录二进制日志。这种格式旨在平衡精度和性能。
这里先做个测试,例如:我在 2023-12-08 16:48:00 至 2023-12-08 16:49:00 的时间范围内误删了数据表 sys_user_depyq 中的所有数据。
注意!误操作的时间很重要,越精确越好,因为生成回滚sql比较慢,如果时间间隔比较长的话,生成sql的时间也会很长,我这里就删除了两条数据,执行命令不能立刻得到误操作的sql。
现在要恢复一下,先查询当前MySQL服务器的主日志文件信息,sql如下:
show master status;
这里提一嘴,不需要关注日志文件 mysql-bin.000002 的所在目录,只要知道查出来叫什么名就好了,在解析sql时只需要文件名,不需要知道绝对路径。
先前往 binlog2sql 安装的根目录,就是 binlog2sql.py 文件所在目录,这里安装在 /usr/local/binlog2sql/binlog2sql 目录,查看删除sql,命令如下:
python ./binlog2sql.py -h127.0.0.1 -P3306 -ubinlog2sql -p123456 -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-datetime="2023-12-08 16:48:00" --stop-datetime="2023-12-08 16:49:00"
记得把IP地址、用户名、密码改为自己的。生成执行sql会比较慢,耐心等待一下。
也可以用 binlog2sql.py 文件的绝对路径,就不用跑到 binlog2sql 根目录了,命令如下:
python /usr/local/binlog2sql/binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -ubinlog2sql -p123456 -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-datetime="2023-12-08 16:48:00" --stop-datetime="2023-12-08 16:49:00"
然后是生成回滚sql,就是在前面命令的基础上加上 --flashback 参数,命令如下:
python /usr/local/binlog2sql/binlog2sql/binlog2sql.py --flashback -h47.120.44.227 -P3306 -ubinlog2sql -pZnfGVcz6rCNT -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-datetime="2023-12-08 16:48:00" --stop-datetime="2023-12-08 16:49:00"
假如生成的回滚sql有很多,可以以写入到文件中,这里写入到根目录的 flashback.sql 文件中,命令如下:
python /usr/local/binlog2sql/binlog2sql/binlog2sql.py --flashback -h127.0.0.1 -P3306 -ubinlog2sql -p123456 -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-datetime="2023-12-08 16:48:00" --stop-datetime="2023-12-08 16:49:00" > /flashback.sql
如果误操作的时间记得比较泛,可以将日志文件导出到sql,再放到本地查找,我这里只是演示,还是使用前面的开始和结束时间,将日志文件导出到根目录的 test.sql 文件中,使用如下命令:
mysqlbinlog --no-defaults --database=yungong --start-datetime="2023-12-08 16:48:00" --stop-datetime="2023-12-08 16:49:00" /opt/jeecg-boot/log/mysql/mysql-bin.000002 > /test.sql
然后查找被删除的表,拿到两个删除表日志前后的 position ,就是表前后 ANONYMOUS 之间的 position ,如下图所示:
就是把前面的 start-datetime 、stop-datetime 分别改为 start-position 、stop-position,然后使用开始和结束的 position 执行 binlog2sql 命令,命令如下:
python /usr/local/binlog2sql/binlog2sql/binlog2sql.py -h47.120.44.227 -P3306 -ubinlog2sql -pZnfGVcz6rCNT -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-position=4964386 --stop-position=4964802
可以看到也能拿到误删除的sql。
然后是执行得到回滚的sql,命令如下:
python /usr/local/binlog2sql/binlog2sql/binlog2sql.py --flashback -h47.120.44.227 -P3306 -ubinlog2sql -pZnfGVcz6rCNT -dyungong -t sys_user_depyq --start-file='mysql-bin.000002' --start-position=4964386 --stop-position=4964802
在一次误操作线上的数据库 yungong ,在更新 ygj_task_ulist 表时没有加上 where 条件,导致全表更新,误操作的时间是 2023-12-05 10:29:28.468 ,那就选定开始时间为 2023-12-05 10:27:00,结束时间为:2023-12-05 10:30:00 。这里记录一下解析日志时报的错。
假如使用时报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 149, in <module>
back_interval=args.back_interval, only_dml=args.only_dml, sql_type=args.sql_type)
File "./binlog2sql.py", line 46, in __init__
self.connection = pymysql.connect(**self.conn_setting)
File "/usr/local/lib/python3.6/site-packages/pymysql/__init__.py", line 90, in Connect
return Connection(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 706, in __init__
self.connect()
File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 931, in connect
self._get_server_information()
File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 1269, in _get_server_information
self.server_charset = charset_by_id(lang).name
File "/usr/local/lib/python3.6/site-packages/pymysql/charset.py", line 38, in by_id
return self._by_id[id]
KeyError: 255
原因:
该问题主要原因是MySQL8.0更新了很多字符集,但是这些字符集长度超过255了,所以旧版的PyMySQL不支持长度超过255的字符。
解决办法就是更新 pymysql ,前面已经提到我的pip命令为 pip-3.6 或者 pip3.6 ,记得把pip换成自己的,这是我的命令:
pip-3.6 install --upgrade PyMySQL
如果使用时报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 7, in <module>
from pymysqlreplication import BinLogStreamReader
File "/usr/local/lib/python3.6/site-packages/pymysqlreplication/__init__.py", line 23, in <module>
from .binlogstream import BinLogStreamReader
File "/usr/local/lib/python3.6/site-packages/pymysqlreplication/binlogstream.py", line 8, in <module>
from pymysql.util import int2byte
ModuleNotFoundError: No module named 'pymysql.util'
先卸载 pymysql ,前面已经提到我的pip命令为 pip-3.6 或者 pip3.6 ,这里根据自己的情况修改,命令如下:
pip-3.6 uninstall pymysql
重新下载 pymysql ,也有说是MySQL8的版本问题,安装 binlog2sql 需要的 PyMySQL 版本,可以到 requirements.txt 文件中查看
可以看到 binlog2sql 要求 PyMySQL 版本为 0.7.11 ,我测试过下载 0.9.3 版本也可以满足,这里下载了 0.9.3 版本,我的命令如下:
pip-3.6 install pymysql==0.9.3
这时如果还报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 149, in <module>
back_interval=args.back_interval, only_dml=args.only_dml, sql_type=args.sql_type)
File "./binlog2sql.py", line 48, in __init__
cursor.execute("SHOW MASTER STATUS")
File "/usr/local/lib64/python3.6/site-packages/pymysql/cursors.py", line 170, in execute
result = self._query(query)
File "/usr/local/lib64/python3.6/site-packages/pymysql/cursors.py", line 328, in _query
conn.query(q)
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 517, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 732, in _read_query_result
result.read()
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 1075, in read
first_packet = self.connection._read_packet()
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 684, in _read_packet
packet.check_error()
File "/usr/local/lib64/python3.6/site-packages/pymysql/protocol.py", line 220, in check_error
err.raise_mysql_exception(self._data)
File "/usr/local/lib64/python3.6/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.InternalError: (1227, 'Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation')
这是没有 super 权限,使用 root 登录
mysql -uroot -p
给用户 binlog2sql 授权一下
grant super on *.* to 'binlog2sql'@'%';
然后再刷新一下权限
flush privileges;
再往下走,如果报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 149, in <module>
back_interval=args.back_interval, only_dml=args.only_dml, sql_type=args.sql_type)
File "./binlog2sql.py", line 53, in __init__
raise ValueError('parameter error: start_file %s not in mysql server' % self.start_file)
ValueError: parameter error: start_file binlog.000019 not in mysql server
由于个人原因,在走到这一步时 binlog.000019 已经不是在生成当天了,当天生成的日志名为 mysql-bin.000002
show master status;
解决办法:只需要把文件名 binlog.000019 改为 mysql-bin.000002 再重新执行就可以了,为了出现意外,最好将原 mysql-bin.000002 文件先备份一下。
假如报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 150, in <module>
binlog2sql.process_binlog()
File "./binlog2sql.py", line 74, in process_binlog
for binlog_event in stream:
File "/usr/local/lib/python3.6/site-packages/pymysqlreplication/binlogstream.py", line 408, in fetchone
pkt = self._stream_connection._read_packet()
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 684, in _read_packet
packet.check_error()
File "/usr/local/lib64/python3.6/site-packages/pymysql/protocol.py", line 220, in check_error
err.raise_mysql_exception(self._data)
File "/usr/local/lib64/python3.6/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.InternalError: (1227, 'Access denied; you need (at least one of) the REPLICATION SLAVE privilege(s) for this operation')
看报错提示是因为没有 REPLICATION SLAVE 权限,使用用户 root 登录MySQL查看用户 binlog2sql 的权限
show grants for 'binlog2sql'@'%';
可以看到确实没有 REPLICATION SLAVE 权限,那就给用户 binlog2sql 授权
grant replication slave on *.* to 'binlog2sql'@'%';
再刷新一下
flush privileges;
继续,假如报错:
Traceback (most recent call last):
File "./binlog2sql.py", line 150, in <module>
binlog2sql.process_binlog()
File "./binlog2sql.py", line 74, in process_binlog
for binlog_event in stream:
File "/usr/local/lib/python3.6/site-packages/pymysqlreplication/binlogstream.py", line 408, in fetchone
pkt = self._stream_connection._read_packet()
File "/usr/local/lib64/python3.6/site-packages/pymysql/connections.py", line 684, in _read_packet
packet.check_error()
File "/usr/local/lib64/python3.6/site-packages/pymysql/protocol.py", line 220, in check_error
err.raise_mysql_exception(self._data)
File "/usr/local/lib64/python3.6/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.InternalError: (1236, 'Could not open log file')
可能是因为改名后的 mysql-bin.000002 文件没有读写权限,授权一下
chmod -R 777 /opt/jeecg-boot/log/mysql/mysql-bin.000002
之后再执行 binlog2sql 的命令应该就没问题了。