数据库事务的基础知识
Fenix Lv1

事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
可以简单的理解为:对数据的一次操作就是一个事务。

事务的特性

事务具备四个特性:即原子性(Atomicity)、一致性(consistency)、隔离性(Isolation)、持久性(Durability),简称ACID。

  • 原子性: 事务作为一个整体被执行,其中对数据库的操作要么全部执行,要不都不执行(有始有终)。
  • 一致性: 事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束(表里如一)。
  • 隔离性: 多个事务并发执行时,一个事务的执行不应该影响其他事务的执行(不多管闲事)。
  • 持久性: 被提交的事务对数据库的修改应该永久保存在数据库中(一诺千金)。

脏读、不可重复读、幻读

不同隔离级别与读问题的对照关系

隔离级别 脏读 不可重复读 幻读
读未提交(Read Uncommitted) 可能 可能 可能
读已提交(Read Committed) 不可能 可能 可能
可重复读(Repeatable Read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能

读未提交(Read Uncommitted): 所有读问题都可能发生,一般不会使用这种隔离级别。
读已提交(Read Committed): 只能避免脏读发生,Oracle的默认隔离级别。
可重复读(Repeatable Read): 能够避免脏读和不可重复读发生,MySQL中InnoDB引擎默认的隔离级别。
可串行化(Serializable): 可以解决所有读问题,但由于是串行执行,性能相当一般,所以通常也不会使用。

在MySQL中,可重复读级别就解决了幻读的问题。
假设有一张表:

1
2
3
4
5
6
CREATE TABLE `K_V`
(
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键 id',
`value` int NOT NULL DUFAULT '0' COMMENT '值',
PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

表中有如下数据:

1
2
3
4
5
6
+------+------+
| id | value|
+------+------+
| 1 | 100 |
| 2 | 100 |
| 3 | 100 |

设置数据为手动提交事务:

1
set autocommit=0;

脏读(Dirty Read)

脏读是指一个事务(A)读到了另一个事务(B)未提交的数据。
比如,事务A读到事务B并未提交的数据,恰好事务B因某些因素而导致了事务回滚,那么刚刚事务A就相当于读到了实际上并不存在的数据,这就是脏读。

1
set session transaction isolation level read uncommitted;
事务A 事务B
begin;
select value from k_v where id = 1;(value=100)
begin;
update k_v set value = value + 100 where id=1;(value = 200)
select value from k_v where id = 1;(value = 200)
rollback;(value = 100)

不可重复读(Unrepeatable Read)

不可重复读是指在一个事务内对同一条记录(可以理解为根据同一个id查询)进行多次查询的结果却不一致。

比如,事务A查询了一次账户余额。之后,事务B在该账户中扣除了一笔钱(比如自动还款)并提交了事务。这时事务A再次查询账户余额,就会发现余额变了,这就属于不可重复读。

1
set session transaction isolation level read committed;
事务A 事务B
begin;
begin;
update k_v set value = value + 100 where id = 1;(value = 200)
select value from k_v where id = 1;(value = 100)
commit;(value = 100)
select value from k_v where id = 1;(value = 200)

幻读(Phantom Read)

幻读是指在同一个事务内进行多次操作之间,产生了新的数据或删除了已有数据,并对后续的操作造成了影响。

比如,事务A统计了id大于1的数据。之后,事务B插入了一条id为4的数据并提交了事务。这时事务A再次统计id大于1的数据,就会发现多了一条,这就产生了幻读。

事务A 事务B
begin;
select count(*) form k_v where id>1;(两条)
begin;
insert into k_v values(4,100);
select count(*) from k_v where id>1;(两条)
commit;
select count(*) from k_v where id>1;(三条)

区别
脏读指的是一个事务读到了其他事务未提交的数据。
不可重复读指的是一个事务中多次读到同一条(多条)数据发生了变化,重点在于表里已经存在的数据被其他事务修改了(update)。
幻读指的是一个事务被其他事务插入或删除的数据所影响,重点在于事务开始后,其他事务插入或删除了数据(insert/delete)。

脏读、不可重复读、幻读是在并发事务的情况下才发生的。为了解决这些问题,数据库引入了隔离级别,并且不同的隔离级别可以解决不同的问题。

由 Hexo 驱动 & 主题 Keep