MySQL 5.7贴心参数之binlog_row_image

MySQL 5.7贴心参数之binlog_row_image

相信大家都了解mysql binlog的格式,那就是有三种,分别是STATEMENT,MiXED,ROW。各有优劣,具体的请大家自行查阅资料。在MySQL 5.7版本以前,虽然ROW格式有各种各样的好处。

1. 比如加快从库重放日志;ROW直接调用mysql的存储引擎接口(handler API) 来执行行的插入、删除和更新,完全跳过了mysql的优化器的处理逻辑。

2. 保证主从数据的一致性。记录的每一行的变更。

3. 可以通过对binlog的逆向解析实现闪回功能。

那么实际上还是有一个缺陷,那就是由于记录的是每一行的变更,会带来磁盘IO上的开销,同时由于binlog日志变大,网络开销也变大了。那么在MySQL 5.7以后binlog的格式默认就是ROW了,同时引入了新的参数binlog_row_image,这个参数默认值是FULL,其还有一个值是minimal。由于5.7版本的其他功能都有人提到了,这个没人提到,我也简单的介绍一下,哈哈。(后面发现5.6就有该参数了,关注度不够,该死。。)

FULL记录每一行的变更,minimal只记录影响后的行。下面简单的测试一下大家就明白了。

复制代码
mysql> show variables like '%row_im%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| binlog_row_image | FULL  |
+------------------+-------+
1 row in set (0.01 sec)

mysql> update t2 set uid=99 where name='yayundeng';
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> 
复制代码

解析binlog看看啥情况

复制代码
[root@yayundeng 3306]# mysqlbinlog --no-defaults -v -v --base64-output=DECODE-ROWS mysql-bin.000010 | tail -n 20
#161210 11:02:32 server id 152  end_log_pos 2043 CRC32 0x3ce8a225       Update_rows: table id 110 flags: STMT_END_F
### UPDATE `test`.`t2`
### WHERE
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2='gz' /* STRING(20) meta=65044 nullable=1 is_null=0 */
###   @3='yayundeng' /* STRING(20) meta=65044 nullable=1 is_null=0 */
###   @4=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2='gz' /* STRING(20) meta=65044 nullable=1 is_null=0 */
###   @3='yayundeng' /* STRING(20) meta=65044 nullable=1 is_null=0 */
###   @4=99 /* INT meta=0 nullable=1 is_null=0 */
# at 2043
#161210 11:02:32 server id 152  end_log_pos 2074 CRC32 0x93619126       Xid = 49
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
[root@yayundeng 3306]# 
复制代码

下面设置一下binlog_row_image的值为minimal

复制代码
mysql> set  binlog_row_image ='minimal';
Query OK, 0 rows affected (0.00 sec)

mysql> update t2 set uid=100 where name='yayundeng';  
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> 
复制代码

再解析binlog看看啥情况:

复制代码
[root@yayundeng 3306]# mysqlbinlog --no-defaults -v -v --base64-output=DECODE-ROWS mysql-bin.000010 | tail -n 20
SET TIMESTAMP=1481339144/*!*/;
BEGIN
/*!*/;
# at 2211
#161210 11:05:44 server id 152  end_log_pos 2263 CRC32 0x57948074       Table_map: `test`.`t2` mapped to number 110
# at 2263
#161210 11:05:44 server id 152  end_log_pos 2309 CRC32 0x72114b1f       Update_rows: table id 110 flags: STMT_END_F
### UPDATE `test`.`t2`
### WHERE
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
### SET
###   @4=100 /* INT meta=0 nullable=1 is_null=0 */
# at 2309
#161210 11:05:44 server id 152  end_log_pos 2340 CRC32 0x23e45a71       Xid = 52
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
[root@yayundeng 3306]# 
复制代码

可以清楚的看到,当把binlog_row_image设置为minimal以后,binlog记录的就只是影响后的行,大赞。如此一来使用ROW格式一定是首选了,完全没必要使用STATEMENT,MiXED。不过现在也没有人使用STATEMENT了,所以准确的说就是完全没必要使用MiXED格式了。

 

总结:

MySQL 5.7是真正的一个里程碑的版本,比如可以在线调整bp pool,在线修改复制过滤,真正意义上多线程复制(5.6是基于库,5.7是基于表),支持bp pool预热,支持undo log回收,支持通用表空间,支持json,支持函数索引(通过虚拟列实现),支持只读事务等等等。

