"测试MySQL对相同update语句的处理方式,环境为MySQL5.7.25,Centos 7.4,binlog_format设为ROW。"
在MySQL数据库中,当你执行一个update语句来更新表中的记录,但所用的条件和原数据完全一致,即没有实际改变任何值,MySQL是否会重新执行这个更新操作呢?这个问题涉及到MySQL的事务处理、日志记录以及binlog_format设置。以下是对这个问题的详细分析:
首先,binlog_format是MySQL的二进制日志(binlog)格式设置,它决定了binlog中记录的事件内容。在ROW模式下,binlog会记录行级别的变化,而不是SQL语句本身。这意味着,即使update语句没有实际改变任何数据,只要它被提交,ROW模式的binlog也会记录这次操作。
测试环境中,binlog_row_image设置为FULL,这意味着binlog会记录完整的行图像,包括所有列的值,即使这些值在更新后没有改变。这确保了在复制场景下,目标服务器可以精确地重现源服务器上的每一行状态。
然后,transaction_isolation设置为REPEATABLE-READ,这是MySQL的默认事务隔离级别,它保证了在一个事务内多次读取同一数据的结果是一致的,不会受到其他事务的影响。
测试步骤中,首先开启了一个事务,并尝试更新id为1的记录。在这种情况下,如果update语句的条件和当前记录的值完全相同,MySQL的处理方式通常如下:
1. **解析与校验**:MySQL会解析并验证SQL语句,确保其语法正确。即使update操作不会改变任何数据,这个过程仍然会发生。
2. **读取旧值**:MySQL会读取要更新的行的当前值,即使这些值在update语句中不会被改变。
3. **执行检查**:MySQL会比较新值和旧值。如果新值与旧值完全相同,那么实际上没有变化发生,MySQL通常会跳过更新记录的操作。
4. **binlog记录**:即使没有实际更新,ROW模式的binlog仍然会记录这次尝试的更新,包括完整的行信息。这是因为binlog记录的是行级别的变化,而非SQL语句的效果。
5. **事务提交**:如果事务被提交,binlog中的事件会被写入到redo log(InnoDB引擎的事务日志)以备恢复,但redo log并不关心数据是否实际改变,只关心事务的原子性和持久性。
因此,尽管MySQL执行了update语句,且语句与原数据相同,实际上数据库中的数据并没有发生变化。然而,binlog仍会记录这次操作,因为它基于ROW模式,关注的是行级别的变化。在主从复制的场景下,从库会根据binlog重新执行这个操作,但因为数据没有实际变化,所以从库的状态也不会有任何改变。