MySQL提供四种事务隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE,分别解决脏读、不可重复读和幻读等并发问题。其中REPEATABLE READ为InnoDB默认级别,通过MVCC和间隙锁机制在保证一致性的同时兼顾性能,能有效避免大多数幻读场景。可通过SET TRANSACTION在会话级临时调整,或通过配置文件、SET GLOBAL在全局级设置,默认修改需权衡一致性与并发性能,通常建议保持默认并在特定场景下会话级调整。

MySQL控制事务的隔离级别主要通过两种方式:在会话级别使用SET TRANSACTION语句临时调整,或者在服务器级别通过配置文件或SET GLOBAL语句设置默认值。这其实是数据库系统为了在数据一致性和并发性能之间找到平衡点,提供给开发者和DBA的一项关键能力。理解并恰当地设置它,对应用的稳定性和效率都至关重要。
要控制MySQL事务的隔离级别,你可以选择在会话层面或者全局层面进行设置。
在当前会话中设置隔离级别,该设置只对当前客户端连接有效:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 或者 SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 或者 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 或者 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
这句命令需要在START TRANSACTION之前执行,否则对当前已开始的事务无效,但会影响之后开启的事务。
如果你想为所有新连接设置默认的隔离级别,可以在服务器启动时通过配置文件my.cnf(或my.ini)指定,或者在运行时使用SET GLOBAL语句。
在my.cnf中添加或修改:
[mysqld] transaction-isolation = READ-COMMITTED
注意,这里隔离级别的名称是连字符形式。修改配置文件后需要重启MySQL服务才能生效。
在运行时全局设置(影响之后所有新建立的连接):
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
这会立即影响所有新建立的会话,但不会改变当前已连接会话的隔离级别。对于已连接的会话,它们会继续沿用各自当前的隔离级别,除非它们自己用SET TRANSACTION显式修改。
我个人觉得,虽然有多种设置方式,但日常开发中,我们更多地是依赖MySQL的默认REPEATABLE READ,并在少数需要更高并发或更高一致性的场景下,才考虑在会话级别进行微调。全局修改默认值则需要更谨慎的评估,因为它会影响整个数据库的行为模式。
MySQL,特别是InnoDB存储引擎,提供了四种标准的事务隔离级别,每种级别都在处理并发问题上有所侧重。理解这些级别以及它们解决的“读现象”是至关重要的。
READ UNCOMMITTED (读未提交)
READ COMMITTED (读已提交)
REPEATABLE READ (可重复读)
SELECT语句的幻读问题,但对于UPDATE或DELETE操作,幻读仍有可能发生,需要特定的锁机制来完全避免。SERIALIZABLE (串行化)
REPEATABLE READ隔离级别是如何工作的?它真的能完全避免幻读吗?MySQL的REPEATABLE READ隔离级别之所以能做到“可重复读”,并且在大部分情况下避免幻读,主要归功于InnoDB存储引擎的MVCC(Multi-Version Concurrency Control,多版本并发控制)机制。
当一个事务在REPEATABLE READ级别下启动时,它会获得一个快照(snapshot)。这个快照记录了当前数据库中所有已提交事务的状态。之后,在这个事务的生命周期内,所有的SELECT语句都将基于这个快照来读取数据。这意味着,即使其他事务在此期间修改并提交了数据,当前事务的SELECT操作看到的仍然是它自己快照时刻的数据,从而避免了“不可重复读”。
对于幻读,情况稍微复杂一点。标准的SQL定义中,REPEATABLE READ是允许幻读的。但在MySQL的InnoDB中,MVCC对于SELECT语句的幻读问题提供了强大的缓解。如果一个事务在REPEATABLE READ级别下执行SELECT * FROM table WHERE id > 100;,即使另一个事务插入了id=101的记录并提交,当前事务再次执行同样的SELECT语句,它仍然不会看到id=101这条新记录,因为新记录是在快照之后插入的。
然而,MVCC并不能完全阻止所有形式的幻读,尤其是在涉及数据修改(INSERT, UPDATE, DELETE)的场景。例如,如果一个事务执行了SELECT ... FOR UPDATE或者UPDATE ... WHERE ...,它可能需要锁定一个范围的数据。为了防止其他事务在这个范围内插入新的数据导致“幻影行”,InnoDB会使用间隙锁(Gap Lock)。间隙锁是InnoDB特有的一种锁,它锁定的是索引记录之间的“间隙”,或者锁定某个索引记录之前/之后的间隙。通过锁定这些间隙,可以有效阻止其他事务在被锁定范围内插入新的记录,从而避免了UPDATE或DELETE操作可能导致的幻读。
所以,虽然REPEATABLE READ理论上可能出现幻读,但MySQL InnoDB的实现结合了MVCC和间隙锁,使得它在绝大多数情况下,无论是SELECT还是UPDATE/DELETE,都能提供一个非常接近SERIALIZABLE级别的无幻读体验。这也就是为什么它是MySQL的默认隔离级别,因为它在性能和数据一致性之间找到了一个很好的平衡点。
修改MySQL的默认REPEATABLE READ隔离级别,通常是出于对并发性能或数据一致性更极致的追求。这是一个典型的权衡问题,没有银弹。
考虑降级到 READ COMMITTED 的场景:
READ COMMITTED可以显著提升数据库的并发性能。因为在这个级别下,事务在读取数据时不会像REPEATABLE READ那样持有旧版本快照,而是读取最新的已提交数据。这减少了MVCC的开销,也避免了REPEATABLE READ可能因间隙锁导致的死锁或等待。READ COMMITTED可以提供更好的读取性能,减少锁等待。考虑升级到 SERIALIZABLE 的场景:
SERIALIZABLE可以确保数据在整个事务过程中不会被其他事务修改,从而简化了逻辑,避免了潜在的数据错误。取舍分析:
SERIALIZABLE虽然性能差,但在一定程度上简化了开发,因为它帮你处理了所有并发问题。READ COMMITTED通常只使用行锁,而REPEATABLE READ会结合使用行锁和间隙锁,SERIALIZABLE则可能使用表锁或更严格的行锁。这直接影响了数据库的并发能力。总而言之,修改默认隔离级别是一个需要深思熟虑的决策。我个人的经验是,如果不是有明确的性能瓶颈且确认REPEATABLE READ是瓶颈所在,或者有极其严格的数据一致性需求,通常会选择保持MySQL的默认设置。在需要时,针对特定事务在会话级别进行调整,是一个更安全、更灵活的做法。
以上就是mysql如何控制事务的隔离级别的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号