暂无图片
暂无图片
10
暂无图片
暂无图片
暂无图片

聊聊----oracle、mysql事物隔离级别实现原理

4323

文章前传

前几天闲来无事参加了国内某大型互联网公司的面试,作为候选人一路勇敢闯关,最后一关还是被人kick out了。审判日终面,考官看面相应该是个90后,面试我这个80后,我多少感受到了一些来至内心深处并隐藏起来的歧视。上来第一个问题让我阐述mysql 和 oracle RR隔离级别的实现。我顿时有点蒙圈,毕竟平日里工作主要是产品和技术的应用研究,原理这样的类似学术研究已经不多了,若在早几年的话,还是有把握回答的。我给的答案自然不是连续的,有逻辑性的。今天有空把问题整理一下发出来,一则整理认知,二则普及一下,我尽量以最简单的语言来阐述。

从实用角度出发-理解隔离级别

什么是数据库的read场景呢?

image.png
==>有这样一张表如上图

脏-读
image.png
==>2个事物先后发生,事物1居然能读到事物2没提交的内容。

更新丢失-读
image.png
==>由于时间顺序原因,最终事物1得到了自己的结果,而事物2把钱弄丢了,原因是事物2比事物1早提交了!

非重复-读
image.png
==>事物1在两个时间点读到的内容居然不一样,期间事物2进行了更新

幻像-读
image.png
image.png
==>事物1在两个时间点读到的记录条数不一样,因为事物2在两次查询期间进行了插入或删除

什么是事物隔离级别

未提交读,这是最低的隔离级别,意味着当前事物可以从其它事物读到未提交改变。
处在未提交读隔离级别的事物都可能出现在4种读场景中(脏读、更新丢失读、非重复读、幻想读)

提交读,事物只能读到提交的事物,即提交读隔离级别仅仅防止脏读现象,发生,无法保证更新丢失读、非重复读、幻想读的场景。

重复读,事物会持有读锁和写锁,即重复读隔离级别防止非重复读场景发生,简单理解就是事物1查询的时候,都不允许另一个事物2对事物1 进行变更。重复读隔离级别无法保证幻想读场景。

序列读,该隔离级别是最高的级别,事物持有读锁和写锁之外,还会加上范围锁定,这样的话在事物处理的有效记录范围内,幻读场景不可发生。

image.png
==>已上可见图表,读者们一定要区分场景 和 隔离级别,隔离级别就是针对场景而存在的。

从原理角度出发-理解隔离级别实现

事物隔离级别本质上就是防止ANSI定义的几种事物使用场景,具体的隔离级别和事物场景已经在上文中给出。其实呢这些隔离级别和事物场景没有准确而完全的统一定义。不同的数据库产品有着各自的实现。下面我们分别对市场上的一些主流产品的隔离级别实现做个分析。

共性知识

众所周知隔离级别的实现底层都是凭借着lock实现,各种类别的锁。

简单概念解释

Item lock或 Row lock:锁定访问中的行,防止脏读

Predicate lock(gap lock):锁定一个搜索的范围,如果是全表扫描,那么将锁定整个表防 止幻像读

Short duration:在一个语句执行之后释放

Long duration:在一个事物处理完毕之后释放

image.png

==>以上,可见概念中提及锁操作组合之后便实现了事物的隔离级别。注:S代表共享锁;X代表独占锁。所有的写操作必须是X locks+Long duration的组合,若使用X locks+Short duration,那么势必会产生脏写的现象,就是说事物1已经写入但还没提交的数据会被事物2覆盖掉。

针对所有的写入行为会有3种组合的情形

Short duration + item S locks :该情形是如果一个读的操作发现一个行正在被修改(X lock),那么读操作会在S lock上锁定,写入的事物结束,读锁 S lock释放。

Long duration + item S locks:如果一个写入的事物发现一个读操作(S lock),那么写入的事物会在X lock上锁定,读事物结束,写锁 X lock释放。

