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如何帮助我们实现并发控制和数据一致性。

相关推荐
Livingbody2 分钟前
ubuntu25.04完美安装typora免费版教程
后端
阿华的代码王国9 分钟前
【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
android·xml·java·前端·后端
码农BookSea13 分钟前
自研 DSL 神器:万字拆解 ANTLR 4 核心原理与高级应用
java·后端
lovebugs16 分钟前
Java并发编程:深入理解volatile与指令重排
java·后端·面试
海奥华237 分钟前
操作系统到 Go 运行时的内存管理演进与实现
开发语言·后端·golang
codervibe38 分钟前
Spring Boot 服务层泛型抽象与代码复用实战
后端
_風箏43 分钟前
Shell【脚本 04】传递参数的4种方式(位置参数、特殊变量、环境变量和命名参数)实例说明
后端
斜月44 分钟前
Python Asyncio以及Futures并发编程实践
后端·python
CRUD被占用了1 小时前
coze-studio学习笔记(一)
后端