<think>我们正在解决Debezium因MySQLbinlog_format不是ROW而抛出的异常。用户的问题集中在如何确保MySQL的二进制日志使用ROW格式,以避免Debezium连接器失败。 根据之前的讨论,我们已经知道: 1. 全局设置`binlog_format=ROW`可能被会话级设置覆盖。 2. 某些表引擎(如MyISAM)或特定操作可能导致临时使用非ROW格式。 3. DDL语句总是以STATEMENT格式记录,但这不是问题,因为Debezium可以处理DDL。问题在于DML事件(INSERT/UPDATE/DELETE)以非ROW格式记录。 现在,我们需要提供一套完整的解决方案,确保所有DML事件都以ROW格式记录,从而让Debezium正常工作。 步骤: 1. **确认MySQL的全局binlog设置**: 在MySQL服务器上执行: ```sql SHOW GLOBAL VARIABLES LIKE 'binlog_format'; ``` 确保结果为`ROW`。 2. **检查是否有会话覆盖全局设置**: 使用以下查询检查当前会话中是否有设置`binlog_format`为`STATEMENT`或`MIXED`的: ```sql SELECT * FROM performance_schema.session_variables WHERE VARIABLE_NAME = 'binlog_format' AND VARIABLE_VALUE != 'ROW'; ``` 同时,检查正在运行的会话中是否有执行过设置会话级`binlog_format`的: ```sql SELECT * FROM performance_schema.variables_by_thread WHERE VARIABLE_NAME = 'binlog_format' AND VARIABLE_VALUE != 'ROW'; ``` 3. **确保配置文件中永久设置**: 在MySQL配置文件(如`my.cnf`或`my.ini`)的`[mysqld]`部分添加: ```ini binlog_format = ROW binlog_row_image = FULL # Debezium要求 ``` 4. **重启MySQL服务**: 修改配置文件后,重启MySQL使设置生效。 5. **检查表引擎**: 将所有非InnoDB引擎的表(特别是MyISAM)转换为InnoDB,因为MyISAM表在某些操作中可能触发使用STATEMENT格式。 ```sql -- 查看表引擎 SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database' AND ENGINE != 'InnoDB'; -- 转换表引擎 ALTER TABLE table_name ENGINE=InnoDB; ``` 6. **防止会话级覆盖**: 在MySQL 5.7及以上版本,可以通过设置`binlog_format`为只读系统变量来防止会话级修改: ```sql SET GLOBAL binlog_format = 'ROW'; SET GLOBAL read_only = OFF; -- 确保不是只读实例 -- 注意:MySQL没有直接禁止会话级修改的方法,但可以通过配置文件和重启来强制。 ``` 7. **验证二进制日志格式**: 使用`mysqlbinlog`工具检查二进制日志中的事件格式: ```bash mysqlbinlog --base64-output=decode-rows -v binlog.000001 | grep -C 10 'your_table' ``` 在输出中,DML事件应该以行事件的形式显示(如`### INSERT INTO ...`),而不是完整的SQL语句。 8. **处理Debezium连接器中断**: 在修复MySQL设置后,需要重新配置Debezium连接器以从正确的位置开始读取二进制日志。可以设置`snapshot.mode`为`schema_only_recovery`来恢复: ```json { "name": "your-connector", "config": { "connector.class": "io.debezium.connector.mysql.MySqlConnector", "database.hostname": "...", "database.user": "...", "database.password": "...", "database.server.id": "...", "database.server.name": "...", "database.include.list": "...", "table.include.list": "...", "snapshot.mode": "schema_only_recovery", "database.history.skip.unparseable.ddl": true // ... 其他配置 } } ``` 9. **监控和预防**: 设置监控,定期检查MySQL的二进制日志格式,确保没有会话修改它。可以定期运行: ```sql SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.session_variables WHERE VARIABLE_NAME = 'binlog_format' AND VARIABLE_VALUE != 'ROW'; ``` 10. **升级MySQL版本**: 如果使用的是旧版本MySQL(如5.6),考虑升级到5.7或8.0,因为旧版本可能存在已知的binlog格式退化问题。 相关问题: 1. 如何监控MySQL会话级binlog格式的实时变更? 2. Debezium如何处理ROW模式下的DDL事件? 3. MyISAM表在CDC场景下有哪些限制? 4. MySQL 8.0如何彻底禁止会话级`binlog_format`修改? 5. 如何从Debezium同步中断的位点恢复数据? 注意:在回答中,我们引用了之前讨论的一些原因和解决方案,并进行了扩展。</think>### 解决 Debezium "binlog contains statement/mixed replication format" 异常 #### 问题根源分析 1. **DML 事件非 ROW 格式** Debezium 仅支持 ROW 格式的 binlog 解析,当检测到 `INSERT/UPDATE/DELETE` 以 STATEMENT 格式记录时,会抛出异常[^1]。 $$ \text{Debezium 兼容性} = \begin{cases} \text{DDL} & \rightarrow \text{STATEMENT 可处理} \\ \text{DML} & \rightarrow \text{必须 ROW 格式} \end{cases} $$ 2. **MySQL 配置失效场景** | 场景 | 触发条件 | 影响 | |---|---|---| | 会话级覆盖 | `SET SESSION binlog_format=MIXED` | 该会话所有 DML 使用 STATEMENT | | MyISAM 表 | `INSERT...SELECT` 操作 | 强制使用 STATEMENT 格式 | | MySQL 5.6 Bug | 涉及临时表/存储过程 | ROW 模式退化[^2] | 3. **混合格式日志特征** 在 ROW 模式下,DDL 仍以 STATEMENT 格式记录,但 DML 必须是 ROW 格式。异常表明 **DML 事件被错误记录**。 --- #### 完整解决方案 ##### 步骤 1:强制 MySQL 使用 ROW 格式 ```sql -- 永久配置 (my.cnf) [mysqld] binlog_format = ROW binlog_row_image = FULL -- Debezium 必需 log_bin_trust_function_creators = ON -- 动态生效 SET GLOBAL binlog_format = 'ROW'; SET GLOBAL enforce_gtid_consistency = ON; -- 确保 GTID 一致性 ``` ##### 步骤 2:终止异常会话 ```sql -- 查找修改 binlog_format 的会话 SELECT id, user, host, db, state, INFO FROM information_schema.processlist WHERE INFO LIKE '%binlog_format%' OR INFO LIKE '%tx_isolation%'; -- 终止危险会话 KILL <session_id>; ``` ##### 步骤 3:转换表引擎 ```sql -- 检查非 InnoDB 表 SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND ENGINE != 'InnoDB'; -- 转换为 InnoDB ALTER TABLE problem_table ENGINE=InnoDB; ``` ##### 步骤 4:验证 binlog 格式 ```bash mysqlbinlog --base64-output=decode-rows -v binlog.00000X | grep -A 15 'UMAP_RECORD_LOG' ``` - **正常 ROW 格式**:显示行级变更细节(如 `### INSERT...`) - **异常 STATEMENT 格式**:显示完整 SQL 语句 ##### 步骤 5:修复 Debezium 连接器 ```json { "name": "mysql-connector", "config": { "connector.class": "io.debezium.connector.mysql.MySqlConnector", "snapshot.mode": "schema_only_recovery", // 仅恢复 schema "database.history.skip.unparseable.ddl": true, // 跳过异常 DDL "binlog.buffer.size": "64MB" // 增大缓冲区 } } ``` ##### 步骤 6:监控防护 ```sql -- 创建审计规则 CREATE TABLE binlog_audit ( event_time TIMESTAMP, user_host VARCHAR(255), command VARCHAR(20) ); -- 启用日志审计插件 INSTALL PLUGIN audit_log SONAME 'audit_log.so'; SET GLOBAL audit_log_format = 'JSON'; SET GLOBAL audit_log_policy = 'ALL'; ``` --- #### 关键技术原理 1. **Debezium 的 DML 解析限制** - ROW 格式 binlog 包含**行前镜像/后镜像**,使 Debezium 能精确重建变更事件 - STATEMENT 格式仅记录 SQL 语句,无法获取变更的具体行数据[^3] $$ \text{解析能力} = \frac{\text{行变更细节}}{\text{SQL 语句}} \rightarrow \begin{cases} =1 & \text{(ROW 格式)} \\ =0 & \text{(STATEMENT 格式)} \end{cases} $$ 2. **MySQL 的格式切换机制** - `binlog_format` 是动态变量,会话可覆盖全局设置 - 涉及非事务表时,MySQL 自动切换格式以保证数据一致性 --- #### 验证与测试 1. **模拟异常触发** ```sql -- 在会话中执行 SET SESSION binlog_format = 'STATEMENT'; INSERT INTO UMAP_RECORD_LOG (...) VALUES (...); -- 触发 Debezium 异常 ``` 2. **修复后验证** ```bash # 检查 Debezium 日志 grep "RowData" debezium.log | grep "UMAP_RECORD_LOG" # 应输出行变更数据: source: {... "row": {"id": 123 ...}} ``` --- #### 相关问题 1. 如何实时监控 MySQL 会话级 binlog 格式变更? 2. Debezium 如何处理 ROW 模式下的 DDL 事件? 3. MyISAM 表在 CDC 场景下有哪些具体限制? 4. MySQL 8.0 如何彻底禁止会话级 binlog_format 修改? 5. Debezium 连接器中断后如何精确恢复位点? [^1]: Debezium 官方文档要求 DML 必须为 ROW 格式 [^2]: MySQL 5.6 存在 binlog 格式退化 Bug(Bug #83713) [^3]: STATEMENT 格式无法提供行级变更细节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值