› 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 · Nov 28, 2019 · 5764 views
    This topic created in 2493 days ago, the information mentioned may be changed or developed.

    事务 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 replies  •  2019-11-29 09:34:23 +08:00
    sun1991
        1
    sun1991  
       Nov 28, 2019
    因为事务 B 已经 commit 了呀, 你试试去掉 5, 看看执行是不是卡在那里了. 如果你期望事物 A 报错回滚, 那么需要更高一级的隔离级别.
    zhenhuaYang
        2
    zhenhuaYang  
       Nov 28, 2019   ❤️ 1
    首先是 repeatable read 不是 read repeatable

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

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

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