数据库基础——事务隔离级别

数据库基础——事务隔离级别

事务隔离级别是数据库系统中非常重要的概念,它决定了事务之间如何相互"看到"对方的数据变化。我将用更通俗易懂的方式解释这四种隔离级别,特别是你感到困惑的"读已提交"和"可重复读"级别。

一、四种隔离级别概述

我们可以把隔离级别想象成教室之间的隔墙厚度:

隔离级别隔墙厚度问题解决存在问题
读未提交透明玻璃脏读
读已提交薄墙解决脏读不可重复读
可重复读厚墙解决脏读、不可重复读幻读
串行化实心墙解决所有问题性能差

二、各级别详细解释

1. 读未提交(Read Uncommitted) - 透明玻璃

特点:事务可以读取其他事务未提交的修改。

问题

  • 脏读(Dirty Read):读到别人还没确认的数据,如果对方回滚,你读到的就是"脏数据"。

示例

-- 事务A
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 未提交

-- 事务B(读未提交隔离级别)
SELECT balance FROM accounts WHERE id = 1; -- 能看到A未提交的修改

2. 读已提交(Read Committed) - 薄墙

特点:只能读取已提交的数据。

解决了:脏读问题。

新问题

  • 不可重复读(Non-repeatable Read):在同一事务中,两次读取同一数据可能结果不同。

示例

-- 事务A第一次查询
SELECT balance FROM accounts WHERE id = 1; -- 返回1000

-- 事务B提交了修改
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT;

-- 事务A第二次查询(同一事务内)
SELECT balance FROM accounts WHERE id = 1; -- 返回900(与第一次不同)

现实比喻:你在看一本书,别人可以在你阅读过程中修改书的内容,你每次翻页可能看到不同的内容。

3. 可重复读(Repeatable Read) - 厚墙

特点:事务开始时建立"快照",整个事务期间都读取这个快照。

解决了:脏读和不可重复读问题。

新问题

  • 幻读(Phantom Read):虽然同一数据不会变,但可能出现新数据行。

示例

-- 事务A开始
SELECT COUNT(*) FROM accounts WHERE type = 'saving'; -- 返回5

-- 事务B插入并提交
INSERT INTO accounts(id, type, balance) VALUES(11, 'saving', 500);
COMMIT;

-- 事务A再次查询
SELECT COUNT(*) FROM accounts WHERE type = 'saving'; -- 仍返回5(可重复读)

-- 但事务A执行更新时会发现影响行数超出预期
UPDATE accounts SET balance = balance + 10 WHERE type = 'saving';
-- 可能影响6行而不是预期的5行

现实比喻:你在看一本书的拍照快照,书的内容实际可能已经变化,但你看到的始终是拍照时的内容。不过如果有人往书里夹新页,你可能会发现。

4. 串行化(Serializable) - 实心墙

特点:完全隔离,事务串行执行。

解决了:所有问题(脏读、不可重复读、幻读)。

代价:性能最低,并发性差。

三、重点对比:读已提交 vs 可重复读

读已提交(Read Committed)

  • 数据真实性:总是读取最新已提交的数据
  • 问题:同一事务内多次读取同一数据可能结果不同
  • 适用场景:需要实时数据的系统(如银行ATM机查询余额)

可重复读(Repeatable Read)

  • 数据一致性:事务开始时建立数据快照,整个事务期间读取相同数据
  • 问题:可能读到"过期"数据,存在幻读
  • 适用场景:需要数据一致性的报表生成、统计分析

四、如何选择隔离级别

  1. 优先考虑可重复读:MySQL默认级别,平衡了性能和数据一致性
  2. 需要高实时性:考虑读已提交(Oracle默认)
  3. 严格要求数据一致性:考虑串行化(但性能影响大)
  4. 几乎不使用:读未提交(仅用于特殊场景)

五、实际案例分析

银行转账场景

-- 使用可重复读隔离级别
START TRANSACTION;

-- 检查账户A余额(假设返回1000)
SELECT balance FROM accounts WHERE id = 1;

-- 在此期间,另一个事务从账户A转出100并提交
-- 但当前事务仍看到余额为1000

-- 尝试转出800
UPDATE accounts SET balance = balance - 800 WHERE id = 1;
-- 实际会检查真实余额,如果不足会失败,防止透支
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值