MySQL 闪回技术总结

云掣YunChe2个月前技术文章191

一、闪回技术汇总


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


相关文章

MongoDB的索引(四)

九、Text Indexes示例集合> db.ttlsa_com.find() { "_id" : ObjectId("5d2f35f6c1aace30b3ce9904"), "song" :...

Yarn调度器和调度算法详解

Yarn调度器和调度算法详解

目前,Hadoop作业调度器主要有三种:FIFO、容量(Capacity Scheduler)和公平(Fair Scheduler)。Apache Hadoop3.1.3默认的资源调度器是C...

Kafka监控

1.监控健康状态为了了解 Kafka 的运作状态和性能状况需要对 Kafka 进行监控和诊断,通过Kafka提供的监控工具和插件可以诊断出 Kafka 的异常、错误、瓶颈和故障等问题并及时采取对应的措...

kafka启动失败

kafka启动失败

问题现象客户重启kafka服务,页面显示run fail        问题报错问题原因  &nbs...

mysql 事务隔离级别

mysql 事务隔离级别

一、事务隔离级别介绍多个连接开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个连接在获取数据时的准确性。事务隔离级别      MySQL隔离级别定义了事务与事务之间的隔离程度  二...

单节点Kafka部署并开启Kerberos配置

安装Kerberosserver节点安装kerberos相关软件yum install -y krb5-server krb5-workstation krb5-libs修改配置文件krb5.conf...

发表评论    

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