事务
什么是事务
MySQL的常用的存储引擎有InnoDB,MyISAM。InnoDB支持事务,MyISAM不支持事务。那事务是什么呢?
事务就是一组操作,这一组操作我们希望它是一个不可分割的整体,这组操作要么全部都执行,要么全部都不执行。在很多环境中都有事务,数据库中有事务,Spring中也有事务。
那数据库的什么样的场景下,会用到事务呢?
- 最典型的莫过于转账了。
比如说,我有一百万,我要转给你一百万,那在数据库中会涉及到以下几个步骤:
假如说当执行了前三个步骤后,服务器断电了,后面的三个步骤没有执行,那这时候我的100万没有了,你的账号上也没有加上100万,这100万就这么没了,是不是很离谱,那所以为了避免这种情况发生,我们希望通过事务让上面6个操作,作为一个整体,要么6个操作都执行,要么6个操作都没执行。不过在这里并不是6个操作真的都没执行,意思就是:假如执行了前三个步骤之后,真的断电了,那MySQL在执行前三个步骤时可以将执行了哪些操作记录到日志中,后续当通电后,可以根据日志中记录的信息,执行一些操作来抵消前三个操作(说白了就是给你的账号上加100万,让你的账号余额不变)。根据日志回退的这个过程就叫做回滚。
以上的案例解释了事务的用途,最基本的思想是让一组操作作为一个整体,要么都执行,要么都不执行,不能出现执行了一部分的情况。
那事务具体是怎么使用的呢?
- 就拿上面转账操作来说,我们在开始转账前,先开启事务,然后等转账的六个操作全部成功执行后,再提交事务,如果中途发生错误,那该事务期间对数据库所做的所有修改都会回滚到没有发生修改之前的状态。
事务四大特性
事务的四大特性:ACID
- 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成。如果这些操作执行的中间过程中发生错误或者中断,那就回滚到事务开始前的状态。即事务是一个不可分割的原子。没有中间状态。
- 一致性:一致性是指事务发生前和发生后,数据库保持一致性状态。拿转账来说,张三和李四总共有五千元,不管中间转账几次,每次转了多少钱,最后总金额还得5000元
- 隔离性:一个MySQL服务器可以连接多个客户端,多个客户端的事务操作是在不同线程中执行的,线程可以并行执行,也就意味着事务可以并行执行。隔离性就可以保证多个事务并行执行时对同一份数据读写,多个事务之间互相不会产生干扰。
- 持久性:事务执行过程中对数据的修改是持久的,即事务一旦提交,对数据库中数据的修改是持久性的
持久性是通过:重做日志(redolog)来保证的,
原子性是通过:回滚日志(undolog)来保证的,
隔离性是通过:MVCC来保证的或锁机制来保证的,
一致性是通过持久性+原子性+隔离性来保证的。
并行事务
并行事务产生的问题
MySQL服务器是允许多个客户端连接的,而服务器会为每个客户端分配一个线程,来执行用户的操作。线程可以并行执行,那么事务自然也就可以并行执行。因为事务是在线程中的
并行事务会产生什么问题?
当多个数据对同一份数据产生读写操作的时候,就有可能会产生诸如以下问题:脏读,不可重复读,幻读。
1️⃣脏读:
事务A在执行过程中对数据进行了修改,在事务A未提交之前,事务B读取了对应的数据,此时事务B的读取行为就是脏读。因为事务B读到的数据可能一会事务A就修改了。那事务B就是读到了一个脏数据。(说的简单点就是:一个事务读到了另一个未提交事务修改过的数据)事务A读取到了事务B中间状态的数据
事务A和事务B同时执行,事务A先从账号中读取余额为100万,然后随即更新了余额为200万,此时事务A还没有提交,然后事务B这时候读取了余额200万。因为事务A没有提交,所以事务A有可能发生回滚,假如发生了回滚,将余额更新为100万。那么就相当于事务B读取了一个过期的数据。这个现象称为脏读。
2️⃣不可重复读:
在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一致的情况,就意味着发生了不可重复读现象。事务A并没有读取事务B中间状态的数据,而是事务A的两次查询中插入了事务B的修改,这一点要和脏读区分开,脏读是读到了另一个事务的中间状态的值。事务A在读取的时候,事务B要么是未开启,要么是已提交
事务A在进行多次读取数据,在事务A多次读取数据的过程中,事务B修改了数据,导致事务A前后读取的数据不一致,就称作不可重复读现象。
3️⃣幻读:
在一个事务内,多次查询符合某个查询条件的记录,如果出现了前后读取的记录数不一致(或者说结果集不一致),则代表产生了幻读现象
事务B读取余额大于100万的数量为5条,然后事务A插入一条余额大于100万的账号,然后提交事务,等事务B再读取的时候,余额大于100万的数量变为了6条。
⭕️脏读是读到了另一事务中间状态值,不可重复读是前后两次读到的数据不一致(侧重点是数据),幻读是前后两次读到的结果集不一致(侧重点是结果的数量)
事务的隔离级别
针对事务并发时,可能产生的脏读,不可重复读,幻读等现象,SQL标准提出了四个隔离级别,来解决上述现象。分别是读未提交,读提交,可重复读,串行化。
- 读未提交是指一个事务还没有提交时,他做的变更就能被其他事务看到。每次读到的都是最新的数据。
- 读提交是指一个事务只有提交之后,他做的变更才能被其他事务看到。
- 可重复读是指指事务在事务执行过程中看到的数据跟这个事物刚启动时看到的数据是保持一致的。
- 串行化会对事务加上读写锁,当多个事务对同一条记录发生了读写操作,如果发生了读写冲突,后访问的事务必须等前一个事务执行完才能继续执行。
在不同隔离级别下,可能会发生的现象: