上次阿里面试问到了事务,答的一塌糊涂~
虽然看过很多遍,仍然记不住,还是写篇博客,没事经常翻翻吧。
一、什么是事务
首先了解下什么是事务,或者说为什么要引入事务的概念
事务:数据库中的一个执行单元,被认为是一种连贯的、可靠的、与其他事务独立的方式。事务通常代表数据库中的任何改变。(https://en.wikipedia.org/wiki/Database_transaction)
事务有两个主要目的:
1、为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
2、当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
(https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1)
二、事务的四个特性(ACID)
原子性(Atomicity):事务中的一系列操作要么全部执行,要么全都不执行;事务作为一个整体被执行
一致性(Consistency):保证数据库中的数据从一个一致状态转移到另一个一致状态(一致状态指满足完整性约束,不违背对数据的要求,如constraints, cascades, triggers)
隔离性(Isolation):当前执行的事务感觉不到其他事务的存在,就像是只有自己在修改数据库一样。事务就像是串行执行一样。
持久性(Durability):保证只要事务被提交,对数据的修改就会永久保存下来,无论是遇到服务器当机还是其他原因,都不能导致数据丢失。
三、如何实现这四个特性
数据库系统中利用日志来保证原子性、一致性和持久性,通过锁机制来保证隔离性。日志记录了事务对数据的修改。
四、数据库中并发控制
分为两种实现思想:乐观锁、悲观锁。
乐观锁一般在应用中实现。它认为系统中事务操作数据出现出现冲突(同时修改等)的情况可能性很低,利用悲观锁会带来较大的资源开销。乐观锁的想法是给数据添加版本,事务读取时将版本同时读出,修改数据时比较数据库当前版本与之前读出的版本,如果一致,则予以更新并修改版本;若不一致,说明数据已经被改变,回滚此次操作。
乐观锁主要适用于事务间发生冲突可能性很低,即使冲突也不会造成太大影响的系统。注:乐观锁无法保证事务的四个特性。
乐观锁通常由数据库来实现。它认为系统多个事务出现冲突的可能性很高,或者需要保证数据的可靠性。在修改数据前对数据加上排它锁,阻止其他事务读取/修改数据,当事务提交时,才释放锁。
乐观锁适用于事务冲突可能性大、数据可靠性要求高的系统。
五、事务隔离级别以及多事务并发(可能)存在的问题
先来介绍一些概念
1、丢失更新:
两类情况
a.两个事务都修改了同一个数据,但第二个事务失败,导致第一个事务也失败
b.两个事务同时读取了一个数据,然后都进行了修改,第二个修改会覆盖第一次修改,造成第一次修改失效。
2、脏读:事务读到其他事务未提交的修改,如果那个事务失败,则该事务读到的数据就是错的
3、不可重复读,事务中前后进行相同的查询,出现结果不一致的情况
4、幻读,事务先后执行两条相同查询,但后一次会出现比第一次数据多的情况
注:
1、不可重复读与幻读的区别
不可重复读主要指前后读取的同一条数据存在不一致,而幻读则偏重于多出数据,数据总数不一致。前者重点在于delete和update,后者在于insert
根据数据库悲观锁的实现差异,分为四个隔离级别
1、未提交读(Read uncommited),事务可以读取到其他事务尚未提交的修改
2、读已提交(Read commited),事务可以读取到其他事务提交了的修改,但不会读取到未提交的修改
3、可重复读(Repeatable read),事务执行过程中,读取到的数据始终保持一致,不会受到其他事务的影响
4、串行化(Serializable),事务一个接一个的运行,不存在并行的概念
隔离级别与问题的关系
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | Y | Y | Y |
已提交读(Read committed) | N | Y | Y |
可重复读(Repeatable read) | N | N | Y |
可串行化(Serializable ) | N | N | N |
注:Y-存在该问题;N-不存在该问题 |
六、(悲观)锁的实现
锁的类别
根据粒度不同,可以分为行锁、页锁、表锁等多种锁
根据目的不同,可以分为共享锁(S),排它锁(X)等
共享锁与排它锁的区别在于:同一数据上可以同时拥有多把共享锁,但同一时刻只能拥有至多一把排它锁,且此时没有共享锁。即满足条件**(S>=0 && X==0) || (S==0 && X<=1)**
事务在操作数据之前,对数据进行加锁操作。具体是读取前加共享锁,修改前加排它锁,如果当前不符合加锁条件,则等待。
锁的释放时机:
在已提交读的隔离级别下,共享锁的释放是在本次读取完成时,排它锁的释放是在事务结束时(提交或回滚),其他隔离级别稍有不同
七、处理死锁
额,似乎已经超出事务锁的范围了,给个连接吧
(什么是死锁及死锁的必要条件和解决方法【转】 )
参考
深入理解乐观锁与悲观锁
数据库锁
数据库事务与并发
mysql dba系统学习(22)数据库事务详解
数据库并发访问、事务与锁的关系
Innodb中的事务隔离级别和锁的关系
数据库事务隔离级别与锁
数据库事务隔离级别 (文中的例子蛮有意思的)
事务处理
如果有人问你数据库的原理,叫他看这篇文章(最近发现的,讲的很全面清晰)