一文看懂MySQL事务隔离

1、什么是事务?

事务可以看作是由一组操作指令构成的执行单位,不可分割。简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。MySQL中的InnoDB引擎支持事务。

2、事务的四个特性

原子性(Atomicity):事务内的所有指令,要么全部执行成功,要么全部执行失败。只要其中一个指令执行失败,数据就要回滚到开始执行第一条事务指令前的数据状态。

一致性(Consistency):事务的执行使数据从一个一致性状态转换为另一个一致性状态,对于整个数据的完整性保持稳定。一致性与原子性密切相关。例如,当数据库系统在运行时发生故障,有些事务尚未完成就被迫中断,这些执行到一半的事务只对数据做了部分修改,而非全部修改,这时产生的数据就是不一致的状态。再比如,A给B转账1000元,操作①是A的账户减少1000,操作②是B的账户增加1000,如果操作①完成后,事务中断,则数据就产生了不一致的状态。

隔离性(Isolation):当多个用户并发访问数据库时,比如操作同一张表,数据库为每个用户开启事务,不能被其他事务的操作干扰,多个并发事务之间要相互隔离(相互感觉不到对方在并发执行)。

持久性(Durability):当事务正确完成后,对于数据的改变是永久性的。

3、事务的隔离级别

读未提交(read uncommitted):一个事务还没提交时,它做的变更可以被其他事务看到。

读提交(read committed):一个事务提交之后,它做的变更才可以被其他事务看到。查询时,只承认在此语句启动前就已经提交完成的数据。

可重复读(repeatable read):一个事务在刚启动时看到的数据和它在后续执行过程中看到的数据是一致的,即事务查询过程中只承认事务启动前就已经提交完成的数据。事务提交之后,它做的变更才可以被其他事务看到。

串行化(serializable):对于同一行记录,“写"会加"写锁”,“读"会加"读锁”。遇到锁冲突时,必须等待持有锁的事务释放锁,才可以执行。

4、数据库的并发问题

脏读:对于两个事务 T1和T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后,若 T2 回滚,T1读取的内容就是临时且无效的。“读提交”和“可重复读”隔离级别可解决该问题。

不可重复读:对于两个事务T1和T2,T1 读取了一个字段,然后 T2 更新了该字段。之后,T1再次读取同一个字段,值就不同了。“可重复读”隔离级别可解决该问题。

幻读:对于两个事务T1和T2,T1 从一个表中读取了一个字段,然后 T2 在该表中插入了一些新的行。之后,如果 T1 再次读取同一个表, 就会多出几行。“串行化”隔离级别可解决该问题。

5、长事务有什么影响,如何避免?

长事务的影响:①长事务会使系统中存在很老的事务视图,并且事务中的操作可能会随时访问数据库中的任何数据,所以在这个事务提交前,对于该事务可能用到的数据,其回滚记录必须全部保留,将会占用很大空间。②长事务会占用锁资源,影响并发效率。
解决办法:①将参数autocommit设置为1,当它是0的时候,会关闭线程的自动提交,当只执行一个select语句,事务就启动了,但是不会自动提交,直到显示地执行commit或者rollback或者断开连接。②去掉不必要的只读事务,减少事务长度。

6、多版本并发控制MVCC实现事务视图

《高性能MySQL》中提到:“可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。MVCC的实现,是通过保存数据在某个时间点的快照来实现的。”

那么"快照"又是怎么实现的?
InnoDB中每个事务都有唯一的事务ID(transaction_id),事务开始时向系统申请获得,按申请顺序严格递增。表中的每行数据都有多个版本,每个版本都有一个版本号,称为row trx_id。

update:当一个事务更新某一行数据时,这一行会产生一个新的数据版本,并且把这个事务的transaction_id赋值给这一行的row trx_id。同时,保存当前这个事务的id作为原来行的行删除标识。

select:当一个事务要查找某一行数据时,要确保这一行的数据版本row trx_id早于此事务的事务id,如果row trx_id比事务id大,则进行回滚计算(通过回滚日志undo log,可以将新的数据版本回退,从而计算出旧的数据版本),直到row trx_id比事务id小,则表明此版本的数据对当前事务可见。另外,行的删除版本要么未定义,要么大于当前事务的id,这可以确保事务读取到的行,在事务开始之前并未被删除。

insert:当一个事务新插入了一行数据,则把事务id赋值给这行的row trx_id。

delete:当一个事务删除某一行数据,要为被删除的行保存此事务id作为行的删除标识。

7、什么是当前读?

update数据时,都是先读数据,再作更新操作,而这个读,必须读数据的当前值(最新值),称为"当前读"。此外,select语句如果加锁,也是当前读,比如select …… lock in share mode(S锁,共享锁) 或者select …… for update(X锁,排他锁)。

猜你喜欢

转载自blog.csdn.net/Longstar_L/article/details/106933203