首页 > 编程知识 正文

下面数据库隔离级别中会出现不可重复读和幻读的有,通过设置事务隔离级别来解决读的问题

时间:2023-05-03 11:04:18 阅读:234647 作者:934

  开篇声明,由于两位大佬排版不够美观,然后又发现一些歧义,因此我集大佬之所长,精心整理并加以完善,可放心阅读。
http://blog.csdn.net/yuxin6866/article/details/52649048
https://blog.csdn.net/zjxxyz123/article/details/79413729

一、数据库事务正确执行的四个基本要素

1.1ACID原则。

  ACID原则是数据库事务正常执行的四个基本要素,分别指原子性、一致性、独立性及持久性。

  原子性(Atomicity)是指一个事务要么全部执行,要么不执行,也就是说一个事务不可能只执行了一半就停止了,比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱。不可能划了卡,而钱却没出来,这两步必须同时完成.要么就不完成。

  一致性(Consistency)是指事务的运行并不改变数据库中数据的一致性。例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。或者说,A给B转账300元钱,那么A的账户就必须是减少300元钱,B的账户就必须是增加300元钱,不能说是增加或减少了如200元钱等,这里符合事务的原子性,但是不符合事务的一致性。往实际业务中没有这么简单,往是类似买东西扣库存这类的逻辑,主表里有库存,库存表里有库存,SKU表里还有,然后就因为设计缺陷,就算加了事务还是出现了超卖、SKU库存对不上总库存的问题,这个就是一致性不满足的了。

  隔离性(独立性)(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态,因为这样可能会导致数据不一致。

  持久性(Durability):一旦事务提交或者回滚,这个状态都要持久化到数据库中,不考虑隔离性会出现的读问题。

二、事务并发处理带来的问题

2.1脏读、不可重复读,幻读,更新丢失。

  脏读(Dirty read):在一个事务中读取到另一个事务已经修改但没有提交的数据。例如,事务A对数据进行了修改,但是还没有提交,这时事务B读取这个数据,然后事务A回滚,那么事务B取的数据无效。不符合一致性。

  不可重复读(NonRepeatable Read):既不能读到相同的数据内容,事务A读取到了事务B已经提交的修改数据(一个事务范围内两个相同的查询却返回了不同数据)。例如事务A先读取数据,然后事务B对该同一数据修改并提交,那么事务A再次读取该数据时,由于事务B对该数据的修改,事务A两次读到的的数据可能是不一样的。不符合隔离性。

  幻读(Phantom Read):事务A读取到了事务B已经提交的新增数据。在一个事务中,两次查询的结果不一致(针对的insert操作) 。例如事务A对一个表中的数据进行了修改,同时,事务B也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,事务A的用户后面的操作发现表中还有没有修改的数据行,就好象发生了幻觉一样。不符合隔离性。

  更新丢失(Update lose):两个事务同时操作相同数据,后提交的事务会覆盖先提交的事务处理结果。通过乐观锁就可以解决。

三、数据库事务隔离级别

  数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(读未提交)、Read committed(读提交) 、Repeatable read(可重复读)、Serializable(序列化),这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。

3.1 Read uncommitted(读未提交)

  公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资到账5000元整,非常高兴。可是不幸的是,领导发现发给singo的工资金应该是2000元,于是迅速回滚了事务(将5000元回滚),修改金额后(修改为2000元),将事务提交,最后singo实际的工资只有 2000元,singo空欢喜一场。

  出现上述情况,即我们所说的脏读 ,两个并发的事务,“事务A:领导给singo发工资”,“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。

  当隔离级别设置为Read uncommitted(读未提交)时,就可能出现脏读,如果我们此时将隔离级别提升为Read committed(读已提交),便可避免脏读。

3.2 Read committed(读已提交)

  singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在 singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,到底是啥情况呢?

  出现上述情况,即我们所说的不可重复读 ,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

  当隔离级别设置为Read committed(读已提交)时,避免了脏读,但是可能会造成不可重复读(既不能读到相同的数据内容)。

  大多数数据库的默认级别就是Read committed(读已提交),比如Sql Server , Oracle,此时如果将隔离级别提升为Repeatable read(可重复读),可以避免脏读和不可重复读的发生。

3.3 Repeatable read(可重复读)

  当隔离级别设置为Repeatable read(可重复读)时,可以避免不可重复读。

  有A、B两个会话,分别开启两个事务,B查看余额是100元钱,然后A向B转了500元钱,A 提交事务,B再去查看,发现依旧是100元钱,B只能结束当前事务,在开启一个新事务,才能查询到数据的变化,这样便避免了不可重复读。如果我们设置了Seriizable(序列化),就相当于锁表,某一时间内只允许一个事务访问该表。

  虽然Repeatable read避免了不可重复读,但还有可能出现幻读 。

  比如singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额 (select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction … ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出 现了幻觉,幻读就这样产生了。

  注:Mysql的默认隔离级别就是Repeatable read。

3.4 Serializable(序列化)
  Serializable(序列化)是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。

四、总结

4.1 隔离级别与对应可能产生的问题表

隔离级别脏读(Dirty read)不可重复读(NonRepeatable Read)幻读(Phantom Read)读未提交(Read uncommitted)可能可能可能读已提交(Read committed)不可能可能可能可重复读(Repeatable read)不可能不可能可能序列化(Serializable)不可能不可能不可能 五、一些数据库锁的笔记等

以下观点并非正确,大家可开启多个连接进行实践。

5.1、锁定义和分类

锁定义:
  锁是计算机协调多个进程和线程并发访问某一资源的机制。

锁分类:
  (1) 按性能分类:乐观锁和悲观锁。
  (2) 按操作分类:读锁和写锁。
  (3) 按粒度分类:表锁和行锁。

InnoDB与MyISAM的不同点:
  (1)前者支持事务(Transaction)。
  (2)采用了行级锁。

5.2、一些总结笔记

  MyISAM在执行查询语句(SELECT)前会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的所有表加写锁。

读锁:一个会话给表加了读锁,没有释放,那么当前会话及其他会话就无法写,只能读。
写锁:一个会话给表加了写锁,没有释放,那么当前会话可以对表读和写,其他会话不能读也不能写。

InnoDB行锁:
只对当前行加锁,一个会话开启事务,然后进行更新,另一个会话也要对这行数据进行更新,那么会等前面的会话提交才能更新。

当隔离级别可重复读时:
  第一次查询会有快照,比如事务A更新了数据,但是事务B还没有执行查询操作,然后B查询,那么B查询的数据是事务A更新后的,以后每次查询,都是查询这次的快照。比如金额原先为100元,事务A将之更新为200,事务B查询的时候,是200,如果事务B先查询,事务A后更新,那么事务B查询到的还是100。但即使查询的数据是100(事务B已经更新成了200),如果事务A对金额进行加200的操作,那么结果是400,不是300,所以说,可重复读取不将查询结果(100)拿来当作变量运算的话,其执行结果是正确的。

幻读,也是在其他事务修改数据后,才进行读取,才能读到其他事务已提交的数据。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。