文章非原创,属于对以往学习到的知识的归纳总结,文中可能存在大段截取其他文章的片段,博客属于自我学习的笔记总结会持续更新。。。
一、事务基本概述
平时,我们执行增删改的时候,无非就是从磁盘中加载数据页到缓冲池中,对缓冲页数据进行更新,同时记录下来undo log 回滚日志,和redo log 重做日志,分别应对事务提交之后事务回滚,以及mysql挂了恢复数据的场景。
我们的业务系统去访问数据库而言,他往往都是多个线程并发执行多个事务的,对于数据库而言,他会有多个事务同时执行,每个事务都会执行各种增删改查的语句,把磁盘上的数据页加载到buffer pool的缓存页里来,然后更新缓存页,记录redo log和undo log,最终提交事务或者是回滚事务。同时可能这多个事务还会同时更新和查询同一条数据
事务及其ACID属性
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
- 原子性(Atomicity) :事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
- 一致性(Consistent) :在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。
- 隔离性(Isolation) :数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
- 持久性(Durable) :事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
如果多个事务同时操作一条数据,会涉及到脏写、脏读、不可重复读、幻读,四种问题。
-
脏写
有两个事务,事务A和事务B同时在更新一条数据,事务A先把他更新为A值,事务B紧接着就把他更新为B值,此时事务A的undo log日志大概就是:更新之前这行数据的值为NULL,主键为XX。此时事务B更新完了数据的值为B,结果此时事务A突然回滚了,那么就会用他的undo log日志去回滚。此时事务A一回滚,直接就会把那行数据的值更新回之前的NULL值,导致事务B更新的值也一并没有了,对于事务B看到的场景,就是自己明明更新了,结果值却没了,这就是脏写!
本质就是事务B去修改了事务A修改过的值,但是此时事务A还没提交,所以事务A随时会回滚,导致事务B修改的值也没了,这就是脏写的定义。 -
脏读
事务A更新了一条数据为A值,还没有提交事务。事务B查询到事务A修改的值,此时事务A回滚,数据变回原来的NULL,此时事务B再次查询该条数据,就读不到事务A刚才修改的数据了。
脏读的本质是事务B读取到了事务A还没有提交的数据。 -
不可重复读
事务A读取一条数据(假定为A),事务B将该条数据修改为B 并提交了事务,此时事务A(还没有提交)再次读取该条数据时,就变成了B。如果此时事务C又对该条数据进行了更新为值C,此时事务A再次读取的时候值又变成了C。在事务A 执行期间,多次查询数据,都是同样的一个A值,他希望这个A值是他重复读取的时候一直可以读到的!但是现在是不可重复读的。 -
幻读
事务A 查询第一次查询一组数据为10个,事务B 新增了一条数据,并提交事务,事务A再次查询时 查出11条数据,事务C新增一条数据,事务A第三次查询,查到12条数据。事务A多次查询都会发现查到了一些之前没看到过的数据。这就是幻读!
事务的隔离级别
read uncommitted(读未提交),read committed(读已提交),repeatable read(可重复读),serializable(串行化)
- 读未提交
最低隔离级别 不允许发生脏写 - 读已提交(RC)
避免出现脏读,无法避免重复读与幻读 - 可重复读(RR)
能避免脏读,不可重复读,但还是会出现幻读,Mysql 默认的隔离级别 - 串行化
所有事务串行化执行,不会出现脏读 不可重复读 幻读问题。
常看当前数据库的事务隔离级别: show variables like 'tx_isolation';
要修改MySQL的默认事务隔离级别,是下面的命令,可以设置级别为不同的level,level的值可以是REPEATABLE READ,READ COMMITTED,READ UNCOMMITTED,SERIALIZABLE几种级别。
具体命令: SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;
如用Spring里的@Transactional注解来做事务这块,在@Transactional
注解里是有一个isolati