前几天,一研发小哥突然给我发了个微信,神神秘秘的问我,你觉得这个update会更新几条?(哎,讲话是真的太糙了,原本斯斯文文的我,就是被他们带偏的。。
)task_id=1的有4条,那update更新的就是4条呗,这有什么好猜的?

用脚后跟想,也能猜到结果肯定不是4条,答案是只更新了1行记录。(不然写这篇文章真的是多此一举了,哈哈哈)
居然真的当update修改的内容和原数据一致就不执行了?
天啊,明明我也全部看完了45讲,怎么能一点印象都没有呢,真的是黄鱼脑子啊!又去翻了翻丁奇45讲(强烈推荐),这道题是第15讲的课后问题。
Oracle肯定是第三种认真执行了“把这个值修改成(1,2)"这个操作,该加锁的加锁,该更新的更新。Mysql则会根据binlog_format和binlog_row_image参数的不同,而有不同的执行方式。(先把答案告诉大家吧)首先第一个选项,MySQL 读出数据,发现值与原来相同,不更新,直接返回,执行结束。通过下面这个实验来排除(所有例子都是基于rr隔离级别的):

不管binlog_format是什么格式,Session 2的update都会被blocked了,加锁这个动作,是innodb才能做的,所以能肯定排除了第一个选项。第二个选项,MySQL 调用了 InnoDB 引擎提供的接口,但是引擎发现值与原来相同,不更新,直接返回。在binlog_format=row和binlog_row_image=FULL时,由于Mysql需要在binlog里面记录所有的字段,所以在读数据的时候就会把所有数据都读出来,那么重复数据的update不会执行。


第三个选项,InnoDB 认真执行了“把这个值修改成 (1,2)"这个操作,该加锁的加锁,该更新的更新。在binlog_foramt=statement和binlog_row_image=FULL时,InnoDB内部会认真执行了update语句。丁奇45讲中的两个例子(我就偷懒了,不动手做了):
session A的第2个select语句是一致性读,它是不能看到Session B的更新的,现在它返回(1,3),表示它看见了某个新的版本,这个版本只能是session A自己的update语句做更新的时候生成的。InnoDB 认真执行了“把这个值修改成 (1,2)"这个操作,该加锁的加锁,该更新的更新。
我们可能会觉得为什么MySQL 怎么这么笨,就不会更新前判断一下值是不是相同吗?如果判断一下,不就不用浪费 InnoDB 操作,多去更新一次了?其实 MySQL 是确认了的。只是在这个语句里面,MySQL 认为读出来的值,只有一个确定的 (id=1), 而要写的是 (a=3),只从这两个信息是看不出来“不需要修改”的。

所以也能明白为什么binlog_format参数不同,同样的update语句,执行的方式却不一样了吧?如果Mysql能确认更新的内容,与原数据是一致的,那么就不更新,直接返回。1.在binlog_format=row和binlog_row_image=FULL时,由于Mysql需要在binlog里面记录所有的字段,所以在读数据的时候就会把所有数据都读出来,那么重复数据的update不会执行。即MySQL 调用了 InnoDB 引擎提供的接口,但是引擎发现值与原来相同,不更新,直接返回。
2.在binlog_foramt=statement和binlog_row_image=FULL时,InnoDB内部会认真执行了update语句,该加锁的加锁,该更新的更新。