MySQL(174)如何理解MySQL的多版本并发控制(MVCC)?

MySQL的多版本并发控制(MVCC, Multi-Version Concurrency Control)是一种用于实现高并发性的机制,它允许多个事务同时读取和写入数据,而不会相互阻塞。MVCC主要在InnoDB存储引擎中实现,通过维护数据的多个版本来实现一致性和隔离性。

一、MVCC的基本原理

MVCC通过维护每行数据的多个版本,使得读操作不会阻塞写操作,写操作也不会阻塞读操作。它通过以下机制实现:

  1. 隐藏列

    • InnoDB表的每一行都有两个隐藏列:trx_idroll_pointer
    • trx_id(事务ID)记录了最近一次修改该行的事务ID。
    • roll_pointer指向回滚段中的上一个版本。
  2. 快照读

    • 快照读读取的是数据的某个版本快照,而不是当前最新版本。它通过检查每行数据的trx_id和当前事务的ID来决定读取哪个版本。
    • 快照读通常用于SELECT语句,不会加锁。
  3. 当前读

    • 当前读读取的是数据的最新版本,它会对读取的行加锁,确保数据的最新性和一致性。
    • 当前读通常用于SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE语句。

二、事务隔离级别与MVCC

MVCC在不同的事务隔离级别下有不同的表现:

  1. 读未提交(READ UNCOMMITTED)

    • 事务可以读取其他事务未提交的数据(脏读)。
    • 不适用MVCC。
  2. 读已提交(READ COMMITTED)

    • 事务只能读取其他事务已提交的数据。
    • 每次读取数据时,会读取最新的已提交版本。
  3. 可重复读(REPEATABLE READ)

    • 事务在开始时创建一个一致性视图,所有读取操作都基于这个视图。
    • 事务进行过程中,即使其他事务已提交,也不会看到这些修改。
    • 防止不可重复读和幻读。
  4. 序列化(SERIALIZABLE)

    • 最严格的隔离级别,事务完全串行化执行。
    • 会对读取的每一行数据加锁。

三、示例代码

以下是一些示例代码,展示了在不同隔离级别下,使用MVCC如何避免读写冲突。

1. 创建测试表并插入数据

sql 复制代码
CREATE TABLE test_mvcc (
    id INT PRIMARY KEY,
    value VARCHAR(50)
);

INSERT INTO test_mvcc (id, value) VALUES (1, 'Initial Value');

2. 设置事务隔离级别并启动事务

sql 复制代码
-- 在会话1中(事务1)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

-- 在会话2中(事务2)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

3. 快照读示例

sql 复制代码
-- 在会话1中,读取数据
SELECT * FROM test_mvcc WHERE id = 1;

-- 在会话2中,更新数据
UPDATE test_mvcc SET value = 'Updated Value' WHERE id = 1;

-- 在会话1中,再次读取数据
SELECT * FROM test_mvcc WHERE id = 1;

-- 此时,会话1中的两次读取结果都是 'Initial Value',因为使用的是快照读

4. 当前读示例

sql 复制代码
-- 在会话1中,使用当前读读取数据
SELECT * FROM test_mvcc WHERE id = 1 FOR UPDATE;

-- 在会话2中,尝试更新数据
UPDATE test_mvcc SET value = 'Another Update' WHERE id = 1;

-- 会话2中的更新操作会被阻塞,直到会话1提交或回滚事务

5. 提交事务

sql 复制代码
-- 在会话1中,提交事务
COMMIT;

-- 在会话2中,更新操作解除阻塞,继续执行
UPDATE test_mvcc SET value = 'Another Update' WHERE id = 1;

四、Java代码示例

以下是一个Java示例程序,展示了如何通过JDBC设置事务隔离级别,并展示使用MVCC的效果。

java 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class MVCCExample {

    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String USER = "your_db_user";
    private static final String PASSWORD = "your_db_password";

    public static void main(String[] args) {
        try (Connection connection1 = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             Connection connection2 = DriverManager.getConnection(JDBC_URL, USER, PASSWORD)) {

            connection1.setAutoCommit(false);
            connection2.setAutoCommit(false);

            // 设置事务隔离级别为 REPEATABLE READ
            connection1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
            connection2.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

            // 在会话1中读取数据
            try (Statement statement1 = connection1.createStatement()) {
                ResultSet rs1 = statement1.executeQuery("SELECT value FROM test_mvcc WHERE id = 1");
                while (rs1.next()) {
                    System.out.println("Session 1 - Initial Value: " + rs1.getString("value"));
                }
            }

            // 在会话2中更新数据
            try (Statement statement2 = connection2.createStatement()) {
                statement2.executeUpdate("UPDATE test_mvcc SET value = 'Updated Value' WHERE id = 1");
            }

            // 在会话1中再次读取数据
            try (Statement statement1 = connection1.createStatement()) {
                ResultSet rs1 = statement1.executeQuery("SELECT value FROM test_mvcc WHERE id = 1");
                while (rs1.next()) {
                    System.out.println("Session 1 - After Update in Session 2: " + rs1.getString("value"));
                }
            }

            // 提交会话2
            connection2.commit();

            // 在会话1中再次读取数据(验证隔离级别的效果)
            try (Statement statement1 = connection1.createStatement()) {
                ResultSet rs1 = statement1.executeQuery("SELECT value FROM test_mvcc WHERE id = 1");
                while (rs1.next()) {
                    System.out.println("Session 1 - After Commit in Session 2: " + rs1.getString("value"));
                }
            }

            // 提交会话1
            connection1.commit();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

五、总结

MySQL的MVCC通过维护多个数据版本实现高并发性和一致性,使得读操作和写操作可以并行执行而不会互相阻塞。理解MVCC的工作原理,对于设计高性能、高并发的数据库应用至关重要。通过示例代码,我们可以看到在不同事务隔离级别下,MVCC如何帮助我们实现并发控制和数据一致性。

相关推荐
Victor356几秒前
Hibernate(42)在Hibernate中如何实现分页?
后端
Victor3567 分钟前
Hibernate(41)Hibernate的延迟加载和急加载的区别是什么?
后端
猪猪拆迁队20 分钟前
2025年终总结-都在喊前端已死,这一年我的焦虑、挣扎与重组:AI 时代如何摆正自己的位置
前端·后端·ai编程
ConardLi27 分钟前
SFT、RAG 调优效率翻倍!垂直领域大模型评估实战指南
前端·javascript·后端
Hooray1 小时前
2026年,站在职业生涯十字路口的我该何去何从?
前端·后端
唐叔在学习1 小时前
还在申请云服务器来传输数据嘛?试试P2P直连吧
后端·python
开心猴爷2 小时前
iOS 代码混淆在项目中的方式, IPA 级保护实践记录
后端
魅影骑士00102 小时前
柯里化函数
后端·设计模式
JOEH603 小时前
🛡️ 微服务雪崩救星:Sentinel 限流熔断实战,3行代码搞定高可用!
后端·全栈
aiopencode3 小时前
iOS手动代码混淆函数和变量名基本原理和注意事项教程
后端