事务详解

来源:互联网 发布:淘宝外宣兼职违法吗 编辑:程序博客网 时间:2024/06/06 11:45

如何理解事务

概念:一组业务逻辑,注意,是一组。例如,A转钱给B A就要有更新语句,B也要有更新语句,这两个一起才是一个事务。

事务ACID

automatic:原子性,

每个事务是独立的个体,要么成功,要么失败

consitency:一致性,

例如某个事务是实现A向B转100元,则A账户减100元,B账户增加100元,体现一致性

isolation:隔离性,

并发时事务之间不能相互干扰

  1. read uncomitted:

读未提交,级别最低,B事务并发读取到A事务为提交的数据,这会出现脏读

  1. read comitted:

读提交(oracle默认),顾名思义,B事务操作要等A事务操作完成后进行,但是这样会出现不可重复读问题,即当A事务提交前查看的记录和提交后的记录不一致

  1. repeatable read

可重复读(Mysql默认),开始在读取事务开启的时候不允许进行修改操作,但这样不能阻止发生插入操作,这样会出现虚读(幻读),即在查看事务提交时发生insert,莫名其妙发现数据不一致。

  1. Serializable 是最高的事务隔离级别

,单事务,两个事务同时对一个内容进行操作时,必须等前一个事务提交成功后再执行下一个事务,在该级别下,事务串行化顺序执行,*可以避免脏读、不可重复读与幻读*。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

duriblity:持久性

即事务提交发生后,对改变的数据是永久性的

数据库数据丢失更新问题

概念:

后一次的更新内容将前一次未提交的数据覆盖了
形象解释:第一次开启事务A,减去金额100元,事务未提交或者其他原因,同时事务B查询数据库还是1000元,然后增加100元,B修改后发现是1100,元,这就是造成前一次的更新丢失

解决办法:

  1. 乐观锁:

乐观认为不会出现锁获取失败问题,即先进行业务判断,不得已到更新最后一步去拿锁。

实现思路如下:一般在需要修改加锁的字段上增加版本号或者时间戳或者先查询后修改。

原理:乐观锁是否在事务中其实都是无所谓的,其底层机制是这样:在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这之间没有发生并发的修改。如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程。

  1. 悲观锁:

先加锁,再进行业务操作,即“悲观”认为更新锁会失败,所以要在业务之前先成功获取锁的控制,即“一锁二查三业务”。

分类:排他锁和共享锁

  • 排他锁

只能一个进行写,不能拥有其他锁,也就是说,若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A

  • 共享锁:只能读不能写

实现:通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select for
update时会获取被select中的数据行的行锁,因此其他并发执行的select for
update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for
update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

注意:不同的数据库对select for update的实现和支持都是有所区别的,例如oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,mysql就没有no
wait这个选项。另外mysql还有个问题是select for
update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在mysql中用悲观锁务必要确定走了索引,而不是全表扫描。

springAOP事务传播行为

一个业务A,一个业务B,AB如何共享事务,不同传播行为共享方案不同。 
详解:

什么意思呢?比如业务A为银行转账的业务。
业务B为转完账发短信的业务,平常我们是转完钱,那么我们就需要收到短信说我们的账户上被转走多少钱,而收钱的那一方则需要收到短信说账户被转进多少钱,那么这两个业务是使用同一个事务呢?还是分别使用不同的事务,也就是如果是使用同一个事务的话,我们转钱成功了代表业务A成功了,但是业务B发送短信时出现问题,则说明该事务失败,那么刚才转的钱就算不成功,需要回滚,但是实际生活中,是不能这样的,转钱成功了,短信没发送成功,那么短信在重新发送一次即可。不需要让业务A重新在操作一遍。这就是业务A和业务B共享事务的解决方法,让他们两个使用各自的事务。而传播行为就是提供这样的共享方案的属性。

传播行为方案               

 1.PROPAGATION_REQUIRED ,required ,必须使用事务 (默认值)

A 如果使用事务,B 使用同一个事务。(支持当前事务)
A 如果没有事务,B将创建一个新事务。

 2.PROPAGATION_SUPPORTS,supports ,支持事务

A 如果使用事务,B 使用同一个事务。(支持当前事务)
A 如果没有事务,B 将以非事务执行。

  
3.PROPAGATION_MANDATORY,mandatory 强制

A 如果使用事务,B 使用同一个事务。(支持当前事务)
A 如果没有事务,B 抛异常

4.PROPAGATION_REQUIRES_NEW , requires_new ,必须是新事务

A 如果使用事务,B将A的事务挂起,再创建新的。
A 如果没有事务,B将创建一个新事务

5.PROPAGATION_NOT_SUPPORTED ,not_supported 不支持事务

A 如果使用事务,B将A的事务挂起,以非事务执行
A 如果没有事务,B 以非事务执行

6.PROPAGATION_NEVER,never 从不使用

A 如果使用事务,B 抛异常
A 如果没有事务,B 以非事务执行

7.PROPAGATION_NESTED nested 嵌套

A 如果使用事务,B将采用嵌套事务。

嵌套事务底层使用Savepoint
设置保存点,将一个事务,相当于拆分多个。比如业务A为AB两个曹祖,业务B为CD两个操作,业务AB使用同一个事务,在AB (POINT)
CD,当业务B失败时,回滚到POINT处,从而业务A还是成功的,就是保持点的操作。 底层使用嵌套try方式

掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED

总结

参考链接:
http://www.cnblogs.com/whgk/p/6638192.html
http://www.cnblogs.com/ws-astrologer/p/6681089.html
http://blog.csdn.net/qq_33290787/article/details/51924963

原创粉丝点击