数据库隔离级别的由来 及其与MVCC的关系

本贴最后更新于 3320 天前,其中的信息可能已经沧海桑田

我们都知道根据SQL92标准,关系数据库有四个隔离级别:脏读,读未提交,可重复读,可序列化。但这几个隔离级别是怎么来的?我们来扯扯把

 

如果数据库像Redis一样是单线程 串行处理所有的事务的话,我们就没有隔离级别这茬事了,或者说隔离级别都是最高的 可序列化。但是我们知道,数据库是一个磁盘IO很高的系统,尤其是用机械硬盘的时候,如果串行处理事务,那么就会导致磁盘不断寻道,处理效率低下。因此积攒一堆读写然后批量读取写入机械磁盘这会大大提高数据库的吞吐量。再加上现在CPU都这么多个核,多线程是必不可少了。

 

但多线程会带来一个问题,就是并发。你想象一下,在一个事务A里,你根据事务B未提交的记录做了一个判断并写了数据库,然后B回滚了,你会有怎样的心情~~于是乎数据库系统决定帮助你处理这个问题,提出了各种层次的隔离级别,让你读取数据的时候感觉就像只有自己在访问数据库一样。(NOSQL产品则没有这些隔离级别,甚至也没有ACID中的A-原子性,因此他们数据之间的关联关系很少,易于水平扩展,要扩展性能的话,加机器就可以了,但有得有失,用NOSQL实现复杂逻辑的时候 就要自己处理掉 事务的原子性,并发时候的脏读 等问题。)

 

数据库决定要让我们读取数据的时候感觉就像只有自己在访问数据库一样,要怎么搞呢?我们先从单线程处理所有事务的场景里开始着手,看看哪里可以改进变成并发把。

 

当两个事务里完全没有处理相同的记录的情况下,他们是天然可并发,无需额外处理。但是如果两个事务里存在相同的记录怎么办?我们分析下,对于同一条数据库记录,在不同的事务中仅存在以下几种关系:

一个事务读了,后续的另一个事务要读,简称读读

一个事务读了,后续的另一个事务要写,简称读写

一个事务写了,后续的另一个事务要读,简称写读

一个事务写了,后续的另一个事务要写,简称写写

 

先看读读,哎,好像天然可并发,怎么搞看起来都是像只有自己在访问数据库一样,棒棒哒。略过。

再看读写,第一个事务读过了,然后第二个事务要写,如果允许写的话,第一个事务不就蒙B了,这哪里是我一个人在访问数据库?!好吧,这样的话 ,根据我们程序员最直观的思维,给这条记录加个锁就好啦(行锁),每条读取的记录都要上锁。上锁后其他事务就不得读取这条记录了,除非等第一个事务完结。但这样读读关系不能并行了,于是大家都想,读读而已呀,这必须能并行呀,能提高不少性能呢。于是乎程序猿们继续发挥聪明才智,设计了读写锁~读读可并行,读写不可并行,这样就OK啦,大大提高了并行的效率。

这个做法嘛,对应的就是我们数据库隔离级别里的 可重复读。这个可重复读嘛,有一个BUG,就是所谓的 幻象读(不知道怎么能起到这么一个名字….)。这个BUG的原因如下:行锁是基于已有记录的,但如果记录不存在的话,怎么搞?如:数据库里只有2条记录,id分别为1、2;事务A第一次执行语句select * from user where id<3 有2条结果,并对这两条记录加了锁,但因没有第三条记录,所以无法加行锁。这时另外一个事务B插入了一条记录把id=3的记录插进去并提交了,那么事务A再次执行上述SQL的时候就会得到与第一次不一样的结果。看来可重复读….其实也并不可重复….

 

虽然引入了读写锁的机制后,数据库性能得到了大大的提升,但是人类追求效率的心是永无止境的,现在在上述 几个关系中,已经实现了 读读关系 并行处理了,那么其他关系能并行么?

现在有读锁和写锁,如果允许某个读锁升级成写锁的话,那么 读写关系也可以并行了,这个时候带来的负面效果是 之前一同获得读锁的事务们 再次读取这条记录的时候,有可能读到 获得写锁的事务更新的数据,于是这个隔离级别我们就称为 读已提交。

 

再继续放松限制,完全去掉读锁,也就使得 写读 也能并行了,这时候,数据库隔离级别就变成 读未提交。这时候保留的写锁,是为了保持ACID中的A,让其能回滚。

 

基于上述的实现方式,专家们定义了4种隔离级别。可见,这个隔离级别并没有其他特殊的含义,仅仅是局限于当时的技术实现形式而已。

 

在后来出现了MVCC这种基于复制记录形式的并发处理形式(具体实现自行再百度吧),指定隔离级别的专家们就蒙B了,MVCC可以做到比 可重复读更高的隔离级别(没有幻象读),且在读多些少的场景下实现比 行锁 机制里 读已提交,可重复读 更高的效率。

但由于 隔离级别在之前已经深入人心了,甚至于有些程序还利用了RC的特性来编写了程序,因此基于MVCC实现的数据库通常也会对应回四种隔离级别:可序列化(不使用MVCC),可重复读(使用MVCC,对应于SQL92里的可重复读,但不会出现幻象读),读已提交(故意减少MVCC的隔离程度,映射回RC这个隔离级别,这种实现相对于RR或许会更消耗数据库性能),以及读未提交。

大家普遍存在一个误区,隔离级别越高,数据库性能越差,这在行锁对应的数据库实现里,是正确的。但在MVCC里可就不一定了,个人认为RR的性能会比RC高,或者至少相差无几。

 

嗯~写完,OVER~

  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    345 引用 • 742 回帖
  • 一致性
    10 引用 • 5 回帖
  • MVCC
    4 引用 • 4 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    345 引用 • 742 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    180 引用 • 408 回帖 • 489 关注
  • 安全

    安全永远都不是一个小问题。

    203 引用 • 818 回帖
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 568 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • abitmean

    有点意思就行了

    37 关注
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 177 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    146 引用 • 972 回帖
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 120 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 3 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    88 引用 • 139 回帖 • 1 关注
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    367 引用 • 1844 回帖 • 4 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1280 回帖 • 4 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 57 关注
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    66 引用 • 114 回帖 • 198 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 55 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 6 关注
  • V2Ray
    1 引用 • 15 回帖
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 1 关注
  • CodeMirror
    2 引用 • 17 回帖 • 161 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    133 引用 • 1124 回帖 • 115 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 812 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 30 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 786 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 547 关注
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    54 引用 • 37 回帖