MySQL 闪回技术总结

云掣YunChe5个月前技术文章303

一、闪回技术汇总


1. 第一类为以 patch 形式集成到官方工具 mysqlbinlog 中,阿里彭立勋 2012 年曾提交过相关工具;

2. 第二类是独立工具,通过伪装 Slave 拉取 Binlog 来进行处理,以美团的 binlog2sql 为代表; 

  ○ 优点:兼容性好、安装部署简单、源码难度不大可以进行改造;

  ○ 缺点:需要连接数据库;

3. 第三类简单脚本,使用 mysqlbinlog 生成文本然后根据回滚原理及正则进行匹配和替换; 

  ○ 优点:支持离线解析、脚本写起来简单;

  ○ 缺点:通用性及兼容性不好;


二、binlog 初探


●  binlog 介绍:二进制日志为 MySQL 记录数据库变化的日志,主要应用于主从复制和配合其它工具实现数据恢复。binlog 文件分为两类,其中一类为 mysql-bin.00000n 里面记录的是 binlog 事件,另一类为 mysql-bin.index 为 MySQL 二进制日志的索引文件,负责跟踪服务器上所有的 binlog 文件以便必要时可以正确的创建新的 binlog 文件。 

●  binlog 组成单元:每一个 binlog 文件由若干个 binlog 事件组成,以 Format_description(格式描述事件)作为文件头,以 Rotate(日志轮换事件)作为文件尾。除了控制事件,binlog 中其它事件被分为组,在事务存储引擎中,每个事务就是一个组,但是对于非事物存储引擎每条 SQL 语句就是一个组。 

●  binlog 事件的组成单元: 

  a. 通用头(Common Header):记录事件的基本信息,事件类型和大小等;

  b. 提交头(Post Header):提交头与特定的事件类型有关,不同的事件类型,该字段存储的信息不同;

  c. 事件体(Event Body):存储事件的主要信息;

  d. 校验和(Checksum):从 5.6 版本开始新增校验参数,如果服务器设置产生校验的话,事件末位会产生校验字段,类型为 32 位整数,用于检查事件写入后是否有损坏。

●  binlog 写入:由于 binlog 属于公共资源,大部分会话线程都需要写二进制日志,所以需要避免两个线程同时更新二进制日志,所以在写入二进制日志之前都会获得一个互斥锁 LOCK_log 写完事件后释放。由于服务器所有的会话线程都对应的是一个 binlog 所以 LOCK_log 常常会堵塞某些会话线程。 


三、“篡改” binlog 实现闪回


●  原理:MySQL binlog 中使用二进制数字来标记 binlog 的事件,例如一条 DELETE 语句会使用 30 来标记写入事件,使用 32 来标记为删除事件,我们只需要找到对应的 position 然后修改其事件类型即可完成闪回; 

●  binlog 事件引入:首先为们可以看到 Log_name 为 binlog 事件写入的物理文件名,Pos 为事件开始的位置 End_log_pos 为事件结束的位置,使用 End_log_pos 减去 Pos 就可以知道这个事件占有的字节大小 Event_type 为事件类型 Server_id 为该事件的服务器 MySQL Server ID Info 该事件的描述信息。 

Log_name Pos Event_type Server_id End_log_pos Info

mysql-bin.000001 4 Format_desc 33061 123 Server ver: 5.7.32-log, Binlog ver: 4

mysql-bin.000001 123 Previous_gtids 33061 154

mysql-bin.000001 154 Gtid 33061 219 SET @@SESSION.GTID_NEXT= '7d8ce50b-2e3c-11eb-b893-000c29adbb20:1'

mysql-bin.000001 219 Query 33061 293 BEGIN

mysql-bin.000001 293 Table_map 33061 355 table_id: 238 (school.student)

mysql-bin.000001 355 Delete_rows 33061 410 table_id: 238 flags: STMT_END_F

mysql-bin.000001 410 Xid 33061 441 COMMIT /* xid=4363 */

●  

●  binlog 常见事件类型(详细请见 MySQL Event Meanings): 

事件名 含义

Format_desc 格式描述事件,属于系统控制事件

Query 语句编写

Table_map 记录即将要修改表的结构

Xid 事务 commit 时候写入的事物 ID

Delete_rows 表示为一个删除事件

●  

●  MySQL 内部如何标记 binlog 事件类型(详细请见 MySQL Event Classes and Types)下面的 C++ 代码片段为 MySQL 官方文档中给出的事件类型代码。我们可以看到 WRITE_ROWS_EVENT 事件在源码中使用 30 来标记,也就是说在 binlog 事件的 Event_type 中使用二进制 30 来标记事件为一个写入事件,同理使用 32 来标记一个 DELETE_ROWS_EVENT 写入事件。 

