事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。
1 、原子性
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
2 、一致性
数据库事务不能破坏关系数据库的完整性以及业务逻辑上的一致性。
3 、隔离性
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
4 、持续性
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务应用场景:
对于同一个银行帐户A内有200元,甲进行提款操作100元,乙进行转帐操作100元到B帐户。如果事务没有进行隔离可能会并发如下问题:
脏读(Dirty Read): 一个事务读到另一个事务未提交的更新数据。
事务T1更新了数据还未提交,这时事务T2来读取相同的数据,则T2读到的数据其实是错误的数据,即脏数据。基于脏数据所作的操作是不可能正确的 。
脏读:甲取款100元未提交,乙进行转帐查到帐户内剩有100元,这是甲放弃操作回滚,乙正常操作提交,帐户内最终为0元,乙读取了甲的脏数据,客户损失100元。
解读:甲进行回滚操作,账户将变成200元,乙进行正常提交,200元将被覆盖为0,故顾客损失100元
第一类丢失更新: 撤销一个事务时,把其他事务已提交的更新数据覆盖。
例子:首先甲提款时帐户内有200元,同时乙转帐也是200元,然后甲乙同时操作,甲操作成功取走100元,乙操作失败回滚,帐户内最终为200元,这样甲的操作被覆盖掉了,银行损失100元。
不可重复读(Nonrepeatable Read) :一个事务读到另一个事务已提交的更新数据。
一个事务的两次读取中,读取相同的资源得到不同的值。当事务T2在事务T1的两次读取之间更新数据,则会发生此种错误。(重点在修改)
例子:甲乙同时开始都查到帐户内为200元,甲先开始取款100元提交,这时乙在准备最后更新的时候又进行了一次查询,发现结果是100元,这时乙就会很困惑,不知道该将帐户改为100还是0。
幻读(虚读): 一个事务读到另一个事务已提交的新插入的数据。(解释一)
事务T1对一定范围内执行操作,T2对相同的范围内执行不兼容的操作,这时会发生幻读。
如:T1删除符合条件C1的所有数据,T2又插入了一些符合条件C1的数据,则在T1中再次查找符合条件C1的数据还是可以查到,这对T1来说好像是幻觉一样,这时的读取操作称为幻读。(重点在新增或删除)事务T1对一定范围内执行操作,T2对相同的范围内执行不兼容的操作,这时会发生幻读。
如:T1删除符合条件C1的所有数据,T2又插入了一些符合条件C1的数据,则在T1中再次查找符合条件C1的数据还是可以查到,这对T1来说好像是幻觉一样,这时的读取操作称为幻读。(重点在新增或删除)
如:是针对于插入操作过程中的读取问题,如丙存款100元未提交,这时银行做报表进行统计查询帐户为200元,然后丙提交了,这时银行再统计发现帐户为300元了,无法判断到底以哪个为准?
大家好像觉得统计这个东西肯定是时时更新的,这种情况很正常;但是如果统计是在一个事务中的时候就不正常了,比如我们的一个统计应用需要将统计结果分别输出到电脑屏幕和远程网络某台计算机的磁盘文件中,为了
提高性能和用户响应我们分成2个线程,这时先完成的和后完成的统计数据就可能不一致,我们就不知道以哪个为准了。
和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
第二类丢失更新: 这是不可重复读的特例,一个事务覆盖另一个事务已提交的更新数据。
如上,乙不做第二次查询而是直接操作完成,帐户内最终为100元,甲的操作被覆盖掉了,银行损失100元。
在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题。大体上有三种问题:
1、丢失更新说明事务进行数据库写操作的时候可能会出现的问题。
2、不可重复读说明了做数据库读操作的时候可能会出现的问题。
3、脏读
数据库系统采用锁来实现事务的隔离性。
1、锁的基本原理如下:
A、当一个事务访问某种数据库资源时,如果执行select语句,必须先获得共享锁;如果执行insert、update或者delete语句,必须获得独占锁。
B、当第二个事务也要访问相同的资源时,如果执行select语句,也必须先获得共享锁;如果执行inert、update或者delete语句,也必须先获得独占锁。此时根据已经放置在资源上的锁的类型,来决定第二个事务应该等待还是立即获取该锁。
第二个事务获取锁的情况说明:
资源上已经放置的锁 | 第二个事务进行读操作 | 第二个事务进行更新操作 |
无 | 立即获得共享锁 | 立即获得独占锁 |
共享锁 | 立即获得共享锁 | 等待第一个事务解除共享锁 |
独占锁 | 等待第一个事务解除独占锁 | 等待第一个事务解除独占锁 |
3、锁的类型和兼容性:
A、共享锁:
共享锁用于读数据操作,它是非独占的,允许其他事务同时读取其锁定的资源,但是不允许其他事务更新它。
B、独占锁:
独占锁也叫排他锁,适用于修改数据的场合。它所锁定的资源,其他事务不能读取也不能修改。
C、更新锁:
更新锁在更新操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。
共享锁、独占锁、更新锁的特征总结:
特征项 | 共享锁 | 独占锁 | 更新锁 |
加锁条件 | 当一个事务执行select语句时,数据库会 为这个事务分配一个共享锁,来锁定被查询的数据。 |
事务执行inert、update、delete语句时,分配独占锁。 | 事务执行 update语句时 ,分配更新锁 |
解锁条件 | 一般数据被读取后,数据库系统立刻解除共享锁 。当查询多条记录时,也是一条一条的锁定-解锁。 |
独占锁直到事务结束才能被解除。 | 数据读取完毕, 执行更新操作时 ,会把更新锁升 级为独占锁。 |
与其他锁兼容性 | 如果数据资源上放置了共享锁,还能再放置共享锁和更新锁。 | 不能和其他锁兼容。 | 与共享锁兼容, 但只能有一个更 新锁,这样可以 避免死锁 |
并发性能 | 具有良好的并发性能 | 并发性能较差 |
更 新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。
4、死锁及其防治方法:
在数据库系统中,死锁是指多个事务分别锁定一个资源,又试图请求锁定对方已经锁定的资源,这就产生了一个锁定请求环,导致多个事务都处于等待对方释放锁定资源的状态。数据库的隔离级别:
数据库提供了4种事务隔离级别供用户选择:
隔离级别 | 是否有第一类丢失更新 | 是否出现脏读 | 是否出现虚读 | 是否出现不可重复读 | 是否出现第二类丢失更新 |
Serializable | 否 | 否 | 否 | 否 | 否 |
Repeatable Read | 否 | 否 | 是 | 否 | 否 |
Read Commited | 否 | 否 | 是 | 是 | 是 |
Read Uncommited | 否 | 是 | 是 | 是 | 是 |
Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
1、从应用程序的角度,锁可以分为2类:
A、悲观锁:指在应用程序中显式的为数据资源加锁。会影响并发性能。
B、乐观锁:乐观锁假定当前事务操作数据资源时,不会有其他事务同时访问该数据资源,因此完全依靠数据库的隔离级别来自动管理锁。
2、悲观锁的实现方式:
A、在应用程序中显示指定采用数据库系统的独占锁来锁定数据源。
B、通过在数据库表中增加一个标记字段,来判断事务是否可以访问。
验证:http://xm-king.iteye.com/blog/770721
参考文献:
http://blog.chinaunix.net/uid-22741583-id-127431.html
http://kinglyhum.iteye.com/blog/784428
http://www.cnblogs.com/tq03/p/3789434.html?utm_source=tuicool&utm_medium=referral(重点参考)