MySQL 闪回技术总结

云掣YunChe6个月前技术文章367

一、闪回技术汇总


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 闪回;


相关文章

Hbase 存储相关知识

1.Hbase的写流程Client 写入-> 存入MemStore,一直到MemStore 满-> Flush 成一个StoreFile,直至增长到一定阈值-> 触发Compact...

Ranger中Solr审计日志配置修改

Ranger中Solr审计日志配置修改

1、获取solr 中的rangeraudits的配置#查看其中的配置及 solrctl instancedir --list#获取配置 solrctl instancedir --get rang...

Elasticsearch8.5及Kibana8.5安装部署

Elasticsearch8.5及Kibana8.5安装部署

一、环境准备1、Centos7系统2、切换英文系统[root@master02 ~]# tail -n2 /etc/profile export LANG="en_US.UTF-8"3、下载、安...

大数据基础之HDFS入门

大数据基础之HDFS入门

一、NameNode是整个文件系统的管理节点。它维护着整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表。二、NameNode的工作特点Namenode始终在内存中保存meteda...

MySQL 8.0 新特性:Descending Indexes

MySQL 8.0 新特性:Descending Indexes

一、前言MySQL 8.0 之前的索引排序规则之前只允许 ASC 存储,创建时指定 DESC 也会被忽略,8.0 版本为我们带来了 Descending Indexes 降序索引 👏👏👏只能使用 AS...

linux下xfs文件系统类型/目录扩容

1、查看分区信息[root@172-16-121-112 ~]# fdisk -lDisk /dev/vda: 107.4 GB, 107374182400 bytes, 209715200 sect...

发表评论    

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