草庐IT

事物的隔离性和MVCC

时有限学无涯 2023-03-28 原文

事物的隔离性

mysql的服务端是支持多个客户端同时与之连接的,每个客户端可能还并发了好几个连接,所以mysql是需要同时处理很多事情的,每一件独立的事情就叫做事务。我们知道事务有一个叫隔离性的特性,隔离性理论上是指在某个事物对某个数据进行访问时,其他的事务就应该排队知道访问数据的事务提交才能继续访问该数据。但是这样对性能的影响就太大了,但是我们又必须保持一定的隔离性,所以就需要折中一下。

事务并发可能的问题

先来看看不保证绝对的隔离性会遇到哪些问题呢

  • 脏写

如果一个事务修改了另一个未提交事务修改过的数据,这就意味着发生了脏写。

  • 脏读

如果一个事务读到了另一个未提交事务修改过的数据,这就意味着发生了脏读。

  • 不可重复读

如果同一个事务中能读到其他事务提交后的最新值,这时其他事务对这个数据进行的每次改动都会让该事务读到不同最新值,这就意味着发生了不可重复读。

  • 幻读

如果一个事务根据某些查出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,该事务再次用该条件查询的时候,就会查出上次查没查到的数据即另一个事务刚插入的数据,这就是幻读。

小贴士:

不可重复读和幻读确实有点相似,但不可重复读重点在于update和delete,而幻读的重点在于insert。对于前者,要避免只需锁住满足条件的已有记录即可,避免后者就需要锁住满足条件的记录(包括存在的和不存在的),不存在的记录如何才能锁住呢?所以锁的范围需要扩大到满足条件的相邻范围的记录(临键锁)

事务的隔离级别

这时就出现了一个标准用来定义上面说的折中的程度,在SQL标准中定义了4个隔离级别:

  • READ UNCOMMITTED:读未提交
  • READ COMMITTED:读已提交
  • REPEATABLE READ:可重复读
  • SERIALIZABLE:可串行化

SQL标准中规定,针对不同的隔离级别并发事务可以发生不同严重程度的问题

MVCC

MVCC解决了事务并发时读和写同时进行互不影响的问题,从而提升系统性能。mvcc并不能解决完全解决脏读和不可重复读的问题,如果innoDB只有mvcc没有锁,那么当前事务确实没办法读取到未提交的数据,但是可以修改。

对于读已提交和可重复读的事务来说,都必须保证读到的记录是已经提交了的事务修改的记录,所以如果想做到读写互不影响,核心问题就是读取记录时需判断版本链中的哪个版本是对当前事务可见。

MVCC原理

对于innoDB存储引擎来说,每张表中都含有两个必要的隐藏列trx_id和roll_pointer。每次对某条记录进行改动时,都会把旧的版本写入到 undo日志中,然后在这个roll_pointer列中存储旧版本在undo日志的地址,可以通过它来找到该记录修改前的信息,同时将进行改动的事务id写入trx_id列。

 

在可重复读的隔离模式下每个事务都会生成一个 ReadView ,主要包含4个重要的内容:

m_ids :表示在生成 ReadView 时当前系统中活跃的读写事务的 事务id 列表。

min_trx_id :表示在生成 ReadView 时当前系统中活跃的读写事务中最小的 事务id ,也就是

m_ids 中的最 小值。

max_trx_id :表示生成 ReadView 时系统中应该分配给下一个事务的 id 值。

creator_trx_id :表示生成该 ReadView 的事务的 事务id 。

有了这个 ReadView ,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己 修改过的记录,所以该版本可以被当前事务访问。

如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 前已经提交,所以该版本可以被当前事务访问。

如果被访问版本的 trx_id 属性值大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 后才开启,所以该版本不可以被当前事务访问。

如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断 可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对 该事务完全不可见,查询结果就不包含该记录。

上面两段引用来自于《MySQL是怎样运行的:从根儿上理解MySQL》,我觉得已经很明白了。

 

我写文章都是力求正确,但水平有限,欢迎大佬斧正。

 

相关资源:

MySQL是怎样运行的:从根儿上理解MySQL

 

 

 

 

 

 

 

 

 

 

 

 

