MySQL在执行sql语句的时候,会遇到一些场景,一系列操作必须全部执行,而不能仅执行一部分。例如,一个转账操作:
-- 从id=1的账户给id=2的账户转账100元
-- 第一步:将id=1的A账户余额减去100
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 第二步:将id=2的B账户余额加上100
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
这两条SQL语句必须全部执行,或者,由于某些原因,如果第一条语句成功,第二条语句失败,就必须全部撤销。这种把多条语句作为一个整体进行操作的功能,被称为数据库事务。
数据库事务可以确保该事务范围内的所有操作都可以全部成功或者全部失败。如果事务失败,那么效果就和没有执行这些SQL一样,不会对数据库数据有任何改动。
所以,数据库事务具有ACID这4个特性:
Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。
事务并发多个事务,同时提交的话,如果串行执行,则会造成事务的响应时间过长,用户体验极差,所以
同时提交的事务,是并发执行的。
对多个并发事务如果不加以控制,则可能会造成一下四种问题
脏读
事务读取了另一个事务未提交的修改,如下图,事务T2读取了事务T1未提交的修改Write(x)
,
之后事务T1进行了回滚操作,导致事务T2读取了一个不存在的数据,所以称为脏读
不可重复读
由于其他事务对记录进行了修改,导致同一个事务,对同一条记录,读出两个不同的结果
如下图,事务T2对xxx两次读取的结果,由于事务T1的写操作,导致不同。
幻读
在一个事务读的过程中,另外一个事务可能插入了新数据记录,影响了该事务读的结果。
如下图,事务T2执行两次查询id为1的记录
的操作,但是由于事务T1执行了插入id=1的元组
的操作,导致两次查询结果不一致。
丢失更新
一个事务的更新被另一个事务覆盖,如下图,事务T2的写更新,被T1的写更新覆盖
事务的隔离级别并发操作会带来数据的不一致性,包括脏读、不可重复读、幻读等。数据库系统提供了隔离级别来让我们有针对性地选择事务的隔离级别,避免数据不一致的问题。mysql innodb
主要有一下四种隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable) | 不可能 | 不可能 | 不可能 |
事务并发控制比较复杂,先占个坑,之后更新。
参考廖雪峰对事务的介绍