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

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

我们都知道根据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% 的性能瓶颈都在数据库。

    330 引用 • 614 回帖
  • 一致性
    10 引用 • 5 回帖
  • MVCC
    4 引用 • 4 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖 • 1 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 595 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 49 关注
  • 知乎

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

    10 引用 • 66 回帖 • 1 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    173 引用 • 990 回帖
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 18 关注
  • Spark

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

    74 引用 • 46 回帖 • 547 关注
  • PWA

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

    14 引用 • 69 回帖 • 131 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 24 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    311 引用 • 546 回帖 • 33 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 109 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    124 引用 • 580 回帖
  • abitmean

    有点意思就行了

    24 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    60 引用 • 287 回帖 • 2 关注
  • SpaceVim

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

    3 引用 • 31 回帖 • 74 关注
  • Solo

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

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

    1425 引用 • 10043 回帖 • 470 关注
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    83 引用 • 165 回帖 • 40 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 53 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 421 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 8 关注
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    205 引用 • 357 回帖 • 1 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 114 关注