什么是事务?
1、事务是一组sql语句,事务中的所有sql语句被当做一个操作单元,要么全部执行成功,要么全部执行失败,事务内的sql语句被当做一个整体,被当做一个原子进行操作。
事务的特性?
1、原子性,整个事务中的所有操作要么全部执行成功,要么全部执行失败后回滚到最初状态。
2、一致性,数据库总是从一个一致性状态转为另一个一致性状态。一致性状态包含数据库完整性约束,系统的状态反应数据库本应描述的现实世界的真实状态。
3、隔离性,并发执行的事务相互之间不会影响。
4、持久性,事务一旦提交,事务所做的修改将会永久保存。
事务默认属性
1、默认自动提交on,即每执行一条sql语句就会把这条sql当做一个单语句事务进行提交,在事务中执行时自动提交失效,提交由commit执行决定
2、默认隔离级别repeatable-read(可重复读)
数据库常见的并发异常
1、脏写(第一类丢失更新),是指事务回滚了其他事务对数据项的已提交修改
2、丢失更新(第二类丢失更新),指事务覆盖了其他事务对数据的已提交修改,导致这些修改好像丢失了一样
3、脏读,是指一个事务读取了另一个事务未提交的数据
4、不可重复读,是指一个事务对同一数据的读取结果前后不一致(脏读和不可重复读的区别在于:前者读取的是事务未提交的脏数据,后者读取的是事务已经提交的数据,只不过因为数据被其他事务修改过导致前后两次读取的结果不一样,由其他事务的数据修改引起)
5、幻读,是指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致(幻读和不可重复读的区别在于,不可重复读是针对确定的某一行数据而言,而幻读是针对不确定的多行数据。因而幻读通常出现在带有查询条件的范围查询中,由其他事务的数据插入或者删除引起)
事务的隔离级别(基于锁实现)
RUC(读未提交)
读不加锁,写加排他锁,并到事务结束之后释放。(因此不存在数据库中的丢失更新问题)
RC(读已提交):不可重复读,幻读
事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。(读取时加共享锁(注意读取完锁就会被释放,不会到等事务结束),因此避免了脏读问题,但读完后其他事务还是可以修改数据,因此不能解决不可重复读的问题)
RR(可重复读):幻读
事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
序列化
事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
事务的隔离级别(基于锁+MVCC)
MVCC的引入主要是解决了读加锁的问题,即读写冲突的问题。
RC(读已提交):不可重复读,幻读
RC级别中,基于MVCC实现,读默认不需要加锁,解决了脏读问题,但由于每一次读都会生成新的ReadView,因此无法解决不可重复问题和幻读问题。
RR(可重复读):
RR级别中,基于MVCC实现,读默认不需要加锁,解决了脏读,不可重复读,以及部分幻读问题,事务只生成一次ReadView,因此在同一事务过程是可重复读的,并且部分场景没有幻读。
注意:MVCC只应用在RC和RR两种隔离级别中,因此这里只看RC和RR两种隔离级别
注意:丢失更新问题,我这里把它分为业务中的丢失更新问题和数据库中的丢失更新
业务中的丢失更新问题,一般是如下场景:
事务A,读取了1记录,余额为100,
事务B,读取了1记录,余额为100,
事务A,执行一系列逻辑操作后,将1记录的余额更新为90,update a set balance=90 where id=1
事务A提交事务
事务B,执行一系列逻辑操作后,将1记录的余额更新为110,update a set balance=110 where id=1
事务B提交事务
若要解决类似场景的问题,必须在读取1记录的时候通过排他锁的方式,即select * for update,这样直到A事务执行完毕,B事务才能读取1记录继续执行。
数据库中的丢失更新问题,一般是如下场景:
事务A,update a set balance=100 where id=1
事务B,update b set money=100 where id=1
若update操作没有排他锁,则事务B的修改会导致事务A的修改丢失,但在RC和RR两种隔离级别中,update等当前读都是有锁的,因此RC和RR不存在数据库中的丢失更新问题。