事务种类
- 隐式事务
如单条 sql 语句的执行
- 显式事务
如多条 sql 语句的执行,一般谈论数据库事务时,指的是数据库的显式事务
事务特性
数据库事务具有 ACID 这4个特性
-
A —— Atomicity /ˌætəˈmɪsəti/
原子性。指事务是一个不可分割的工作单元。事务中的操作,要么全部执行,要么全部不执行。这里借用了化学学科的术语概念:原子虽然在物理状态中可以继续细分(原子由原子核和核外电子组成),但原子在化学反应中是不可再分的。
-
C —— Consistent /kənˈsɪstənt/
一致性。事务必须使数据库从一个一致性状态变换为另外一个一致性状态。比如我的账户余额是 ¥1000,你的账户余额是 ¥2000,两者可以看成一个二元组
(1000, 2000)
,这代表一个一致性状态。此时,我转账给你 ¥100,并且转账成功了,那么我的账户必定减去了 ¥100,你的账户则必定加上了 ¥100,此时(1000, 2000)
这个一致性状态变为另外一个一致性状态(900, 2100)
。 -
I —— Isolation /ˌaɪsəˈleɪʃn/
隔离性。即便有多个事务并发执行,每个事务做的操作必须与其它事务隔离。一个事务处理时的中间状态对其它事务是不可见的。
-
D —— Duration /duˈreɪʃn/
持久性。事务提交后,相关的对数据库数据的修改被持久化存储,接下来的操作和数据库故障不会对其造成其它任何影响。
数据不一致问题
多个并发执行的事务,如果操作时涉及同一条记录数据,可能会发生问题,即并发操作可能导致数据的不一致问题,这些问题包含以下3种情形:
-
脏读(Dirty Read)
读取未提交数据
执行时间 事务A 事务B 1 事务开启 2 事务开启 修改一条记录record 3 读取事务B修改的那条记录(并依据其修改后数据进一步处理,产生未提交的数据依赖关系) 4 事务回滚 5 上一步得到的数据依赖关系是有问题的(比如再次读取事务B修改的那条记录,发现和第3步读取到的数据不一致了) 6 事务提交 -
不可重复读(Non Repeatable Read)
前后多次读取,数据内容不一致(数据发生了改变或某些记录已被删除)
执行时间 事务A 事务B 1 事务开启 2 读取一条记录record 事务开启 3 修改事务A读取的那条记录 4 事务提交 5 再次读取这条记录record(此时发现这条记录跟在同一个事务内之前读取的不一致了,不能重复读) 6 事务提交 -
幻读(Phantom Read)
Phantom /ˈfæntəm/ ,一个事务按相同的查询条件重新读取以前检索过的数据,却发现其它事务插入了满足其查询条件的新数据。比如前后多次读取,数据记录总数不一致
执行时间 事务A 事务B 1 事务开启 2 查询记录总数count 3 事务开启 4 增加或删除一些记录 5 事务提交 6 再次查询记录总数count(此时count比原来的大了或小了,记录总数变了,幻读) 7 事务提交
隔离级别
为了避免(不是解决)上述可能出现的数据不一致问题,数据库系统提供了隔离级别(Isolation Level)这一机制。
SQL标准定义了4种隔离级别:
- Read Uncommitted (可以读取未提交数据)
所有事务都可以读取其他事务所做的更改,即使这些更改并没有提交
- Read Committed(可以读取已提交数据)
只有已经提交的更改,其它事务才能读取到
- Repeatable Read(可重复读)
比如一个事务,它首先进行了一次查询,这次查询得到了某一行数据的快照,之后如果该事务没有对行的数据内容进行更改,那么该事务内所有对该行数据内容的查询都将以快照的为基准,即使其它事务在该事务读取后立即更改了该行
- Serializable(可串行化)
与
Repeatable Read
差不多,只是禁用了事务的自动提交。它会在任何更新或删除时进行行锁定,并且只能在事务提交后才能进行读取
各种隔离级别分别对应可能出现的数据不一致的情况(Y标志可能出现):
隔离级别(Isolation Level ) | 脏读(Dirty Read) | 不可重复读(Non Repeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
Read Uncommitted | Y | Y | Y |
Read Committed | - | Y | Y |
Repeatable Read | - | - | Y |
Serializable | - | - | - |
隔离级别双刃剑
Read Uncommitted、Read Committed、Repeatable Read、Serializable 这4种隔离级别,按隔离级别严格程度划分,Read Uncommitted隔离级别最低,Serializable隔离级别最严,隔离级别越严格,安全性越高,越能保证数据的一致性,但对并发性能的影响也越大,所谓“鱼与熊掌不可兼得”吧。例如,对于Serializable隔离级别,由于事务是串行执行,即所有事务按照次序依次执行,所以效率会大大下降,应用程序的性能会急剧降低。实际项目使用时需要权衡“利弊”,比如MySQL中的InnoDB引擎,其为了兼顾安全与性能,默认的隔离级别就是Repeatable Read,然后采取隔离级别之外的措施来避免“幻读”的问题。