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
lazyczx
V2EX  ›  MySQL

在看《高性能 MySQL》第 3 版,有点问题想问

  •  
  •   lazyczx · 221 天前 · 2892 次点击
    这是一个创建于 221 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我之前知道 InnoDB 实现的可重复读级别可以解决大部分情况下幻读的问题。

    然后我看书的时候,看到书里对 InnoDB 的 MVCC 的描述是基于事务开始时系统的版本号。

    每开始一个事务,系统版本号都会递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面看一下在 REPEATABLE READ 隔离级别下,MVCC 具体是如何操作的。 SELECT InnoDB 会根据以下两个条件检查每行记录: a. InnoDB 只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。 ...

    问题来了,想问书里的描述:_这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的_。

    这个是怎么确保的?

    因为描述中写了事务的 id 是在开始时决定的。那如果事务开始时,有一个旧的事务还没结束呢?然后事务先读取了一行数据(不使用当前读启用 next-key lock 的情况下),旧的事务又修改了这行数据,然后提交,然后事务又读了这行数据,那是不是就出现幻读了呢?

    15 条回复    2024-04-24 10:17:20 +08:00
    keakon
        1
    keakon  
       221 天前
    这个是用快照机制保证的:
    REPEATABLE READ
    This is the default isolation level for InnoDB. Consistent reads within the same transaction read the snapshot established by the first read.
    https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
    keakon
        2
    keakon  
       221 天前
    Flourite
        3
    Flourite  
       221 天前   ❤️ 1
    innodb 每行有三个隐藏列,其中一个是 tx_id ,read_view 的 tx_id 跟每行的 tx_id 比较得出可见的行

    https://relph1119.github.io/mysql-learning-notes/#/mysql/24-%E4%B8%80%E6%9D%A1%E8%AE%B0%E5%BD%95%E7%9A%84%E5%A4%9A%E5%B9%85%E9%9D%A2%E5%AD%94-%E4%BA%8B%E5%8A%A1%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E4%B8%8EMVCC

    ps:mysql 的 rr 级别没有彻底解决幻读的问题
    Flourite
        4
    Flourite  
       221 天前
    [高性能 mysql] 这本书没太大用,还是看 [MySQL 是怎样运行的:从根儿上理解 MySQL]
    ttoh
        5
    ttoh  
       221 天前 via Android   ❤️ 5
    每个事务开启的时候,除了记录自己的 trx id ,还会记录当前所有活跃(已启动未提交的)事务 id ( active_trx_ids ),以及当前最小活跃 id ( min_trx_id )和当前已开启事务的最大 id ( max_trx_id )。满足 view_id = trx_id || view_id < min_trx_id || (view_id between min_trx_id and max_trx_id && view_id not in active_ids),是可见的版本记录
    lazyczx
        6
    lazyczx  
    OP
       221 天前
    @keakon

    > consistent read 一致读
    > A read operation that uses snapshot information to present query results based on a point in time, regardless of changes performed by other transactions running at the same time. If queried data has been changed by another transaction, the original data is reconstructed based on the contents of the undo log. This technique avoids some of the locking issues that can reduce concurrency by forcing transactions to wait for other transactions to finish.
    > 一种读操作,它使用快照信息基于某个时间点呈现查询结果,而不管同时运行的其他事务执行的更改如何。如果查询的数据被另一个事务更改,则根据 undo log 的内容重建原始数据。此技术通过强制事务等待其他事务完成来避免一些可能降低并发性的锁定问题。

    这个重建原始数据的意思,就是找到本事务开始的时候的数据(快照)对吧?所以这一步是通过 MVCC 实现的对吧。

    > MVCC
    > Acronym for “multiversion concurrency control”. This technique lets InnoDB transactions with certain isolation levels perform consistent read operations; that is, to query rows that are being updated by other transactions, and see the values from before those updates occurred. This is a powerful technique to increase concurrency, by allowing queries to proceed without waiting due to locks held by the other transactions.

    也就是说我这个问题的答案就是,MVCC 不仅支持向前回滚查找数据,也支持向后回滚?因为官方文档对 MVCC 的描述是 before those updates occurred ,但没有强调 transaction ID 的先后关系,就是不管哪个事务新哪个事务旧,反正都可以从 undo log 上找到当前这个事务开始的版本,取用?

    但是不太懂这个描述的最后一句:强制事务等待其他事务结束,这个和我想问的问题有关系吗?

    还是说这个数据快照不是通过 MVCC 实现的?
    lazyczx
        7
    lazyczx  
    OP
       221 天前
    @ttoh 牛逼,这个判断确实完全解决了我的问题
    lazyczx
        8
    lazyczx  
    OP
       221 天前
    @Flourite
    看了下,确实好东西。。。越来越怀疑我手上这本是不是已经太过时了。看这本书的时候看到和我网上搜出来,问 GPT 问出来的结论不一样的描述了已经。。

    没解决的幻读是不是这两种:
    - 事务 A 先快照读,读出 empty set ,事务 B 插入数据,事务 A 再 update 这个数据,然后再快照读,就能读出来这个数据了。这个情况下预期的应该是无法 update 也无法读出来才对。
    - T1 时刻,事务 A 执行快照读,读出 3 条,T2 ,事务 B 插入一条记录,T3 ,事务 A 执行当前读,读出 4 条。这里的解决办法是再开启事务后,马上执行当前读语句。
    Flourite
        9
    Flourite  
       220 天前
    @lazyczx 第一种,第二种应该不会出现
    keakon
        10
    keakon  
       220 天前
    @lazyczx 最后一句是说查询时不需要等待被其他事务持有的锁啊
    lazyczx
        11
    lazyczx  
    OP
       220 天前
    @keakon o 仔细看了下英文。。确实。。= =抱歉问了个蠢问题
    fkdtz
        12
    fkdtz  
       220 天前   ❤️ 1
    MVCC = trx_id + roll_pointer + undolog 版本链 + ReadView
    ReadView 结构中维护了当前事务开启时的活跃事务 id 列表,判断可见性就是 5 楼说的规则,这就保证了 repeatable read 隔离级别下不会出现不可重复读问题。

    至于幻读问题,按照 SQL 标准来说 repeatable read 隔离级别是会出现的,但 MySQL InnoDB 的实现中避免了幻读,主要是通过 MVCC + Gap Locks 实现的。因为幻读描述的是在一个区间内的两次查询会出现原本不存在的行、或原本存在的行消失了,Gap Locks 通过在一个区间内加锁,导致后续事务无法修改数据,从而避免了幻读的发生。
    dog82
        13
    dog82  
       220 天前
    这书我也买了,只看了前 100 页,文中反复提及的基线测试工具有点印象
    现在 mysql 在走下坡路,国内被各种国产分布式数据库反超了,国际上还有 postgresql 这个追兵
    Narcissu5
        14
    Narcissu5  
       220 天前
    @lazyczx 这个书已经出到第四版了,你看这版确实有点太老了
    RangerWolf
        15
    RangerWolf  
       220 天前
    这本书深度阅读过,而且做了比较多的读书笔记。
    最好有实际业务或者一定的数据量做测试,能极大的提供对 MySQL 的认知

    这本书还在我的案头,我觉得可以持续读下去,把你觉得对你有帮助的章节都仔细阅读、做实验、做测试
    反正我自己深度阅读之后感觉收益颇丰
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2696 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 12:11 · PVG 20:11 · LAX 04:11 · JFK 07:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.