Mysql中事务隔离级别与binlog_format的一点理解

本文详细解析了Mysql中事务隔离级别及binlog_format的影响,通过实例介绍了如何解决因InnoDB与binlog不兼容导致的问题,以及如何通过修改binlog_format来解决事务隔离级别与binlog方式不匹配的冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    之前几年的软件开发,总是离不开Oracle,涉及的各大项目中使用的数据库也大多为Oracle,偶尔遇到Mysql也是简单的使用,一直觉得Mysql很小型也很简单,对其的理解也处于皮毛阶段,最近遇到了一些Mysql的问题,对其中的事务隔离级别,以及binlog_format方法,有了一些了解,这里简单的整理和总结一下。
    首先回顾一下那伤心的往事:涉及支付接口和用户资金,最初在支付接口回调时,需要更新用户以及资金的一些记录,需要同时对多张表进行更新,最初的代码遗漏了对事务的控制,导致了数据库偶尔出现表数据未更新的bug,但是在代码中增加完事务后,却总是伴随着这样的异常信息:
java.sql.SQLException: Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
    简单翻译一下: 事务隔离级别为 READ-COMMITTED的InnoDB引擎,在binlog方式为 STATEMENT时并不安全。
  
    开发框架使用的是开源的JFinal,最初总以为是JFinal的问题,于是立刻在服务器上去除事务的控制代码,恢复最初版本的代码,开始解决问题,经过一段时间的百度和google,在这里找到了答案 关于JFinal中的事务处理 ,了解到了Mysql低版本存在的一个bug,官方的bug反馈: http://bugs.mysql.com/bug.php?id=40360
    造成问题的原因可能是由于InnoDB与binlog不兼容导致,服务器上的mysql版本也是5.1,于是乎,采用了最简单的办法来解决这个问题,数据库升级,这里感谢我的同事,花费了一些时间将数据库版本升级到了最新的稳定版5.7,以为问题解决了,重新控制事务,在线上运行。
    好吧,你猜到了,问题更严重了,所有的支付都出现了问题,这时出现的异常信息是:
Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.
    简单翻译一下:无法写入二进制文件,因为 BINLOG_FORMAT方式为STATEMENT,存在一个或多个表使用的存储引擎是基于行的日志记录,InnoDB数据库引擎, 当事务隔离级别为READ COMMITTED 或READ UNCOMMITTED 时,只限定于binlog为Row方式。
    先来了解一下Mysql的数据库存储引擎,有很多中,不用常用的有以下两种:MyISAM和InnoDB,两者最主要的区别是:MyISAM是默认的错处引擎,不支持事务,InnoDB支持事务。
    这里有一篇详细的介绍文章: MySQL: InnoDB 还是 MyISAM?
    所谓的事务隔离级别有以下四种,高性能Mysql中有详细的说明,来自( MySQL数据库事务隔离级别(Transaction Isolation Level)):

暂无图片

暂无图片

    翻看JFinal的源码,在控制事务的时候默认的是READ_COMMITTED,数据库在更新的时候,由于默认的binlog_format为STATEMENT,所以才导致了异常的出现,问题的根源是找到了,那么应该怎么解决呢?我们先来了解一下binlog:
    BINLOG就是一个记录SQL语句的过程,和普通的LOG一样,它以二进制存储,普通的LOG以十进制存储,binlog有以下几种方式:STATEMENT、ROW、MIXED,默认的日志记录方式,即binlog_format为STATEMENT,当数据库存储引擎为InnoDB时,在使用到事务控制,并且事务隔离级别为就 READ COMMITTED 或 READ UNCOMMITTED 时,就可能出现上面的异常,三种方式的不同区别,请参阅( MySQL binlog_format (Mixed,Statement,Row) )。
    最后得出的解决方案是,修改binlog_format,即设置为ROW或MIXED,最终将数据库中的binlog设置为' MIXED',使用的依据是:

暂无图片

    具体是否能够解决当前的错误,还需要等待明天的测试,希望一切顺利吧。同样的对于binlog和事务隔离级别的测试,也可以参阅这里: mysql binlog格式与事务级别read committed的关系
    这里具体的修改可以通过修改my.ini或 my.cnf文件,也可以在sql命令模式下,使用:
#查看事务隔离级别
select @@tx_isolation;
#查看binlog方式
select @@binlog_format;
#设置session级别的BINLOG
SET SESSION binlog_format = 'MIXED' ;
#设置系统级别的binlog
SET GLOBAL binlog_format = 'ROW' ;

 转自:http://www.xx566.com/detail/177.html

