事务、ACID与隔离

什么是事务

假设A给B转100元,即:

sql 复制代码
UPDATE account SET money = money - 100 WHERE id = 1;
UPDATE account SET money = money + 100 WHERE id = 2;

正常情况:

复制代码
A:1000 B:1000
↓
A:900 B:1100

但如果执行到一半:

sql 复制代码
UPDATE account SET money = money - 100WHERE id = 1;

成功后服务器崩了,第二条没执行,结果:

复制代码
A:900
B:1000

钱凭空消失。

于是,数据库提出:

Transaction(事务)

sql 复制代码
BEGIN;

UPDATE account SET money = money - 100 WHERE id = 1;
UPDATE account SET money = money + 100 WHERE id = 2;

COMMIT;

要求:

复制代码
要么都成功要么都失败

这就是事务

ACID

复制代码
A Atomicity
C Consistency
I Isolation
D Durability

A:Atomicity(原子性)

意思:

复制代码
事务不可分割

例如转账,扣钱与加钱必须看成一个整体,不能扣钱成功加钱失败

原子性是谁实现的?

undo log

理解:

复制代码
执行成功保留
执行失败回滚

D:Durability(持久性)

意思:

复制代码
事务提交后数据不能丢

例如:

复制代码
COMMIT;

返回成功。

用户已经看见:

复制代码
转账成功

这时候断电,你不能说:

复制代码
刚刚骗你的

所以提交成功的数据必须存在。是谁保证?

答案:

redo log
复制代码
undo log 
↓
Atomicity

redo log 
↓
Durability

Consistency(一致性)

一致性:

复制代码
事务执行前后数据满足业务规则

例如银行总金额:

复制代码
A:1000 B:1000
总计2000

转账后:

复制代码
A:900 B:1100
总计仍然2000

一致性没有被破坏。

如果:

复制代码
A:900 B:1000

总计:

复制代码
1900

一致性被破坏。

一致性是:

复制代码
Atomicity+Isolation+Durability

共同保证的最终结果。

Isolation(隔离性)

假设事务A:

sql 复制代码
BEGIN;
UPDATE account SET money = 500 WHERE id = 1;

但还没提交。

此时事务B:

sql 复制代码
SELECT * FROM account WHERE id = 1;

B看到 500 还是原值?

这里就出现:

并发问题

因为多个事务同时执行,如果不控制,数据会乱。

所以,数据库提出:

Isolation

隔离性

目标:事务之间互不干扰

并发会带来什么问题

1.脏读

事务A:

sql 复制代码
BEGIN;
UPDATE user SET money = 500;

未提交。

事务B:

sql 复制代码
SELECT money;

看到 500

然后A回滚,实际值:100

B读到了不存在的数据。

这就是脏读

2.不可重复读

事务A:

sql 复制代码
BEGIN;

第一次查询:

sql 复制代码
SELECT money;

结果:100

事务B:

sql 复制代码
UPDATE money=200;
COMMIT;

事务A再次查询:

sql 复制代码
SELECT money;

结果:200

同一个事务,同一个SQL,两次结果不同,这就是不可重复读

3.幻读

事务A:

sql 复制代码
SELECT * FROM user WHERE age > 20;

结果:10条

事务B:

sql 复制代码
INSERT ...

新增一条。

事务A再次查询:

sql 复制代码
SELECT * FROM user WHERE age > 20;

结果:11条

突然多出一条,像出现幻觉。这就是幻读

隔离级别

为了解决上面问题,SQL标准提出四个隔离级别。

Read Uncommitted

允许读未提交。

问题:脏读、不可重复读、幻读全部可能发生,所以几乎不用。

Read Committed(RC)

只读已提交数据。

解决:

复制代码
脏读

但仍有:

复制代码
不可重复读
幻读

这是:

  • Oracle默认
  • PostgreSQL常用模式

Repeatable Read(RR)

解决:

复制代码
脏读
不可重复读

MySQL默认。

注意:很多数据库:RR仍有幻读,但InnoDB比较特殊

为什么 RR 解决脏读和不可重复读?

例如事务A:

sql 复制代码
BEGIN;
SELECT money FROM account WHERE id=1;

结果:100

事务B:

sql 复制代码
UPDATE account SET money=200 WHERE id=1;
COMMIT;

事务A再次查询:

sql 复制代码
SELECT money FROM account WHERE id=1;

为什么 RC 看到200,RR 还能看到100?

如果数据库真的把数据改掉了,那么:

  • 磁盘里已经是200
  • Buffer Pool里也是200

为什么事务A还能看到100?

这里就出现一个非常关键的问题:

数据库是不是只有一份数据?

答案:

复制代码
不是

这是 MVCC 的起点。

假设有一条记录:

复制代码
id=1
money=100

事务A开始:

sql 复制代码
BEGIN;

时间:

复制代码
T1

事务B开始:

sql 复制代码
BEGIN;

时间:

复制代码
T2

事务B修改:

sql 复制代码
UPDATE account 
SET money=200 
WHERE id=1;

然后提交。

现在数据库里:

复制代码
money=200

问题:事务A为什么还能看到100?

如果数据库只有:

复制代码
money=200

这一份数据,显然做不到。

所以:InnoDB干了一件很聪明的事:

不覆盖旧数据

而是:

复制代码
版本1:money=100
版本2:money=200

同时存在。

这就叫:

Multi Version

多版本。

所以,MVCC全称:

复制代码
Multi-Version Concurrency Control

多版本并发控制。

可以先把 MVCC 理解成 Git,例如Git里:

复制代码
commit1
commit2
commit3

都存在,你不会因为 commit3 出现,就看不到 commit1

MVCC也一样,数据库会保留:旧版本+新版本

然后决定:

复制代码
哪个事务能看到哪个版本

Serializable

最严格,事务串行执行。

问题:性能极差,因此几乎不用。

隔离级别 脏读 不可重复读 幻读
RU 有 有 有
RC 无 有 有
RR 无 无 理论有
Serializable 无 无 无
相关推荐
自强的小白8 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务
Wang's Blog8 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生8 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入
ao-weilai9 小时前
MySQL数据库:内置函数
android·数据库·mysql
LaughingZhu9 小时前
Product Hunt 每日热榜 | 2026-10-02
数据库·人工智能·深度学习·神经网络·搜索引擎
她的男孩9 小时前
分片上传的大文件人人可下载:文件模块 isPrivate 在合并时被抹成 false,另有 4 个静默坑
java·后端·架构
SWAGGY..9 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法
Wang's Blog9 小时前
Java框架 SpringCloud 快速入门: Eureka 服务发现与服务名调用改造
java·spring cloud·eureka
夕除9 小时前
redis--OpenResty
java·redis
Wang's Blog9 小时前
Java框架 SpringCloud 快速入门: Eureka 注册中心原理分析
java·spring cloud·eureka