MySQL事务的实现是在引擎级别上的,现在InnoDB
存储引擎是支持事务的
事务的ACID
事务拥有四个重要的特性(ACID
):原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)、持久性(Durability
)
原子性Atomicity
事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样
注:MySQL
使用undo log
来保证事务的原子性
一致性Consistency
指事务将数据库从一种状态转变为另一种一致的的状态。事务开始前和结束后,数据库的完整性约束没有被破坏。例如工号带有唯一属性,如果经过一个修改工号的事务后,工号变的非唯一了,则表明一致性遭到了破坏。
可以理解成满足数据库完整性约束
隔离性Isolation
要求每个读写事务的对象对其他事务的操作对象能互相分离,即该事务提交前对其他事务不可见。 也可以理解为多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。这指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。例如一个用户在更新自己的个人信息的同时,是不能看到系统管理员也在更新该用户的个人信息(此时更新事务还未提交)。
注:MySQL
通过MVVC
+ 锁机制来保证事务的隔离性。
持久性Durability
事务一旦提交,则其结果就是永久性的。即使发生宕机的故障,数据库也能将数据恢复,也就是说事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。这只是从事务本身的角度来保证,排除 RDBMS(关系型数据库管理系统,例如 Oracle、MySQL 等)本身发生的故障。
注:MySQL
使用redo log
来保证事务的持久性
事务的四种隔离级别
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 | 不可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 | 不可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 | 不可能 |
可串行化(Serializable) | 不可能 | 不可能 | 不可能 | 可能 |
未提交读(Read uncommitted)
未提交读是指一个事务可以读取另一个事务还未提交的数据。这样就会产生脏读、也不能重复、也会产生幻读。即:
事务A | 事务B |
---|---|
设置事务隔离级别为READ UNCOMMITTED | 设置事务隔离级别为READ UNCOMMITTED |
开始事务A:begin; | |
insert into test values (2); | |
select * from test; | |
开始事务A:begin; | |
select * from test;// 读取到2 |
已提交读(Read committed)
已提交读是指一个事务只能读取到另一个事务提交过后的数据。但是不可以重复读、也会产生幻读。即:
事务A | 事务B |
---|---|
设置事务隔离级别为READ COMMITTED | 设置事务隔离级别为READ COMMITTED |
开始事务A:begin; | |
insert into test values (4, ‘Leon’); | |
开始事务B:begin; | |
查询数据:select * from test; (看不到id = 4的数据) | |
事务 A 提交: commit; | |
再次查询数据:select * from test; (id = 4的数据可见) | |
事务 B提交:commit; |
可重复读(Repeatable read):MySQL的默认事务隔离级别
可重复读是指在同一事务中多次读取同样记录的结果是一致的。但这有可能产生幻读问题。
幻读指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新记录,当之前的事务再次读取时该范围的记录会产生幻行。比如说:
事务A查询范围为1~10的数据,此时表里只有1~9的数据;而事务B也开始准备事务查询,自然是只能看到1~9的数据;当事务A插入id为10的数据,并提交事务后;事务B还是看不到id=10的数据,此时事务B如果尝试插入id=10的数据就有可能报错。
事务A | 事务B |
---|---|
设置事务隔离级别为REPEATABLE READ | 设置事务隔离级别为REPEATABLE READ |
开始事务A:begin; | |
查询:select * from test; | |
开始事务B:begin; | |
查询数据:select * from test; (看不到id = 10的数据) | |
insert into test(id,username) values(10,’zhangsan’); | |
事务 A 提交: commit; | |
再次查询数据:select * from test; (看不到id = 10的数据) |
MySQL的InnoDB是如何解决幻读问题
MySQL
使用next-key lock
+ MVVC
解决幻读问题:https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html
可串行化(Serializable)
可串行化是指事务与其他事务是串行执行的,避免了幻读问题。
事务A | 事务B |
---|---|
设置事务隔离级别为SERIALIZABLE | 设置事务隔离级别为SERIALIZABLE |
开始事务A:begin; | |
insert into test(id, usename) values(15, ‘LaoZhang’); | |
开始事务B:begin; | |
查询数据:select * from test; (看不到任何数据,一直等待) | |
事务 A 提交: commit; | 立即返回数据 |
相关命令
https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-model.html