什么是事务?事务常见的并发问题及含义

(1)问题分析:

考官主要相对于事务方面的考核,如什么是事务,事务都有哪些特性;事务并发问题如何解决;事务的隔离级别分别可以解决什么问题;jdbc,spring如何实现事务控制;事务的应用场景等。

(2)核心答案讲解:

事务是指逻辑上的一组操作,组成这组操作的一系列操作要么全部成功,要么一个都不做。因此,事务的结束有两种,当事务中的所有操作全部成功执行时,事务提交。如果其中一个操作失败,将发生回滚操作,撤消到事务开始时的状态。
事务常见并发问题:
丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
脏读:一个事务读到另一个事务未提交的更新数据。
幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
不可重复读:一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据。

(3)问题扩展:

事务的特性:
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存
事务的隔离级别及可以解决什么问题:
Serializable (串行化) 8 可避免脏读、不可重复读、幻读的发生。但是效率最低
Repeatable read (可重复读) 4 可避免脏读、不可重复读的发生。(最常用的MySQL默认的级别就是4)
Read committed (读已提交) 2 可避免脏读的发生(Oracle默认)。
Read uncommitted (读未提交) 1 最低级别,任何情况都无法保证,但效率最高,最不安全。

怎样实现事务的隔离呢?
隔离机制的实现必须使用锁。
锁的基本原理
a.当一个事务访问某个数据库资源时,如果执行的是select语句,必须为资源加上共享锁,如果执行的是insert,update,delete语句,必须为资源加上排他锁,这些锁锁定正在被操作的资源。
b.当第二个事务也要反问相同的资源时,如果执行的select语句,那么也必须为资源加上共享锁;如果执行的是insert,update,或delete语句,也必须为资源加上排他锁。但此时第二个事务并非就立即能为资源加上锁,当第一个事务为资源加的是共享锁时,第二个事务能够为资源加上共享锁,但当第一个事务为资源加的是排他锁时,第二个事务必须等待第一个事务结束,才能为资源加上排他锁。
1)共享锁(s锁)
共享锁用于读取数据操作,它允许其他事务同时读取锁定的资源,但不允许其他事务更新它。
2)排他锁(X锁)
排他锁用于修改数据的场合,他锁定的资源,其他事务部能读取也不能修改。
3)更新锁(U锁)
更新锁在更新操作初始化截断用来锁定可能要被修改的资源,从而避免使用共享锁造成的死锁现象。

表锁(table lock)
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。表锁会锁定整张表。一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。当没有写锁时,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的。

行级锁(row lock)
行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。在InnoDB和XtraDB,以及其他一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。

一次封锁or两段锁?
因为有大量的并发访问,为了预防死锁,一般应用中推荐使用一次封锁法,就是在方法的开始阶段,已经预先知道会用到哪些数据,然后全部锁住,在方法运行之后,再全部解锁。这种方式可以有效的避免循环死锁,但在数据库中却不适用,因为在事务开始阶段,数据库并不知道会用到哪些数据。
数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)

加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁(共享锁,其它事务可以继续加共享锁,但不能加排它锁),在进行写操作之前要申请并获得X锁(排它锁,其它事务不能再获得任何锁)。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。

解锁阶段:当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。

悲观锁
悲观锁是指假设并发更新冲突会发生,所以不管冲突是否真的发生,都会使用锁机制。悲观锁会完成以下功能:锁住读取的记录,防止其它事务读取和更新这些记录。其它事务会一直阻塞,直到这个事务结束.悲观锁是在使用了数据库的事务隔离功能的基础上,独享占用的资源,以此保证读取数据一致性,避免修改丢失。悲观锁可以使用Repeatable Read事务,它完全满足悲观锁的要求。

乐观锁
乐观锁不会锁住任何东西,也就是说,它不依赖数据库的事务机制,乐观锁完全是应用系统层面的东西。如果使用乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

(4)结合项目中使用:

spring 的声明式事务:使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。

点击此处
隐藏目录