MySQL在update发现要修改值跟原值相同,会再执行修改吗 ?

举报
JavaEdge 发表于 2022/03/08 23:48:45 2022/03/08
【摘要】 创建一个简单的表user,并插入一行,然后对这一行做修改。CREATE TABLE `users`( `id` int(11) NOT NULL primary key auto_increment, `type` int(11) DEFAULT NULL) ENGINE = InnoDB;insert into usersvalues (1, 2);这时候,表t里有唯一...

创建一个简单的表user,并插入一行,然后对这一行做修改。

CREATE TABLE `users`
(
    `id` int(11) NOT NULL primary key auto_increment,
    `type`  int(11) DEFAULT NULL
)
    ENGINE = InnoDB;
insert into users
values (1, 2);

这时候,表t里有唯一的一行数据(1,2)。

假设,现在执行:

update users
set type=2
where id = 1;

会观察到:

仅从现象上看,MySQL内部在处理这个命令的时候,可有如下选择:

①:更新都是先读后写,MySQL读出数据,发现type值本就是2呀,不更新,直接返回

②:MySQL调用InnoDB引擎提供的“修改为(1,2)”这个接口,但引擎发现值与原来相同,不更新,直接返回

③:InnoDB认真执行了“把这个值修改成(1,2)"这个操作,该加锁的加锁,该更新的更新

所以,当MySQL去更新一行,但是要修改的值跟原来的值相同,这时MySQL会选择:

  • 简单暴力地执行一次修改

  • 看到值相同了,就直接返回

下面开始实验验证,环境参数:

  • binlog_format=statement

1 锁实验

  • MySQL读出数据
  • 发现:值 == 原来值
  • 不更新
  • 直接返回

假设,当前表t里的值是(1,2):

Session 1 Session 2
begin;
update users set type=2 where id=1;
update users set type=2 where id=1 (阻塞!)

S2的update被阻塞,由于InnoDB才能加锁,所以排除①。

2 可见性验证

MySQL调用InnoDB引擎提供的接口,但引擎发现值与原来相同,不更新,直接返回。
假设当前表里的值是(1,2):

S 1 S 2
begin;
select * from users where id=1; // 返回(1, 2)
update users set type=3 where id=1;
update users set type=3 where id=1;
0 row affected
select * from users where id=1; //返回(1,3)

S1的第二个select语句是一致性读(快照读),它看不见S2的更新。但它返回(1,3),表示它看见了某个新版本,这个版本只能是S1自己的update语句做更新时生成的。因此,排除②。

3 ✅的想法

InnoDB认真执行“把这个值修改成(1,2)",该加锁的加锁,该更新的更新。

你会说MySQL咋这么蠢,就不会更新前判断一下值是否相同?这不是浪费InnoDB多更新一次吗?

其实MySQL已确认过。只是在这个语句里,MySQL认为读出来的值,只有一个确定的 (id=1),而要写的是(type=3),只从这两个信息看不出“无需修改”。

再看个实验。

4 可见性验证方式–对照

S 1 S 2
begin;
select * from users where id=1; // 返回(1, 2)
update users set type=3 where id=1;
update users set type=3 where id=1 and type=3;
0 rows affected
select * from users where id=1; //返回(1,2)

因为where条件中存在update的值InnoDB认为值一致所以没有修改,A的一致性视图也看不到B的修改。

mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.02 sec)

mysql> show variables like 'binlog_row_image';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| binlog_row_image | FULL  |
+------------------+-------+
1 row in set (0.02 sec)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。