事物隔离性详解

前言:

本文是阅读周志明大佬的《凤凰架构》中对于事物隔离性的个人愚见,本文涉及到的图片案例也均出自该书,如有问题请多多指教。

事物实现的三种要素

ACID (Atomic Consisitency Isolation Durability)是信息系统为了保证系统中所有的数据都是符合期望的,且互相关联的数据之间不会产生矛盾,即数据状态的 一致性(Consistency),而按照数据库经典的理论,需要通过三个方式

  • 原子性(Atomic)

  • 隔离性(Isolation)

  • 持久性(Durability)

原子性和持久性并不是本文讨论的重点,但是知道实现这两种最常见的方式便是日志提交(commit logging)和影子分页(shadow paging)即可,隔离性是保护正在处理业务的过程中,不会因为各个业务读写导致数据彼此独立不会被影响。

实现隔离性

数据库实现方法隔离性方法简单而有效:加锁 加锁的类型有

  • 写锁:(也叫排他锁, 事物持有写锁时候,只允许持有写锁的事物写入,不允许其他事物施加读锁和写锁)

  • 读锁 :(也叫共享锁, 多个事物可以对数据同时加锁,同时读,但是加读锁期间不能被施加写锁,但是当数据只有一个事物持有读锁的时候,可以自动升级为写锁)

  • 范围锁: (加锁期间不允许范围中的数据进行修改,其中这里修改的含义包括插入和删除 )

隔离性等级

串行化,即对事务加上 读锁、写锁、和范围锁

  • 串行化访问提供了最高强度的隔离性,ANSI/ISO SQL-92 中定义的最高等级的隔离级别便是可串行化(Serializable)。可串行化完全符合普通程序员对数据竞争加锁的理解,如果不考虑性能优化的话,对事务所有读、写的数据全都加上读锁、写锁和范围锁即可做到可串行化

可重复读,只有读锁和写锁

可重复读(Repeatable Read),可重复读对事务所涉及的数据加读锁和写锁,且一直持有至事务结束,但不再加范围锁。可以从名字可重复读,说明事物过程中会一直占用读锁.

幻读问题

相比于串行化,可重复读带来了幻读问题。 幻读问题(Phantom Read),它是指在事务执行过程中,两个完全相同的范围查询得到了不同的结果集。譬如现在要准备统计一下Fenix’s Bookstore中售价小于100元的书的本数,可以执行以下第一条SQL语句:

SELECT count(1) FROM books WHERE price < 100 /* 时间顺序:1,事务: T1 */ 
INSERT INTO books(name,price) VALUES ('深入理解Java虚拟机',90)/* 时间顺序:2,事务: T2 */ 
SELECT count(1) FROM books WHERE price < 100 /* 时间顺序:3,事务: T1 */ 

根据前面对范围锁、读锁和写锁的定义可知,假如这条SQL语句在同一个事务中重复执行了两次,且这两次执行之间恰好有另外一个事务在数据库插入了一本小于100元的书,这是会被允许的,那这两次相同的SQL查询就会得到不一样的结果,原因是可重复读没有范围锁来禁止在该范围内插入新的数据,这是一个事务受到其他事务影响,隔离性被破坏的表现。

读已提交

读已提交(Read Committed),读已提交对事务涉及的数据加的写锁会一直持续到事务结束,但加的读锁在查询操作完成后会马上释放

  • 从解释来看,读锁并不会一直占用,而是在读操作完成后就释放,这个对比可重复读 很明显会带来不可重复读的问题注意这里读已提交在后面sql中会带有commited 字样,表示着这个隔离级别会读取到别的事务已经被提交的事务(也就是所谓的不可重复读的问题)

不可重复读问题

image.png

由于隔离级别为读已提交,因此读锁的加锁周期只能在读期间(读取期间不允许数据被改变),读完后就被释放,因此读完后是可以被修改的,所以再次重复读出来的结果也许会不一样(不可重复读)
读锁的意义

原因是 读已提交的隔离级别缺乏贯穿整个事务周期的读锁,无法禁止读取过的数据在读取之后发生变化

  • 加锁期间不允许数据发生变化,但是该限制级并不能全覆盖整个事物处理周期

读未提交

读未提交(Read Uncommitted),它只会对事务涉及的数据加写锁,且一直持续到事务结束,但完全不加读锁。不加读锁意味着在读取期间随时会有事务进来被读到通过上面 读已提交隔离级别的意思,这个读未提交意思为:可以读到未被提交到的事务。如下面脏读问题的sql例子,被读取到的事务没有被提交(no commited)

脏读问题

image.png

写锁的新认知

请再读一次写锁的定义:写锁禁止其他事务施加读锁,而不是禁止事务读取数据,如果事务T1读取数据前并不需要加读锁的话,就会导致事务T2未提交的数据也马上能被事务T1所读到。这同样是一个事务受到其他事务影响,隔离性被破坏的表现。

  • 写锁并不代表 数据写期间不能读,而是规定不能再加读锁,即:我现在正在写呢,你不能要求我不写,但是正因为没有加读锁,所以另一个事务可以读取加了写锁的数据

完全不隔离级

  • 理论上还存在更低的隔离级别,就是“完全不隔离”,即读、写锁都不加

脏写问题实质与完全不隔离级别的意义

读未提交会有脏读问题,但不会有脏写问题(Dirty Write),即一个事务没提交之前的修改可以被另外一个事务的修改覆盖掉。脏写已经不单纯是隔离性上的问题了,它将导致事务的原子性都无法实现,所以一般谈论隔离级别时不会将完全不隔离纳入讨论范围内,而是将读未提交视为最低级的隔离级别 脏写与脏读问题类似,“是在事物执行过程中,一个事物读取到了另一个事物未提交的数据”,在这里解读为在事物写的过程中,由于没有加写锁(几乎所有隔离级都有加写锁,就完全不隔离级没有加锁,所以脏写问题仅仅出现在这个隔离级别上),所以会在写的过程出现被另外一个事务的数据覆盖。由于完全没有锁,事务的原子性无法保证,完全隔离级基本上只是一个理论的级别,平时不会讨论

隔离性问题的相似点

幻读、不可重复读、脏读等问题都是由于一个事务在读数据的过程中,受另外一个写数据的事务影响而破坏了隔离性。

  • 已知这些问题的特点都是 一个事务读,一个事务写 的隔离性问题,都是因为适当放开了一些读锁的时间点,以增加数据库读io性能,从而导致了这样的问题

MVCC (多版本并发控制)针对“读+写”事务场景的无锁优化

image.png

需要注意的是 只有可重复读和读已提交会使用mvcc优化,其他的两种隔离级别都不需要用到
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值