什么是并发控制?MySQL并发访问的实现原理是什么

来源:AI社区作者:柬埔寨程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《什么是并发控制?MySQL并发访问的实现原理是什么》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《什么是并发控制?MySQL并发访问的实现原理是什么》有用,将其分享出去将是对创作者最好的鼓励。

并发控制指的是当多个用户或进程同时操作同一数据库资源时,通过特定规则协调操作顺序,保证数据一致性和完整性的机制。在多用户场景下,如果没有合理的并发控制,很容易出现数据覆盖、读取到未提交数据等问题,影响业务正确性。

什么是并发控制?MySQL并发访问的实现原理是什么

并发控制的核心问题

当多个事务同时操作数据库时,会产生三类典型问题:

  • 脏读:一个事务读取到另一个事务未提交的数据,若后者回滚,前者读取的数据就是无效的。
  • 不可重复读:同一事务内多次读取同一数据,结果不一致,原因是其他事务在两次读取之间修改了该数据。
  • 幻读:同一事务内多次查询符合条件的数据集,结果集行数不一致,原因是其他事务插入或删除了符合该条件的数据。

MySQL的事务隔离级别

MySQL通过定义四种事务隔离级别,从规则层面控制并发问题的发生范围,不同隔离级别的并发能力和一致性保障不同:

隔离级别脏读不可重复读幻读
读未提交(READ UNCOMMITTED)可能可能可能
读已提交(READ COMMITTED)不可能可能可能
可重复读(REPEATABLE READ)不可能不可能InnoDB引擎下不可能
串行化(SERIALIZABLE)不可能不可能不可能

MySQL默认的事务隔离级别是可重复读,可通过以下语句查看和修改当前会话的隔离级别:

-- 查看当前会话隔离级别
SELECT @@transaction_isolation;

-- 设置当前会话隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

MySQL的锁机制

锁是MySQL实现并发控制的基础手段,按照锁的粒度可以分为三类:

全局锁

全局锁会对整个数据库实例加锁,加锁后整个实例处于只读状态,常用于全库逻辑备份场景。加锁语句如下:

-- 加全局锁
FLUSH TABLES WITH READ LOCK;

-- 释放全局锁
UNLOCK TABLES;

表级锁

表级锁会锁定整张表,开销小但并发度低,分为表锁和元数据锁(MDL)。表锁可以手动添加,MDL是访问表时自动加的,防止表结构变更和读写操作冲突。手动加表锁的示例如下:

-- 给user表加读锁,当前会话只能读该表,不能写,其他会话可以读但不能写
LOCK TABLES user READ;

-- 给user表加写锁,当前会话可以读写该表,其他会话不能读写
LOCK TABLES user WRITE;

-- 释放所有表锁
UNLOCK TABLES;

行级锁

行级锁是InnoDB引擎特有的锁,只锁定操作涉及的行,开销大但并发度高,分为共享锁和排他锁:

  • 共享锁(S锁):允许事务读一行数据,多个事务可以同时持有同一行的共享锁,但不能加排他锁。
  • 排他锁(X锁):允许事务修改或删除一行数据,加锁后其他事务不能对该行加任何锁。

行级锁的加锁示例如下:

-- 开启事务
START TRANSACTION;

-- 给id=1的行加共享锁
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE;

-- 给id=1的行加排他锁
SELECT * FROM user WHERE id = 1 FOR UPDATE;

-- 提交事务释放锁
COMMIT;

多版本并发控制(MVCC)

MVCC是InnoDB引擎实现可重复读和读已提交隔离级别的核心技术,通过保存数据在某个时间点的快照来实现非锁定读,提升并发读性能。MVCC的实现依赖三个部分:

  • 隐藏字段:InnoDB的每一行数据都有三个隐藏字段,分别是DB_TRX_ID(最后修改该行的事务ID)、DB_ROLL_PTR(回滚指针,指向undo log中的历史版本)、DB_ROW_ID(行ID,无主键时自动生成)。
  • undo log:记录数据修改前的版本,多个undo log通过回滚指针形成版本链,不同的事务可以读取到对应时间点的历史版本。
  • Read View:事务开启时生成的快照,记录了当前活跃的事务ID列表,通过对比数据行的DB_TRX_ID和Read View中的事务ID,判断当前事务可以读取哪个版本的数据。

下面通过一个简单的Java代码示例,模拟不同隔离级别下的并发读取表现:

import java.sql.*;

public class MysqlConcurrencyDemo {
    // 数据库连接信息,示例地址替换规则:原ippipp.com替换为ipipp.com
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false";
    private static final String USER = "root";
    private static final String PASSWORD = "password";

    public static void main(String[] args) throws Exception {
        // 事务1:修改数据但不提交
        Thread t1 = new Thread(() -> {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(URL, USER, PASSWORD);
                // 设置隔离级别为读已提交
                conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                conn.setAutoCommit(false);
                // 修改id=1的用户名
                PreparedStatement ps = conn.prepareStatement("UPDATE user SET name = 'new_name' WHERE id = 1");
                ps.executeUpdate();
                System.out.println("事务1修改完成,未提交");
                // 休眠5秒,让事务2先读取
                Thread.sleep(5000);
                // 回滚修改
                conn.rollback();
                System.out.println("事务1回滚完成");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        // 事务2:读取数据
        Thread t2 = new Thread(() -> {
            Connection conn = null;
            try {
                Thread.sleep(1000);
                conn = DriverManager.getConnection(URL, USER, PASSWORD);
                // 设置隔离级别为读已提交
                conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                conn.setAutoCommit(false);
                // 第一次读取
                PreparedStatement ps = conn.prepareStatement("SELECT name FROM user WHERE id = 1");
                ResultSet rs = ps.executeQuery();
                if (rs.next()) {
                    System.out.println("事务2第一次读取结果:" + rs.getString("name"));
                }
                // 休眠3秒,等待事务1回滚
                Thread.sleep(3000);
                // 第二次读取
                rs = ps.executeQuery();
                if (rs.next()) {
                    System.out.println("事务2第二次读取结果:" + rs.getString("name"));
                }
                conn.commit();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t1.start();
        t2.start();
    }
}

如果隔离级别是读未提交,事务2第一次读取会拿到事务1未提交的new_name;如果是读已提交,事务2两次读取都会拿到修改前的原始值,因为事务1未提交,符合读已提交的隔离规则。

总结

MySQL的并发访问控制是锁机制和MVCC结合实现的,锁机制保障了写操作的并发安全,MVCC提升了读操作的并发性能。开发者在实际使用中,需要根据业务场景选择合适的隔离级别,读多写少的场景可以优先使用默认的可重复读隔离级别,对一致性要求极高的场景可以选择串行化,同时要注意避免长事务导致的锁等待和undo log膨胀问题。

并发控制MySQL事务隔离级别锁机制多版本并发控制修改时间:2026-07-09 01:36:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。