数据库事务简单分析

1.数据库事务基本特征

      原子性(Atomic):事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。
      一致性(Consistency):事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。
      隔离性(Isolation):由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,到底是另一个事务执行之前的状态还是中间某个状态,相互之间存在什么影响,是可以通过隔离级别的设置来控制的。
      持久性(Durability):事务结束后,事务处理的结果必须能够得到固化,即写入数据库文件中即使机器宕机数据也不会丢失,它对于系统的影响是永久性的。

2.数据库事务概述

       数据库锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库中的数据而不发生数据不一致现象的重要保障。 一般来说,锁可以防止脏读、不可重复读和幻读。

√: 可能出现    ×: 不会出现

  脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

3.脏读(Dirty Read)

       一个事务T1读取到了另外一个事务T2没有提交的修改数据。即当一个事务T2正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务T1查询然后使用了这个数据,此时T1查询出来的数据是没有提交且发生变化(回滚、修改 )的数据, 故事务T1查询出来的数据就是脏数据,产生脏数据查询的过程即为脏读。例如:公司发工资了,财务把5000元打到A的账号上,但是该事务并未提交且A正好去查询账户余额,发现工资已经到账且是5000元整,A非常高兴(买彩票中奖的感觉~O(∩_∩)O~);可不幸的是财务及时发现了发给A的工资金额不对应该是2000元,于是迅速回滚了事务修改金额为2000后将事务提交,最后A实际的工资只有 2000元,这真是一个让人悲伤的故事。当隔离级别设置为Read uncommitted 时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。

4.不可重复读(Nonrepeatable Read)

       事务T1事先读取了数据,事务T2紧接了更新了数据并提交了事务,而事务T1再次读取该数据时,发现数据已经发生了改变。即在同一事务中,两次读取同一数据得到内容不同。例如:A君拿着工资卡去消费,系统读取到卡里确实有2000元,而此时他老婆也正好在网上转账,把A工资卡的2000元转到自己的账户中(好男人标准~O(∩_∩)O~),并在A提交事务之前提交了事务,当A扣款时系统检查到A的工资卡已经没有钱即扣款失败,A正纳闷儿时老婆的短信突然过来了。。。突然A明白了。即我们所说的不可重复读,两个并发的事务“事务T1:A消费”、“事务T2:A的老婆网上转账”,事务T1事先读取了数据,事务T2紧接了更新了数据,并提交了事务,而事务T1再次读取该数据时数据已经发生了改变。当隔离级别设置为Read committed 时,避免了脏读,但是可能会造成不可重复读,大多数数据库的默认级别就是Read committed,比如Sql Server、Oracle。

5.幻读(Phantom

       T1事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时T2事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么以后就会发生操作T1的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。A的老婆工作在银行部门,她时常通过银行内部系统查看A的信用卡消费记录。有一天,她正在查询到A当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而A此时正好在外面胡吃海塞后在收银台买单消费1000元,即新增了一条1000元的消费记录(insert transaction...),并提交了事务,随后A的老婆将A当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,A的老婆很诧异,以为出现了幻觉。当隔离级别设置为Repeatableread时,可以避免不可重复读(例如当A拿着工资卡去消费时,一旦系统开始读取工资卡信息即事务开始,A的老婆就不可能对该记录进行修改,也就是A的老婆不能在此时转账),但还有可能出现幻读,且幻读是指当事务不是独立执行时发生的一种现象。Mysql的默认隔离级别就是Repeatable read。

6.串行序列化(Serializable)

       事务执行的时候不允许别的事务并发执行.完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,但是效率太低,一般不会用。

7.丢失更新(Lost Update) 

      事务T1读取了数据并执行了一些操作,然后更新数据,同时事务T2也做相同的事,则T1和T2更新数据时可能会覆盖对方的更新,从而引起数据错误。解决丢失更新,主要使用两种方法:乐观锁悲观锁传送门

  • 使用排它锁(即悲观锁)。丢失更新可以使用写锁(排它锁)进行控制。因为排它锁添加到某个表的时候,事务未经提交,其他的事务根本没法获取修改权,因此排它锁可以用来控制丢失更新。需要说明的是有时候,当知道某一行会发生并发修改的时候,可以把锁定的范围缩小。例如使用select * from t_account t wheret.id='1' for update; 这样能够比较好地把控上锁的粒度,这种基于行级上锁的方法叫"行级锁"。
  • 使用乐观锁。乐观锁的原理是:认为事务不一定会产生丢失更新,让事务进行并发修改,不对事务进行锁定。发现并发修改某行数据时,乐观锁抛出异常。让用户解决。可以通过给数据表添加自增的version字段或时间戳timestamp。进行数据修改时,数据库会检测version字段或者时间戳是否与原来的一致。若不一致,抛出异常。

8.数据库事务隔离级别

     处理以上事务的问题,可以采用如下事务隔离方法:
        (1)TRANSACTION_NONE  不使用事务。
        (2)TRANSACTION_READ_UNCOMMITTED  允许脏读。
        (3)TRANSACTION_READ_COMMITTED  防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。
        (4)TRANSACTION_REPEATABLE_READ  可以防止脏读和不可重复读。
        (5)TRANSACTION_SERIALIZABLE  可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率。

      以上的五个事务隔离级别都是在Connection接口中定义的静态常量,使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。如:con.setTransactionIsolation(Connection.REPEATABLE_READ)。注意:事务的隔离级别受数据库的限制,不同的数据库支持的的隔离级别不一定相同。

9.Spring事务的传播行为

       事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring 默认的事务传播行为是 PROPAGATION
_REQUIRED,它适合于绝大多数的情况,Spring支持7种事务传播行为:
    propagation_requierd:若当前没有事务就新建一个事务,若已存在一个事务中,加入到这个事务中,这是最常见的选择。
    propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
    propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
    propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
    propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
    propagation_nested:若当前存在事务则在嵌套事务内执行。若当前没有事务则执行与propagation_required类似的操作。

猜你喜欢

转载自blog.csdn.net/u011635492/article/details/80547366
今日推荐