SQL之事务

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mmake1994/article/details/80721628

目录

一、含义(一组操作,同生共死)

二、场景构思

1.案例

2.问题

3.解决

三、事务的四个特性(ACID)

1.原子性(Atomicity)

2.一致性(Consistency)

3.隔离性(Isolation)

4.持久性(Durability)

四、事务并发问题(不考虑隔离性)

1.脏读

2.不可重复读

3.幻读/虚读

五、事务并发问题解决(设置事务的隔离级别)

1.读未提交(Read Uncommitted,1级)

2.读已提交(Read Committed,2级)-Oracle默认

3.可重复读(Repeatable Read,4级)-MySQL默认

4.序列化/串行化(Serializable,8级)

六、丢失更新

1.产生原因

2.解决方法(悲观锁|乐观锁)


一、含义(一组操作,同生共死)

在数据库中,一项事务(Transaction)是由一条或多条操作数据的SQL语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被回滚

总结起来理解为:逻辑上的一组操作,组成这组操作的各个单元,要么一起成功,要么一起失败

二、场景构思

1.案例

现有两个好友需要使用支付宝转钱操作:

    (1)A准备向B转账100元(update xxx set 余额=余额-100 where 主键=1)

    (2)银行收到A的转账请求,从A账户上扣除100元

    (3)银行准备向B账户打款100元(update xxx set 余额=余额+100 where 主键=2)

2.问题

执行完2操作后,突然停电。待电力系统回复之后,银行并不会继续执行第3步,甚至连1,2步的操作记录都丢失 

A 认为已经转账成功;银行并没有产生发生转账事情;B没有收到A的转账

3.解决

其实整个过程可以用一个词来描述:数据库中的数据产生了“不一致性”。可以将1和2包装成一个事务,如果执行成功:提交事务,改变原来的数值;如果执行失败:回滚事务,返回原来的数值

三、事务的四个特性(ACID)

1.原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生 

2.一致性(Consistency)

一个事务中,事务前后数据的完整性必须保持一致。小明有100块,小红有100块。小明转给小红50后,小明和小红的总钱数应该还是200块。

3.隔离性(Isolation)

多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离

4.持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

四、事务并发问题(不考虑隔离性)

1.脏读

一个事务读取到了另一个事务尚未提交的数据 (别人修改数据----未提交事务----你去读取到别人事务中数据----别人回滚事务

2.不可重复读

一个事务读到了另一个事务已经提交的update数据,导致在同一个事务中多次查询结果不一致(你去读取数据库数据---别人修改数据后提交事务----你再次读取数据库数据------两次结果不一致)

3.幻读/虚读

一个事务读到了另一个事务已经提交的insert数据,导致在同一个事务中多次查询结果不一致(你正准备删空某表的记录----别人在你准备删除时向数据库该表里插入了一条数据----你并不知道有人向这个表插入数据,删除完校验时发现该表竟然还有数据,仿佛产生幻觉

五、事务并发问题解决(设置事务的隔离级别)

为了避免事务并发问题的发生,在标准的SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同

1.读未提交(Read Uncommitted,1级)

一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据,此隔离级别可防止丢失更新,以上问题无法避免

2.读已提交(Read Committed,2级)-Oracle默认

一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行,此隔离级别可以有效防止脏读

3.可重复读(Repeatable Read,4级)-MySQL默认

一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务,但允许读事务,写事务则禁止任何其他事务,此隔离级别可以有效防止不可重复读和脏读

4.序列化/串行化(Serializable,8级)

提供严格的事务隔离,它要求事务系列化执行,事务只能一个接着一个地执行,不能并发执行,此隔离级别可有效防止幻读、不可重复读和脏读

六、丢失更新

1.产生原因

 丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。

2.解决方法(悲观锁|乐观锁)

(1)悲观锁

    select * from account for update; --排他锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现

(2)乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

版本号机制控制(加入一个字段version)

一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

 

展开阅读全文

没有更多推荐了,返回首页