对数据库事务理解以及脏读、不可重复读以及幻读问题

引言

事务就是能够把多个SQL给打包在一起,变成一个整体。因为有些操作是需要作为一个整体来完成的,比如转账操作,A给B转账100,那么A的账户余额就会减100,B的账户余额就会加100。如果在A转账的过程中,因为某些原因只执行到一半,那么A的账户减100,B的账户却没有加100,显然这种是不正确的状态。事务就是要保证避免出现这种不科学的状态,把整个转账操作打包成一个整体,要么全部执行,要么一个都不执行。关于事务把操作打包成一个整体,就叫做“原子性”,这也是事务最核心的特性。

关于在执行过程中出错,就一条都不执行,这里并不代表一条都没有执行,而是出错之后,自动恢复到执行之前的样子,在外界看来就是一条都没执行的样子。这里面涉及到了一个关键操作,“回滚”。回滚就是把执行过的操作逆向恢复回去,类似于ctrl+Z。数据库会把执行的每个操作都记录下来,如果某个操作出错,就会把事务中前面的操作进行回滚。根据之前进行的操作,进行逆向操作,前面是插入,回滚就删除,前面是删除,回滚就插入,前面是修改,回滚就改回去。关于保存操作记录是有很大开销的,一般地就把正在执行的事务的操作给记录下来。

事务的使用

1.开启事务:start transaction;

2.执行多条SQL语句

3.回滚或提交:rollback/commit; (开启事务之后,中间的这些sql不会立即就执行,而是先等commit再统一执行,保证原子性,rollback主动进行回滚)

rollback即是全部失败,commit即是全部成功。

示例:

start transaction;

-- A账户减少100

update accout set money=money-100 where name = 'A';

-- B账户增加100

update accout set money=money+100 where name = 'B';

commit;

数据库的事务主要有四大特征:

1.原子性:这是事务的初心,⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。

2.一致性:事务执行前执行后都必须是数据合法的状态。一致性是事务要解决的问题,也是要依赖原子性来实现的,在事务开始之前和事务结束以后,数据库的完整性没有被破坏

3.持久性:事务产生的修改,都是会写入硬盘的,即使程序重启或主机重启或掉电,事务都是可以正常工作的,保证修改是有效的。

4.隔离性:一个数据库服务器同时执行多个事务的时候,事务之间的相互影响程度。数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务

并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(

Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化

(Serializable)。

谈谈脏读、不可重复读以及幻读问题

关于MySQL提供的四个隔离级别:

mysql服务器要同时给多个客户端提供服务,此时多个客户端之间,可能会同时发起事务,尤其是这多个事务在操作同一个数据库的同一个表的时候,就很有可能引起一些麻烦。如果隔离性越高,就意味着事务之间的并发程度越低,执行效率是越慢,但是数据的准确性是越高的。如果隔离性越低,就意味着事务之间的并发程度越高,执行效率是越快,但是数据的准确性是越低的。

MySQL提供的四个隔离级别,根据实际需求选择不同的隔离级别。

1.如果事务B正在写数据,然而事务A也在读取数据,因为针对的是同一份数据,那么这种情况就属于“脏读”问题,在这个场景下,事务间是完全并发的,没有任何限制,这个级别就是read uncommitted。为了解决“脏读”问题,就是降低并发性,提高隔离性,也就是给“写操作”加锁。比如事务B在写数据的时候,事务A是不能读取的。

2.为了解决“脏读”问题,给“写操作”加锁了,所以A只能在B写入完成之后才能读取,这个级别就是read committed。但是又引入了新的问题,当A正在读取数据的时候,这个时候B又开始写入数据了,导致A读取数据一半的时候后面的数据就变了。在一个事务中,连续两次读取的数据的结果不一致,这就是“不可重复读”问题。为了解决“不可重复读”问题,那么给“读操作”也加锁了,在A读取数据的时候,是不能写入数据的。这两个事务之间的并发程度被进一步降低了,隔离性进一步的提高了,运行速度进一步降低了,但是数据准确性进一步提高了。

3.为了解决“不可重复读”问题,给“写操作”和“读操作”都加锁了,这个级别就是repeatable read,针对同一个代码文件来说,事务B在写入数据的时候,事务A不能进行读取,同样,事务A在进行读取数据的时候,事务B也是不能进行写入数据的。但是随着又引入了新的问题,既然B在写入数据的时候,A不能进行读取,同样A在读取数据的时候,B不能写入数据,但是他们都不是一直傻等着的,他们各自也会去做其他的事情,比如新增或删除其他文件。此时,虽然读取的代码内容没有变,但是会发现文件的数量有变化,例如这种情况,在同一个事务中,两次读到的结果集不相同,这就是“幻读”问题。为了解决“幻读”问题,就只有实现串行化,完全舍弃并发,比如B在写入数据的时候,A什么都不干,就干等着,这个级别就是serializable。

总结:MySQL提供的四个隔离级别

1.read uncommitted(读未提交):不做任何限制,事务之间都是随意并发执行的,并发程度最高,隔离性最低,会产生脏读+不可重复读+幻读。

2.read committed(读已提交):对写操作加锁了,并发程度降低了,隔离性提高了。解决了脏读问题,仍然存在不可重复读+幻读。

3.repeatable read(可重复读):对写和读都加锁了,并发程度又降低了,隔离性又提高了.解决了脏读+不可重复读,可能存在幻读问题。

4.serializable(串行化):严格串行化,并发程度最低(串行执行的)隔离性最高。解决了脏读+不可重复读+幻读问题,执行速度最慢的。

MySQL的隔离级别是通过MySQL的配置文件来进行调整的,在开发中,根据实际问题的需求场景来决定选用哪一个级别。其中MySQL的默认级别是repeatable read。

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

crazy_xieyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值