MySQL 事务介绍

发布时间: 更新时间: 总字数:1952 阅读时间:4m 作者: IP上海 分享 网址

数据库引入 事务隔离(Transaction Isolation) 的核心目的是为了解决:多事务并发执行时可能引发的数据不一致性问题

介绍

  • 事务保证一组原子性的操作,要么全部成功,要么全部失败。四种常见的隔离级别:
    • 未提交读(Read UnCommitted) 事务中的修改,即使没提交对其他事务也是可见的
      • 事务可能读取未提交的数据,造成脏读。
    • 提交读(Read Committed) 一个事务开始时,只能看见已提交的事务所做的修改
      • 事务未提交之前,所做的修改对其他事务是不可见的
      • 也叫不可重复读,同一个事务多次读取同样记录可能不同
    • 可重复读(RepeatTable Read) 同一个事务中多次读取同样的记录结果时结果相同
    • 可串行化(Serializable) 最高隔离级别,强制事务串行执行
  • SDK 实现

事务隔离要解决的三大核心问题

如果事务之间完全不受隔离,多个事务同时对同一数据执行读写操作,可能导致脏读不可重复读幻读等问题,破坏数据的完整性和可靠性。

脏读(Dirty Read)

  • 场景:事务 A 修改了某数据但未提交,事务 B 读取到了这个未提交的修改
  • 风险:如果事务 A 最终回滚,事务 B 读到的数据是无效的(脏数据
  • 示例
-- 事务 A 修改余额(未提交)
UPDATE accounts SET balance = 500 WHERE id = 1;

-- 事务 B 读取到未提交的余额(500)
SELECT balance FROM accounts WHERE id = 1;

不可重复读(Non-Repeatable Read)

  • 场景:事务 A 多次读取同一数据,事务 B 在期间修改并提交了该数据,导致事务 A 两次读取结果不一致
  • 风险:同一事务内无法保证数据的一致性
  • 示例
-- 事务 A 第一次读取余额(100)
SELECT balance FROM accounts WHERE id = 1;

-- 事务 B 修改余额并提交
UPDATE accounts SET balance = 200 WHERE id = 1;

-- 事务 A 再次读取余额(200)
SELECT balance FROM accounts WHERE id = 1;

幻读(Phantom Read)

  • 场景:事务 A 按条件查询一批数据,事务 B 在期间插入或删除符合该条件的记录并提交,导致事务 A 两次查询结果的记录数不一致
  • 风险:范围查询的结果集不可靠
  • 示例
-- 事务 A 查询余额 > 100 的账户(返回 2 条记录)
SELECT * FROM accounts WHERE balance > 100;

-- 事务 B 插入一条余额为 200 的记录并提交
INSERT INTO accounts (id, balance) VALUES (3, 200);

-- 事务 A 再次查询,返回 3 条记录
SELECT * FROM accounts WHERE balance > 100;

为什么需要不同的事务隔离级别?

不同的业务场景对数据一致性和性能的要求不同。数据库通过提供可配置的隔离级别,允许开发者在以下两方面进行权衡:

  1. 数据一致性:隔离级别越高,数据越一致(如 Serializable 完全避免并发问题)
  2. 并发性能:隔离级别越高,锁的粒度越大,并发吞吐量越低
隔离级别 脏读 不可重复读 幻读 性能
Read Uncommitted
Read Committed 较高
Repeatable Read 中等
Serializable

事务隔离的实现机制

数据库通过以下技术实现事务隔离:

  • 锁机制(Locking): 如行锁、表锁、共享锁(S Lock)、排他锁(X Lock)

    • 悲观锁:默认认为冲突会发生,提前加锁(如 SELECT ... FOR UPDATE
    • 乐观锁:通过版本号(Version)或时间戳(Timestamp)检测冲突
  • 多版本并发控制(MVCC): 为数据维护多个版本,事务读取特定时间点的快照(Snapshot),而非实时数据

    • 实现方式:PostgreSQL 和 MySQL 的 InnoDB 引擎均使用 MVCC
    • 优点:读操作不会阻塞写操作,提高并发性能

为什么数据库不默认使用最高隔离级别?

最高隔离级别(如 Serializable)虽然能彻底解决并发问题,但会导致:

  1. 性能下降:大量锁竞争,事务串行执行,吞吐量急剧降低
  2. 死锁风险增加:复杂的锁机制更容易引发死锁
  3. 适用场景有限:只有对数据一致性要求极高的场景(如金融交易)才需要

实际开发中的选择建议

  1. 常规业务场景: 使用默认隔离级别(如 MySQL 的 Repeatable Read、PostgreSQL 的 Read Committed)。

  2. 高一致性场景(如支付、库存扣减): 显式指定 Repeatable ReadSerializable,结合悲观锁(SELECT ... FOR UPDATE)。

  3. 高并发读场景: 使用 MVCC 支持的隔离级别(如 Read Committed)或乐观锁。

  4. MySQL 指定事务级别示例

dba> set tx_isolation='read-committed';
dba> BEGIN;
dba> update t1 set b2=4 where b2=2;
dba> commit;

F&Q

MySQL 同一个事物中,先插入后查询可以获取到最新的数据么

这依赖于 MySQL 的事务隔离级别设置。默认的事务隔离级别是可重复读(REPEATABLE READ)。在这个隔离级别下,在同一个事务中的查询可以看到事务中先前插入的数据。即使使用读提交(READ COMMITTED)隔离级别,该行为依然成立。

以下是一个简单的例子:

START TRANSACTION;

INSERT INTO 表名 (1, 2) VALUES (1, 2);

SELECT * FROM 表名 WHERE 某条件;  -- 这将返回你刚插入的数据(如果符合条件)

COMMIT;

确保事务中的所有操作都最终执行COMMIT来提交事务,以保证数据被永久保存到数据库中。否则,事务会在回滚(ROLLBACK)时撤销所有未提交的更改。

总结

事务隔离的本质是在并发性能和数据一致性之间寻找平衡。数据库通过隔离级别和并发控制机制(锁、MVCC),既允许高效并发操作,又避免了数据混乱。理解隔离级别及其解决的问题,是设计高可靠分布式系统的关键基础。

参考

  1. https://zh.wikipedia.org/wiki/%E4%BA%8B%E5%8B%99%E9%9A%94%E9%9B%A2
  2. https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数