首先数据库的特性就是 ACID;
- Atomicity 原子性:所有事务是一个整体,要么全部成功,要么失败。原子性由undolog日志来保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql;
- Consistency 一致性:在事务开始和结束前,要保持一致性状态。事务的最终目的,即需要数据库层面保证,又需要应用层面进行保证,并且MySQL底层通过两阶段提交事务保证了事务持久化时的一致性。
- Isolation 隔离性: 对于同一个表的操作,每个事务都是单独的,不会影响其他事务。隔离性是由MVCC来保证;
- Durability 持久性: 事务一旦提交,数据库中的数据就是永久的了。持久性由redolog来保证,mysq|修改数据的时候会在redolog中记录一份日志数据,就算数据没有保存成功,只要日志保存成功了,数据仍然不会丢失(因为当MySQL宕机或停电后,可以通过redo log最终将数据保存至磁盘中)。
什么是脏读,不可重复读,幻读?
- 脏读:事务A读取了事务B中尚未提交的数据,如果事务B回滚了,则A读取使用了错误的数据。
- 不可重复读:在事务A的两次读数据之间,由于事务B的修改导致事务A两次读到的数据可能是不一样的。这就发生了在一个事务内两次读到的数据不一样,这就被称作不可重复读;
- 幻读:指一个事务A对一个表中的数据进行了修改,而且该修改涉及到表中所有的数据行;同时另一个事务B是向表中插入一行新数据。那么经过这一番操作之后,操作事务A的用户就会发现表中还有没修改的数据行,就像发生了幻觉一样。这就被称作幻读。
级别高低:脏读 < 不可重复读 < 幻读
幻读和不可重复读的区别:
不可重复读是针对记录的update操作,只要在记录上加锁就可以避免;幻读是对记录的insert操作,想要禁止幻读就必须加上全局的写锁(在表上加写锁)。
两种事务丢失:
- 回滚丢失:事务A和B同时操作一个数据,事务B已经提交了,然后A事务回滚了,这样事务B的操作就因事务A回滚而丢失了;通过设置隔离级别Repeatable Read可以防止 。
- 覆盖丢失:A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。
我们对丢失更新问题建议采取在更新提交时做一次查询确认在更新提交,我个人觉得都是乐观的做法,区别在于悲观锁方法是通过select…for update方式,这个可能会导致其他会话的阻塞,而乐观锁方法需要多一个版本列的维护,参考链接
下面开始讲解事务的4个隔离级别(从低到高):
mysql中,innodb所提供的事务复合ACID要求,而事务通过事务日志中的redo log和undo log满足了原子性,一致性和持久性;隔离性是通过锁机制来满足的。
使用 show variables like ‘%isolation%’; 查看隔离级别:
-
读未提交(Read uncommited),就是可以读取别的事务未提交的数据,会出现脏读,不可重复读,幻读的问题。
-
读提交(Read commited),写加锁,读加锁
会出现不可重复读,幻读的问题。 -
可重复读(Read repeatable),数据库默认隔离级别,写加锁,读加锁 。
解释:事务A对表中的某条数据进行了修改,不管事务A是否提交,在事务B提交之前,这条数据对于事务B来说一直都是没有发生改变的,这条数据在事务B中是可以重复的被读取到,所以这种隔离级别称之为“可重复读”。
一个事务读的时候,我们把2次读看成整体,在读的过程中,不允许写的操作,这样就可以禁止不可重复读。就是2次读操作不允许其他事务操作。存在幻读的问题。
- 串行化(Serializable),对表级读写加锁
设置事务的隔离级别:set @@session.transaction_isolation=‘SERIALIZABLE’;
当事务处于串行化隔离级别时,是不可能出现幻读的情况的,因为如果另一个事务中对表添加了写锁,那么当前事务中是无法读取到数据的,必须等到另一个事务提交,另一个事务释放了对表的写锁,当前事务才能进行申请读锁,所以使用串行化的隔离级别不会出现幻读。
缺点:失去了并发能力。
参考链接:事务隔离级别
数据库和spring的事物的传播机制和隔离级别
隔离级别和锁的关系