Long duration + predicate or table S locks:当一个基于范围写入事物遇到一个基于范围的读事物(predicate S locks )时候,那么写入的事物会被锁定,读取事物结束,写入事物可以继续。

image.png
==>以上,纵向列表示场景,其中fuzzy read= non-repeatableread(非重复-读),横向列表示基于锁的隔离级别。

综上知识和概念,出于性能的考虑每个产品都是基于lock实现的隔离级别。更进一步使用MVCC+LOCK的方法,读不会被锁。这是一种非常流行的实现方法哦。

Oracle隔离级别的实现

1、依据官方资料,oracle仅仅支持read committed 、 serializable isolation 、READ ONLY isolation 隔离级别。

2、为了实现MVCC,oracle内部使用scn作为依据,事物提交的时候scn会发生变化,特别地一个读操作也会获取最新的scn哦。在oracle内部mvcc的实现把数据块分成了两类:current block–最新的+持久的数据块;Consistent read blocks–基于scn快照的一致性数据块。

3、oracle 的read committed隔离级别是每个语句都要获取最新的scn,且所有的读操作都是基于快照的读,写入需要row lock。两个写事物操作同一行数据的时候,互相不会阻止对方的操作,当第二个事物获取到需要的行锁之后更新操作成功,基于上面写入的方式,根据事物提交的先后会出现lost update的情况。该隔离级别是与语句级别的读一致性。

4、oracle 的serializable isolation隔离级别依旧是所有的读请求是基于scn快照的,写操作也同样需要row lock。两个写事物操作同一行数据的时候,互相不会阻止对方的操作,当第二个事物获取到需要的行锁之后不是立即做更新,而是检查当前事物的scn和要操作的行的最新scn,如果最新更新的行的scn大于当前事物的scn,那么当前事物报错,这样就避免了lost update的情况发生了。该隔离级别是事物级别的读一致性。

小结:oracle read committed实现了语句的读一致性,避免了脏读的现象发生,读永远不会被阻塞,无法避免丢失更新读、非重复读、幻象读等现象发生。Oracle serializable 实现了事物的读一致性,避免了脏读、丢失更新读、非重复读、幻象读等现象发生,并且读永远不会被阻塞。

Mysql(INNODB)隔离级别的实现

1、根据官方资料,innodb支持ANSI定义的4种隔离级别。

2、Mysql 的Serializable isolation,这种事务的隔离级别下,所有select语句都会被隐式的转化为select … in share mode,如果有未提交的事务正在修改某些行,所有读取这些行的select都会被阻塞住。注意比较:同样是 Serializable isolation隔离级别,oracle的读是不会阻塞的,mysql 的读写都会阻塞

3、 Mysql 的Repeated Readisolation,在该隔离级别下普通的读都使用mvcc方式,即不加锁定的无阻塞读取。相对于select for update、delete、update等操作,若使用唯一索引,那么使用item(row) lock;若不使用唯一索引进行范围扫描,那么使用 Predicate lock,mysql里的昵称叫gap lock。基于以上使用锁的方式,mysql在此隔离级别防止non-repeableread 现象发生。注意:oracle 没有这个隔离级别哦,innodb的RR隔离级别依然没有防止lost update现象出现

4、Mysql 的read committed隔离级别和oracle的方式一致的,不需要单独解释了

总结:一般在使用mysql的场景中没企业或组织使用Serializable isolation和uncommitted isolation,因为Serializable isolation读和写都互相阻塞,会大大影响并发;uncommitted isolation因为会读取脏数据,会大大影响数据库的一致性。read committed isolation和oracle的使用机制是一致的,所以被广泛采用。较为鸡肋的是Repeated Read isolation,mysql使用了一套自己开发的锁来实现,能防止脏读、非重复读、幻象读现象,不能防止丢失更新读现象。相比read committed,Repeated Read降低了并发能力,换来的仅仅是防止非重复读现象有点得不偿失。

最后修改时间:2021-09-28 17:18:20
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
2人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论