enum Log_event_type { 

  UNKNOWN_EVENT= 0, 

  START_EVENT_V3= 1, 

  QUERY_EVENT= 2, 

  STOP_EVENT= 3, 

  ROTATE_EVENT= 4, 

  INTVAR_EVENT= 5, 

  LOAD_EVENT= 6, 

  SLAVE_EVENT= 7, 

  CREATE_FILE_EVENT= 8, 

  APPEND_BLOCK_EVENT= 9, 

  EXEC_LOAD_EVENT= 10, 

  DELETE_FILE_EVENT= 11, 

  NEW_LOAD_EVENT= 12, 

  RAND_EVENT= 13, 

  USER_VAR_EVENT= 14, 

  FORMAT_DESCRIPTION_EVENT= 15, 

  XID_EVENT= 16, 

  BEGIN_LOAD_QUERY_EVENT= 17, 

  EXECUTE_LOAD_QUERY_EVENT= 18, 

  TABLE_MAP_EVENT = 19, 

  PRE_GA_WRITE_ROWS_EVENT = 20, 

  PRE_GA_UPDATE_ROWS_EVENT = 21, 

  PRE_GA_DELETE_ROWS_EVENT = 22, 

  WRITE_ROWS_EVENT = 23, 

  UPDATE_ROWS_EVENT = 24, 

  DELETE_ROWS_EVENT = 25, 

  INCIDENT_EVENT= 26, 

  HEARTBEAT_LOG_EVENT= 27, 

  IGNORABLE_LOG_EVENT= 28,

  ROWS_QUERY_LOG_EVENT= 29,

  WRITE_ROWS_EVENT = 30,

  UPDATE_ROWS_EVENT = 31,

  DELETE_ROWS_EVENT = 32,

  GTID_LOG_EVENT= 33,

  ANONYMOUS_GTID_LOG_EVENT= 34,

  PREVIOUS_GTIDS_LOG_EVENT= 35, 

  ENUM_END_EVENT 

  /* end marker */ 

};

 

●  下面为我们使用 mysqlbinlog --hexdump 转换为十六进制的 binlog 可以看到 event_type 为 1e 转换为十进制为 1e(十六进制) = 30(十进制) 表明该事件为写入事件。 

# at 355

#201209 17:01:52 server id 33061  end_log_pos 410 CRC32 0x31c97555

# Position  Timestamp   Type   Master ID        Size      Master Pos    Flags

#      163 80 92 d0 5f   1e   25 81 00 00   37 00 00 00   9a 01 00 00   00 00

#      176 ee 00 00 00 00 00 01 00  02 00 04 ff f0 02 30 33 |..............03|

#      186 06 e5 ad 99 e9 a3 8e 99  46 a8 00 00 03 e7 94 b7 |........F.......|

#      196 55 75 c9 31                                      |Uu.1|

# Write_rows: table id 238 flags: STMT_END_F


BINLOG '

gJLQXxMlgQAAPgAAAGMBAAAAAO4AAAAAAAEABnNjaG9vbAAHc3R1ZGVudAAEDw8SDwcoACgAACgA

DxKVlvY=

gJLQXx4lgQAANwAAAJoBAAAAAO4AAAAAAAEAAgAE//ACMDMG5a2Z6aOOmUaoAAAD55S3VXXJMQ==

'/*!*/;

 

●  MySQL 内部如何来存储事件(详细请见 MySQL Event Header Fields)下面是 MySQL V4 版本的 binlog 事件头的文件偏移量,从中我们可以了解到在一个 binlog 事件中 type_code 存储在第 5 个字节,也就是说上面 type_event=30 存储在该事件里的第 5 个字节 

Event_type Position

timestamp 0 : 4

type_code 4 : 1

server_id 5 : 4

event_length 9 : 4

next_position 13 : 4

flags 17 : 2

extra_headers 19 : x-19

●  

●  我们已经了解 binlog 事件类型及存储结构,也就理解了如何去通过 ‘篡改’ binlog 实现闪回,接下来请看一个 闪回案例。 


三、闪回失败案例


1.  收到需要进行一次 DELETE 语句的闪回 Position 位置点相关信息都有,安装流程进行闪回: 

--start-position=551445 --stop-position=1298494

 

2.  下载对应的 binlog 文件: 

python chtype.py mysql-bin.002759 mysql-bin.002759-bak

 

3.  查询之前的 binlog 事件我们发现属于 DELETE 事件,此时我们已经将 bak 修改为写入事件: 

# at 551445

#201208 10:27:20 server id 388932253  end_log_pos 551514 CRC32 0xeeee42a2 Table_map: `eolinker_os`.`eo_api_cache` mapped to number 940