<think>好的,我现在需要解决用户提到的MySQL二进制日志记录格式存储引擎不兼容的问题。用户具体提到了binlog_format设置为statement时,使用InnoDB引擎可能出现的基于行的日志错误,需要解决方案。 首先,我需要回忆一下MySQL的二进制日志格式。MySQL主要有三种binlog格式:STATEMENT、ROW和MIXED。STATEMENT格式记录的是SQL语句本身,而ROW格式记录的是每一行数据的变更。MIXED则是在某些情况下自动切换前两种格式。 接下来,用户的问题可能是在使用STATEMENT格式时,某些操作导致InnoDB需要记录为ROW格式,从而产生错误或不兼容的情况。比如,当执行某些不确定性操作(如使用NOW()或RAND())时,STATEMENT模式可能无法正确复制,此时MySQL可能会自动切换到ROW模式,但如果配置不当,可能导致错误。 根据引用中的内容,特别是引用[3]提到MySQL 8.0.22后,某些DML操作不获取读锁,这可能事务和日志记录有关。但用户的问题更可能涉及到binlog_format的设置和存储引擎的兼容性。 InnoDB作为事务型存储引擎,通常建议使用ROW格式的二进制日志,因为它能更可靠地复制数据变更,尤其是在主从复制中。STATEMENT格式可能在涉及非确定性函数或特定存储引擎特性时出现问题,导致主从不一致。 解决方案可能包括: 1. 将binlog_format设置为ROW,确保InnoDB引擎的变更被正确记录,避免不兼容问题。 2. 如果必须使用STATEMENT格式,检查是否有特定的语句导致问题,并修改这些语句以避免依赖行级别的变更。 3. 使用MIXED格式,让MySQL自动选择最合适的日志格式,平衡可靠性和日志大小。 此外,用户可能需要检查MySQL的版本,因为不同版本在处理日志格式时的行为可能不同。比如,在较新版本中,某些问题可能已被修复或优化。 还需要参考引用中的错误处理方式,如引用[1]提到警告信息会被记录到错误日志,用户可以通过SHOW WARNINGS或查看日志获取具体错误信息,从而更准确地定位问题。 总结步骤应该是:确认当前binlog_format设置,检查错误日志中的具体信息,根据情况调整日志格式为ROW或MIXED,并测试是否解决问题。同时,确保MySQL版本兼容性,必要时进行升级。</think>### 解决MySQL二进制日志格式存储引擎不兼容的问题 #### 问题分析 当MySQL的二进制日志格式(`binlog_format`)设置为`STATEMENT`时,若存储引擎(如InnoDB)的某些操作需要基于行的日志记录(`row-based logging`),可能导致复制错误或数据不一致[^1]。常见场景包括: 1. **非确定性操作**:如使用`UUID()`、`RAND()`等函数。 2. **触发器或存储过程**:涉及多行变更的操作。 3. **事务隔离级别**:高隔离级别(如`REPEATABLE READ`)可能强制使用行格式。 #### 解决方案 ##### 1. 修改`binlog_format`为`ROW` **步骤**: - 在MySQL配置文件(`my.cnf`或`my.ini`)中添加: ```ini [mysqld] binlog_format = ROW ``` - 重启MySQL服务。 - 验证配置生效: ```sql SHOW VARIABLES LIKE 'binlog_format'; ``` **优势**: - 避免因非确定性操作导致的主从不一致。 - 兼容InnoDB的事务特性,记录行级变更更精确[^3]。 ##### 2. 使用`MIXED`模式自动切换 若需兼顾日志可读性和可靠性: ```ini [mysqld] binlog_format = MIXED ``` - **行为**:默认使用`STATEMENT`,仅在需要时切换为`ROW`。 - **适用场景**:适合大部分操作可安全记录为语句,仅少数操作需行格式的场景。 ##### 3. 检查并修复特定SQL语句 若必须使用`STATEMENT`格式,需避免以下操作: - 包含非确定性函数的语句(如`NOW()`带时区)。 - 涉及多行修改的`UPDATE`或`DELETE`。 - 通过`SHOW WARNINGS`或错误日志定位问题语句[^1],例如: ```sql SHOW WARNINGS; ``` ##### 4. 升级MySQL版本 MySQL 8.0+ 对日志格式和存储引擎兼容性有优化[^4]。例如: - 改进的锁机制减少性能损耗。 - 更智能的日志格式切换逻辑。 #### 验证监控 - **监控复制状态**: ```sql SHOW SLAVE STATUS\G; -- 查看主从复制是否报错 ``` - **分析日志进度**(适用于MySQL 8.0+): ```sql SELECT WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_current WHERE EVENT_NAME LIKE 'stage/sql/Applying batch of row changes%'; [^4] ``` #### 示例配置 ```sql -- 临时修改会话级binlog_format(无需重启) SET SESSION binlog_format = 'ROW'; -- 永久生效需修改配置文件 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值