有关事物的隔离性和MVCC的更多相关文章

  1. ruby-on-rails - 如何使用 ActiveRecord 连接设置事务隔离级别? - 2

    我需要以一种跨数据库(至少是SQLite、PostgreSQL、MySQL)可移植的方式在每个事务的基础上管理事务隔离级别。我知道我可以手动完成,就像那样:User.connection.execute('SETSESSIONTRANSACTIONISOLATIONLEVELSERIALIZABLE')...但我希望是这样的:User.isolation_level(:serializable)do#...end 最佳答案 此功能由ActiveRecord本身支持:MyRecord.transaction(isolation::re

  2. ruby - 我如何在 Ruby 中反省事物? - 2

    例如,在Python中,如果我想获取一个对象的所有属性,我可以这样做:>>>importsys>>>dir(sys)['__displayhook__','__doc__','__excepthook__','__name__','__package__','__stderr__','__stdin__','__stdout__','_clear_type_cache','_current_frames','_getframe','api_version','argv','builtin_module_names','byteorder','call_tracing','callsta

  3. javascript - 如何将数据从隔离作用域传递到父作用域? - 2

    我对使用AngularJS还很陌生,我想做的是创建一个指令并从其中的父范围调用函数。我能够做到这一点,但我似乎无法弄清楚如何通过表达式将数据从隔离范围传递到父范围。Angular开发人员指南中对此的解释有点令人困惑。指令:app.directive('myDir',function(){return{restrict:'E',template:'',scope:{parentProp:'=property',parentFunc:'&func'},link:function(scope,element,attrs){}}});标记:Controller:app.controller('

  4. javascript - AngularJS 指令隔离范围不更新父级 - 2

    我有一个有模型绑定(bind)的指令,当通过ng-click调用save()方法时,父范围不会更新,除非我调用$scope.$apply()然后抛出$applyalreadyinprogress错误。我正在使用ngResource,事件有一个监听器调用$scope.model.$save();有解决办法吗?还是我做错了什么?.directive('editable',function(){return{restrict:'AE',templateUrl:'/assets/partials/editable.html',scope:{value:'=editable',field:'@f

  5. javascript - 具有隔离范围的 AngularJS 表单验证 - 2

    我想使用AngularJS提供的内置表单验证。但是,在表单中,我使用的是自定义指令,每个指令都有一个隔离范围。因此,表单元素无法访问绑定(bind)值。知道如何解决这个问题吗?或者,是否可以在不使用表单的情况下使用AngularJS验证?ng-minlength和ng-required指令不会触发表单验证。PleaseentersomethingPleaseenteravaluegreaterthan1Pleaseentersomethinglongerthan1digit{{myForm.myElement.$error}}varapp=angular.module('myApp',

  6. javascript - 隔离最左边的 1 位 - 2

    我正在搜索如何隔离二进制中最右边的位:我得到了这个解决方案:y=x&(-x)所以:10111100(x)&01000100(-x)--------00000100但是现在,我想通过找到最最左边的数字(虽然不是符号...)来找到数字的大小如何详细说明我的解决方案以找到最左边的位?例子:1011110001000100 最佳答案 没有类似的O(1)按位技巧来查找数字的大小。许多微处理器指令集包括一个特殊指令来“计算前导零”。在赋予JavaScript按位功能的C语言家族中没有这样的运算符。唯一的O(1)替代方案是使用Math.floor

  7. javascript - 隔离范围 "="绑定(bind)和点符号 AngularJS - 2

    如何在带点符号的独立作用域中使用嵌套属性创建双向绑定(bind)。我认为'myObject.data':"=data"会工作,但事实并非如此。我不想链接myObject对象中的所有内容。我知道我可以做一些观察,但'myObject.data'看起来更干净。.directive("myDirective",[function(){return{restrict:"E",scope:{'myObject.data':"=data"},link:function(scope,element,attrs){scope.myObject={data:"myValue"};}};}])

  8. javascript - OOP Javascript - 在类中隔离对象 - 2

    我正在尝试拥有一个主对象,我可以为其创建多个实例,每个实例都继承子对象(具有独特/独立的属性)。但是,当我这样做时,所有创建的对象的对象属性(更改后)都会发生变化。我可能没有正确解释这一点,但这个例子应该很清楚。Main=function(){};//Extendingthemainclasswithnewobject.DoingitthiswaysoIcanhavethesein//separatefiles.Main.prototype.foo={bar:1}//FirstinstanceofMain().varA=newMain();//SecondinstanceofMain(

  9. 云原生场景下的容器网络隔离技术 - 2

    云原生场景下的容器网络隔离技术一、研究背景随着云计算时代的到来,尤其是容器化技术的飞速发展,云原生作为云计算的未来阶段,其安全势必成为云安全的主要战场。从目前的云原生环境来看,云原生网络安全问题层出不穷,威胁程度逐渐上升,从业人员面临着严峻的挑战。例如,此前Akamai公司进行了一项实验,将一个简单的Docker容器蜜罐用于攻击测试,结果显示该容器在24小时内被攻击者用于四起不同的犯罪活动,这些攻击的目的各不相同:一起攻击试图使用容器作为代理,以访问数据流或其他服务,另一起企图让目标感染僵尸网络,还有一起执行加密货币挖掘,最后一起是通过容器针对居家办公用户实施诈骗。此外,2018年特斯拉AWS

  10. javascript - AngularJS 1.4.3 ngRepeat 在具有隔离范围的指令中使用时打印 {{ variableName }} 而不是值 - 2

    在我们将项目的AngularJS从1.2.28升级到1.4.3后,隔离范围内的ngRepeat停止评估变量。它开始打印“{{variableName}}”而不是值。编辑:问题是由iOS8Webkit错误的补丁引起的:https://github.com/angular/angular.js/issues/9128问题来自ngRepeat周围的包装HTML元素。如果我删除它,它工作正常。如果我删除隔离范围,它也会起作用。最奇怪的是,我在CODEPEN上尝试了相同的方法,它工作得很好,但在我们的项目上却不行。这是codepen代码:http://codepen.io/anon/pen/YX

随机推荐