# at 551514

#201208 10:27:20 server id 388932253  end_log_pos 1298494 CRC32 0x66cb2d37 Delete_rows: table id 940 flags: STMT_END_F

### DELETE FROM `eolinker_os`.`eo_api_cache`

### WHERE

###   @1=4272

###   @2=25

###   @3=922

###   @4=4275

###   @5='{"baseInfo":{"apiFailureMock"}

###   @6=0

###   @7=170

# at 1298494

 

# at 551445

#201208 10:27:20 server id 388932253  end_log_pos 551514 CRC32 0xeeee42a2 Table_map: `eolinker_os`.`eo_api_cache` mapped to number 940

# at 551514

#201208 10:27:20 server id 388932253  end_log_pos 1298494 CRC32 0x66cb2d37 Write_rows: table id 940 flags: STMT_END_F

### INSERT INTO `eolinker_os`.`eo_api_cache`

### SET

###   @3=1549556828

###   @4=1549556828

###   @5=NULL

###   @7=NULL

### INSERT INTO `eolinker_os`.`eo_api_cache`

### SET

###   @3=1549556828

###   @4=1549556828

###   @5=NULL

###   @7=NULL

...........................

 

4.  我们发现闪回生成的格式是有问题的,并没有达到我们的预期,我们看到了一句报错信息: 

### INSERT INTO `eolinker_os`.`eo_api_cache`

### SET

###   @3=1549556828

###   @4=NULL

###   @5=***Corrupted replication event was detected. Not printing the value***

# at 1298494


检查到事件信息损坏,不打印值,我们了解到如果改错了位置的话,那么整个 binlog 文件会损坏 mysqlbinlog 也无法解析出相关的事件信息,那么究竟是什么问题导致出现这个错误呢? 

5.  经过一阵折腾我们从 MySQL 5.7 版本中的 binlogevent 源码中找到了答案(准确来说是源码上的注释😄) 

num Log_event_type

{

  /** The V1 event numbers are used from 5.1.16 until mysql-trunk-xx **/

  WRITE_ROWS_EVENT_V1 = 23,

  UPDATE_ROWS_EVENT_V1 = 24,

  DELETE_ROWS_EVENT_V1 = 25,

  

   /** Version 2 of the Row events **/

  WRITE_ROWS_EVENT = 30,

  UPDATE_ROWS_EVENT = 31,

  DELETE_ROWS_EVENT = 32,

  }


MySQL 二进制日志的版本不同,有使用 V1 与 V2 我们使用 V2 的 event 编码,导致闪回出现异常。 

6.  总结:修改 binlog 实现闪回修改确认事件的 Positon 编号,然后通过 Position 推算出 Event_type 的位置,然后根据需求修改对应的 binlogevent 编码即可完成修改。 

  ○ 优点:绝对准确、支持离线解析、对云上数据库也很友好;

  ○ 缺点:不支持 UPDATA 闪回;


相关文章

证书过期问题之IPV6协议

证书过期问题之IPV6协议

1、首先了解情况:客户的SSL/TLS证书要过期了,所以进行了证书替换工作,但是替换之后,有一部分客户端还是提示证书已过期,如图所示:2、看这个报错是非常的清晰的,就是证书过期的问题,但是为什么同事没...

sqlserver迁移job步骤

sqlserver迁移job步骤

1)源服务器 sql server 找到sql server 代理,选中作业 2)按F7 弹出作业对象资源管理器,全选中作业 3)右击单出编写job 脚本窗口,将job 创建脚本保存到查询编辑器窗口 ...

EMR 配置 Hive on Spark

EMR 配置 Hive on Spark

Hive3 on spark 集成前置条件hadoop yarn环境正常oracle jdk 1.8版本1、spark2 下载准备https://archive.apache.org/dist/spa...

trino容器对接hudi(五)

trino容器对接hudi(五)

前提:本文是基于已经部署了trino容器的基础上进行的。冒烟测试是在trino对接ldap后并且ranger已经对接了metastore权限后,并且spark组件已经对接hudi,并且成功创建hudi...

Linux网络扫描和嗅探工具—Nmap

1、简介Nmap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包。它由Fyodor编写并维护。由于Nmap品质卓越,使用灵活,它已经是渗透测试人员必备的工具。其基本功能有三个...

A集群导入B集群中的高可用版rancher,一直处于pending状态

A集群导入B集群中的高可用版rancher,一直处于pending状态

问题现象:已知在B集群中采用helm方式部署了一个高可用版本的rancher,该rancher中已经配置导入了三套集群,并且三套集群状态在rancher控制台处均显示正常,日常可借助该rancher管...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。