V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
ShutTheFu2kUP
V2EX  ›  MySQL

MVCC 写写操作提交时版本对比的问题

  •  
  •   ShutTheFu2kUP · 2019-11-28 10:27:17 +08:00 · 4497 次点击
    这是一个创建于 1804 天前的主题,其中的信息可能已经有所发展或是发生改变。

    事务 A (版本号为 2 )

    1、START TRANSACTION;
    
    2、SELECT * FROM test_mvcc WHERE id = 1;
    
    6、UPDATE test_mvcc SET `value` = 'b' WHERE id = 1;
    
    7、COMMIT;
    

    事务 B (版本号为 3 )

    3、START TRANSACTION;
    
    4、UPDATE test_mvcc SET `value` = 'c' WHERE id = 1;
    
    5、COMMIT;
    

    SQL 语句前面的序号为执行顺序,事务 A 开始做了一个快照读。然后事务 B 开启,修改数据并提交,此时数据的版本号应该是 3。这时候事务 A 也修改数据,然后提交的时候发现数据库中数据的版本号比自己的大,所以修改失败回滚操作。

    以上是最近通过学习 MVCC 我认为理论上应该出现的情况,然后我自己测试发现事务 A 还是能提交成功,这究竟是什么原因?难道是哪里的理解有偏差吗。(当前数据库隔离级别为 Read Repeatable )

    12 条回复    2019-11-29 09:34:23 +08:00
    sun1991
        1
    sun1991  
       2019-11-28 10:43:48 +08:00
    因为事务 B 已经 commit 了呀, 你试试去掉 5, 看看执行是不是卡在那里了. 如果你期望事物 A 报错回滚, 那么需要更高一级的隔离级别.
    zhenhuaYang
        2
    zhenhuaYang  
       2019-11-28 10:44:45 +08:00   ❤️ 1
    首先是 repeatable read 不是 read repeatable

    事务 A 为什么不能提交成功? mvcc 多版本并发控制和 next-key locks 间隙锁搭配实现了事务的可重复读而已,只不过是让 mysql 可以重复读取数据而已,为什么不能提交成功呢?

    你说的事务 B 提交数据后,事务 A 发现此时的数据库提交版本要高于自己,就不会提交成功会回滚,你说的是乐观锁吗?

    乐观锁是提交版本大于当前数据库提交版本才会去更新数据。
    ShutTheFu2kUP
        3
    ShutTheFu2kUP  
    OP
       2019-11-28 10:59:02 +08:00
    @zhenhuaYang 首先感谢大佬指正。其次,按照你的意思就是 MVCC 中并没有实现这种乐观锁的机制是吗?因为最近学 MVCC 相关知识,国内博客资料和 MySQL 官方文档在这块上都描述的模糊不清,一人一种说法,搞得我学的真的是一头雾水。
    zhenhuaYang
        4
    zhenhuaYang  
       2019-11-28 11:03:07 +08:00
    @ShutTheFu2kUP 你可以看看这篇文章,或许看完你就恍然大悟了。https://tech.meituan.com/2014/08/20/innodb-lock.html
    ShutTheFu2kUP
        5
    ShutTheFu2kUP  
    OP
       2019-11-28 11:03:55 +08:00
    @zhenhuaYang 好的,谢谢
    newtype0092
        6
    newtype0092  
       2019-11-28 11:11:30 +08:00
    事务是没有版本号的吧,事务 A 和事务 B 里这条数据的版本号都是 v2,B 提交后数据版本变为'c'(v3),但在 A 中,只要不修改,重复读取数据,看到的还是'b'(v2),这就是“可重复读”的意思吧。
    banks0913
        7
    banks0913  
       2019-11-28 12:12:25 +08:00
    因为 UPDATE 操作属于当前读,即基于最新数据去进行更新,不像 SELECT 操作是基于 mvcc 读当前事务版本的数据快照。
    peyppicp
        8
    peyppicp  
       2019-11-28 13:05:46 +08:00
    1 楼+7 楼组合起来就是正解了。
    phantomzz
        9
    phantomzz  
       2019-11-28 20:33:27 +08:00
    @zhenhuaYang 今天看了一下午你发的这篇文章,我还是不能认同你的回复,按照我的理解,A 事务先发起,B 事务后发起,A 事务先 select 以后,会给对应的 rows 以及区间加上行锁以及 gap 锁,这种情况下,B 事务的 update 应该是无法执行的。我看文中对 Next-Key 锁介绍的部分是这么说的。还是我的理解有误?请指教!
    phantomzz
        10
    phantomzz  
       2019-11-28 20:38:32 +08:00
    重新尝试着理解了一下,是不是事务 A 的 select 获取的只是 s 锁,事务 B 的 update 获取的是 x 锁,事务 B 在 commit 以后释放了 x 锁,所以事务 A 可以 update ?我来调换一下 5 和 6 的步骤验证下看看
    @zhenhuaYang
    phantomzz
        11
    phantomzz  
       2019-11-28 21:01:24 +08:00
    再次挖掘了一下,理解到:事务 A 第 2 步,是快照读,不会加锁,事务 B 第 4 步是当前读,会加 X 锁,紧接着的第 5 步释放了 X 锁。
    zhenhuaYang
        12
    zhenhuaYang  
       2019-11-29 09:34:23 +08:00
    @phantomzz 嗯嗯,对,没错。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1859 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:29 · PVG 00:29 · LAX 08:29 · JFK 11:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.