数据库事务隔离级别及脏读、不可重复读、幻读的理解

  开篇声明,由于两位大佬排版不够美观,然后又发现一些歧义,因此我集大佬之所长,精心整理并加以完善,可放心阅读。
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)拿来当作变量运算的话,其执行结果是正确的。

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

你可能感兴趣的:(数据库)