00:13
好,谢谢小树,下面由我来进行分享,先给大家做个自我介绍啊,我叫孟庆忠。啊,是博士毕业于中国人民大学,然后现在是腾讯云数据库高级工程师。目前从事这个T数据库内核的一些开发工作,除了对这个数据的存储系统,然后查询优化执行器之外,我本人对这个Linux操作系统的内核代码也有一些了解。啊,下面开始这个今晚正式的分享。本次分享的主题就是这个数据库事务一致性的实现。啊,今天晚上主要这个分享就分三个部分。第一个部分就是先介绍一下这个事物的基本概念以及特性。第二就是主要的隔离级别以及实现。第三个是TDC。
01:03
事物一致性的实现,现在先看这个第一个部分。就说什么是事物。是不就是用户定义了一个数据库的操作序列?这个操作要么全做,要么全不做,它是一个不可分割的工作单位。事物它有四个特性,就是ACD,这个A表示原子性,就是事物中的,事物中的这个操作就要么都做,要么都不做,就你不能做一半。一致性就是这个事物的执行结果,必须使数据库从一个一致性状态转移到另外一个一致性的状态。隔离性就是说这个并发执行的事物之间不能互相干扰。持久性就是说你这个事物一旦提交,它对数据库的改变应该是永久性的,就是说只要是提交以后,你就不能反悔了,这里面这个A和这个D,就是这个原子性和持久性,其实是比较直观的啊,应该大家都比较容易理解,这里面就是说不那么直观的,就是这个C和I,就是可能会。啊,理解起来,甚至可能有的人的理解可能都不太一样啊,我今天就下面就介绍一下我的一些理解啊。
02:07
就首先看一下这个关于这个一致性的理解,一致性它其实是偏应用角度的特性。啊,每个应用程序它需要自己保证他自己他这个现实意义上是一致的。然后数据库在这个一致性的方面哈,对应语程序能做出的保证就是你只要是这个事务执行成功,我都不会违反这个用户定义的一些完整性约束。这个数据库的内核,只要是在这个执行你这个事务的过程中,只要他发现没,他没有发现你违反了这些约束,那么数据库内核就认为其实就是一致的。至于这个现实意义上是不是一致哈,需要由这个应用程序自己来判断。啊,那么这个常见的完整性约束有哪些呢?就是我就列了下面这几个啊,由这个主键约束,然后外键约束,唯一约束。就是非空约束,还有个check约束,只要你定义了这些约束,那么这个数据库系统在运行的时候,它一定不会违反这些约束,它只要是这些约束没有违反,那数据库内核就认命啊,OK,没问题,至于上面是不是对的,那数据库内核就不管了,是应用上面自己的事。
03:16
啊,就是说为什么这个数据库可能会导致不一致呢?其实就是还是由这个由于这个冲突导致的,什么叫冲突呢?就是这个不同事物对相同的数据进行操作。并且其中一个操作是写操作,那么这两个就是冲突了。啊,就是说两个操作,那操作就是只有读和写嘛。两个册子里面至少有一个写,然后要么就是就是看看下面这几个嘛,列出来了啊,就在这儿,就是这个写写冲突,就是两个写,就是你第一个时候写了。然后第一个事物还没有提交,然后第二个事物过来又把它写了,就等于把它把第一个事物写的东西给它覆盖掉了,这个叫脏写。然后这个第二个这是一个事物了。
04:00
然后第二个,第一个事物写了之后,他没有提交,然后第二个事物直接就读到了这个。读到的第一个事物写的东西。这就是读人,他还没有提交呢,又读了,这个是脏读,不可重复读,就是就是说第一个事物读了一遍,他读的这个数据他被第二个事物给修改了,等他再读的时候,他发现不一样了,这个就是那个不可重复读。啊,最后一个是。换读这个这个R是读这个W写啊,这个P啊,就是这个是谓词,这个这个是词的意思,Predict。什么叫词?你看这个C和语句就是select from t等于十等于十,这个条件其实是一个谓词条件,这就是这个括号里面这个P。这是啥意思呢?就是说第一个事物,他先用A等于十去读这个表,然后呢,假如说它读出来十行。他读完十行之后。第二个事物它又往里面插,比如又插了两两行,又插了一个A等于十的这么两行进去了。
05:00
然后你这个第一个时候再读,再读这个A等于十,它其实读出来是12号。就就多了,所以说就这这个是幻图,就像幻影,就像是那种幽灵一样的东西,就是说我刚才没有了,怎么现在又有了。啊,这个换读和这个不可重复读,它的差别就是哈。呃,换读这个不可重复读,就是我最开始读那个东西,我最开始读了一遍,我到第二次再读它变了,而这个换读是我最开始读的那个,到第二次读的时候,它没有变。但是它多了,它比第一次多了,这是换读和不可重复读。啊的区别。啊,主要是异常这么几个哈,这是异常。嗯,其实这个并发执行的这个事物产生冲突啊,它就类似于这个科幻小说里面。那个两个不相融的物体,它进入了同一个时空。那么它既然是进入了同一个时空嘛,时空,时空就是时间和空间嘛,是两个维度,它在时空上面冲突了,那你就从两个维度去,可以从两个维度去解决,第一个就是从这个时间的维度。你就两让这两个操作从时间维度隔开,你不让他们同时访问,那他不就不冲突了吗?
06:05
比如你类似于排队对吧,从时间上隔开排队,基于锁的时间的并发控制,其实就是从时间维度上解决这个问题的。呃,第二个就是从空间维度。你把这两个操作从空间维度隔开的话,就是让他们访,不就是不让他们访问同一份儿数据,你让他们访问两份数据,那他不就不冲突了吗。啊,基于这种思想实现的,其实就是基于多版本实现的变化控制,它就是从空间维度来解决这个问题的。啊,现在我们讲讲的是这个与一致性有关系的哈,下面就看一下这个隔离性。啊,首先先先看下这么一个情况,这个现象就是有一些有些应用程序的执行逻辑啊。啊,它其实永远不会导致前面我们说的这些异常的产生,在这种前提底下哈,即使你这个数据库允许了某些异常,但是实际上最终他也不会产生这些异常,那个数据库仍然一一致的,怎么理解这句话呢?你就把这个上上层一个软件的模块调了一个下层底层的软件模块,这个底层这个软件模块它是有bug的,你你就认为这个底下这是数据库它是有bug的。
07:09
上面这个上层模块,这个就是你的应用软件,哎,就是说虽然你这个底下有bug,但是我上面不踩你这个坑,我永远不会触发你这个bug,哎,那它底下它不就不就跟没bug差不多嘛,对吧,跟没bug一样嘛,它这个数据库叠肯定它也会一致嘛,它也不会导致不一致。啊,我们先介绍了这么一个,这叫啥,这这么一个思想吧,然后就就看到隔离性,我们刚才就介绍过隔离性了,就是并发执行的事物之间不能互相干扰,诶这句话。那么到底隔离到什么程度,就是怎么定义这个互相干扰?什么叫互相干扰?什么叫不互相干扰?隔离到什么,其实就是说要隔离到什么程度呢,你完全隔开呢,肯定是最好的嘛,但是它那个效率不一定好,然后这个C和标准啊,就为了这个,呃,这个数据库系统啊,能提高一下这个系统运行的效率,它就其实这个标准就在这个隔离性上面,他做了妥协了。
08:06
就是说我允许你这个数据库产生某些异常啊,说的不好听一点,你就说我可以允许它有bug,你就呃,你可以,你也可以把它叫做bug,一个叫那个叫的好听点,把它叫做feature,对吧,这个叫feature。呃,就是允许某些异常的产生,到底我隔离到这个程度是什么样子的呢?它其实由我们后面要讲的这个隔离级别来确定的。啊,我不知道讲到这儿大家是不是有点晕哈,我现在举一个比较直观的例子,让大家来来来更好,来来来感受一下这个事。就说,就想象一下,老师可能有一天就对着学生说了,啊,大家同学们,你们要统一着装来学校,哎,他说了这么一句话,那肯定就得说呀,什么叫这个统一着装呢?我最基本的要求可能就是你穿的上衣和下衣必须是一样的,这是第一层要求。第二层要求呢,我就是在这个第一层的要求的基础上,上下衣不一样,你鞋子也必须一样。
09:03
第三层要求呢,就是在第二层的要求上,哎,你还得必须大家都得戴一样的手套。就说这个对,就你看就对这个老师说的这么一句话叫统一着装,它其实可能就是有这么三个不同的级别。其实就对应上这个数据库,这这其实就对应了数据库到底提供什么样的隔离级别,这个这个这三个是跟数据库这个是对应的啊。然后你到底是采用哪一种解释,可能是与你这个任务是有关系的。假如说你这个这个老师只是说为了在比如说组织一下,组织一下,组织一次这个课堂的直播。啊,你在摄像头里面可能也就能看到上下一吧,你执行这个第一啊,第一层这个要求就可以了,如果你要是为了拍个毕业照,可能要拍到大家的鞋子的话,你可能就。就需要执行这个第二层的要求了。啊,你如果要是比如说你像阅兵或者在什么军训要走方阵。他可能要执行这个最严格的这个要求,就是还要带上大家戴白手套都戴的都一样。
10:03
嗯,大体就是这么个意思,这个这个底层的要求就是跟那个数据库这个隔离级别是对应的,我们下面将会将会讲。啊,你你只要是理解了这个底下这个这个例子的话,我相信对这个后面的隔离级别的要理解应该会。会更容易一些吧。啊,我们先对这个第一部分做一个小结哈,第一部分我们介绍了一下这个事物的概念,介绍了这个事物a cid4个特性,啊,介绍一下这个关于这个一致性的理解,还有就是说导致不一致的原因,以及这两种解决方式,就是从时间和空间上两种解决方式。最后我们介绍了一下这个隔离性的理解,还介绍了啊,还举了一个例子。啊,下面我们就进入这个第二部分啊,这就是今天晚上应该算是一个啊,重点难点吧。啊,就是介绍一下这个主要隔离级别事情刚才不说了吗?他可能隔离的那个级别是不一样的嘛,就看到底有哪些隔离级别呢。
11:01
其实这个隔离级别啊,隔离级别就是这个还是就是并发执行的事物,他们互相能看到对方的多少。然后这个C考标准,这个上面这个图是C标准给出来的定义。它是用什么定义的,它是用。我这个数据库到底禁止什么样的异常定义的?这上面是三种异常。啊,就是还是刚才说的就是脏读,脏读就是我第一个事物写了,但是没有提交,第二个读到了,第二个是不可重复读,就是我第一次读了一遍,然后后面有其他事物改了,我再读就不一样了。啊,第三个这个换读还是就是我第一次读,比如读出来十行,我后面再读它多了。OK,有了这三个异常之后,然后这个C标准就开始定义这个。隔离级别了,所它C标准一共定义了四种隔离级别,就是read UN committed read committed repeatable read和这个s levelable,你看这个名字就能看出来这个read UN了嘛,就是读未提交的事物,而且对就读未提交,就是说你这个事物没有提交,其他的事物也可以读到。
12:07
他当然就肯定就是允许这三种异常都有可能发生啊。比他更严格一级的就是这个read committed,就是读提交,他肯定就是不能读这个未提交这东西了,就这个脏他就避免了,但是后面。这个不可重复读啊,和这个换读它都是有的,呃,更严格一点的话,那么它就连这个不可重复度也给禁掉,就是我的数据库支持的是可重复读,我不会发生不可重复读,OK,你只要是能把前面这两个现象给禁掉的话。这两个异常给禁掉的话,那么你就是这个repeatable read的这个级别,最后一个able就是可性化。只要是这三种异常都不会发生,OK,我就说你这个数据库是liable,就是可创新化了。啊,注意哈,这个可串行化,它是说一堆并发执行的事物,最后执行的结果看起来像是一个一个执行完的,他这句话的意思是这样的,就一堆大家并发执行,但是最后的结果。
13:03
是跟某一个创新执行的结果是一样的。啊,其实上面这个定义哈,它是当时这是30年前定义的,就是但当时来源于这个所实现的变化控制。但是他定义的时候吧,他给上面这个定义的时候,他又想摆脱对这个所实现的依赖,他就想定义出来一个与这个实现无关的标准。所以说他就取了这么一种方法,就是我根据你这个数据库到底不允许哪些异常,来定义这个数据库的隔离级别。啊,它其实这个这个定义是稍微有点问题的哈。啊,基于这个锁的实现的变化控制哈,可以完美的匹配上面这个定义,但是。这个上面这个定义就不一定能匹配其他的实现方式了,比如你像现在常见的这个快照隔离叫SI,叫snapshot isolation。它这在这个SI这个隔离级别底下,上面这三种异常都不会发生,它能保证就这种隔离级别是能保证这三种都不会发生。
14:03
你如果按照上面这个定义来讲的话呢,它就是C阿AB了,它就是可穿性化了,但是哈,它可能会发生其他的异常,叫写倾斜的异常,这个血倾斜的异常在这上面这三种里面都没有的。所以说它最终这个快照隔离,它并不是真正意义上的可创新化。啊,这是说了这上面的一个问题哈,这个定义的问题,然后它这个定义哈,它使用的是这个自然语言,就是就是英语,就是就像我我今天在这在这直播一样,它就是呃大白话,你理解成就大白话,它不是一种那种程序的非形式化的语言,那个形式化的语言的话,它是一就是一,是二就是二,没有第三种没有歧义。但是这个自然语言就可能会导致人们理解有歧义了,你这个C考L标准都说了,只要是数据库不会发生这三种异常,那它就是C拉的,那有些数据库它就是这么叫的。有些系统它就直接把这个快照隔离就称为可重新化了,你只要是在那个数据库底下医疗把它设成可创新化,OK,他提他提供的那个隔离,其实就是这个快照隔离,它没有上面这三种异常,但是它有写倾斜的问题。
15:08
啊,OK,那么我们介绍了隔离级别之后,我们先来看一下基于所实现的变化控制。啊,这张图就是还是节节选那那篇论文的话,就是那个。呃,我在参考文献里面了,我等会儿会介绍一下。啊,这个是基于锁身的病患控制,这个隔离级别是怎么实现的啊,首先我们是先看一下这个锁是有多种类型的。这个所有读,所有写锁,还有这个位词锁,位词所predictlo,这个位词锁就是锁那种A等于十这个条件,它锁一个范围的,那么读锁写锁它都是有学,都是锁那么一个数据的。还有的时长它也是不一样的。啊,它有可能是这个操作完了这个数据之后,他直接把锁放了,比如读数据,比如读索,我在读一个数,读一个数据之前我加上读锁,我读完了数据之后,我接着放了,呃,另外一种就是事物的结束才放锁,就是我。
16:01
比如说我读这个数据或写数据之前,我先把锁拿到手里面,我这个锁一直持着不放,一直到什么时候事务结束了我才放。啊,这个图里面的底下这个well form的read和right啊,它的意思就是说我读写数据之前都要加锁,比如我读数据之前我要加个读索,我写数据之前要加个写锁。啊,这个囊呢,就是我读的时候根本就不加锁,它就这么个意思。OK,有了300,呃,上面这三个前提,我们就看一下这这几个隔离级别,它这里面列了六个,我们今天看四个,就是因为它前面这个标准不是定义了四个嘛,我们就看这四个。我们就看啊,这边这个写关于写的操作它都是一样的哈,这四个里面都是写,要要写一个数据的话,要先拿到写锁,然后这个锁要事务结束的时候才放。这这个这个这个这个这个都是这样的哈,这个所以说我们就等会就不看这个鞋索了,写索都是一样的,他这是为了解决那个。
17:01
啊,写写冲突,当然它跟这个读也会能冲突上。啊,主要区别就是就是看这个毒所。这个read UN committed。啊,Read committed的话,它这个读。都是不需要加锁的。呃,大家可以想一下啊,瑞安可比的是不加锁的。那别人写一个数据,别人是加了一个写索。别人加这个写锁,然后事务结束的时候才放锁,那么你读的时候你又不加锁,那很可能就读到别人写了,但是没提交的数据,你根本你他加了写锁,读锁和写锁本来是互斥的,他加了写锁,你读的是不加锁,那直接就读到了嘛。所以是这个瑞科的,它是它是这么实现的。啊,比这个要是更严格一级的,就是基于所剩下的read committee的话,它就是读的时候它需要加一个读锁。诶,他只要是能加上,他只要是能把这个读所给加上,就代表没有其他事物在写,其他写这个事物,写这个数据的那个事物,他一定是提交了的。
18:08
所以说你这个读的话是读不到提交的数据的。因为别人那个未提和事物,它那个。他那个协锁还没有放,他的鞋锁不放,你堵锁就加不上。他其实就从这卡着了,所以说你这个用用这个隔离级别的话。他一定读到的都是committed。然后他这个毒索是什么时候放的,他就是我读完了,我马上就放掉了。你看他读的时候加了一个读索,读完了之后马上放了,那么我下次再读这个数据呢,他要重新再加读锁,哎,你刚才把锁放了,现在又再加,中间就有可能被别人修改过了,所以说这个它读出的数据有可能是变了嘛。啊,这个地方就short duration relo,就是我读完了之后,马上就把这个读索放了,这个bos的意思就是说我这个读所和词所都是这么处理的,就是我读一行还读那个A等于十的那种。
19:00
啊,那种那种数据它是一样的,都是把这个读锁,这两种锁都是读完之后马上放。我们看完了这个之后,我们先看一下最后这个able啊,Able的话,它也是读的时候要加读索。他这个毒是什么时候放呢?他这个毒所是到15提交的时候才放。我这个一般的毒索和位磁的毒索都是要等到十提液的时候再发啊,大家可以想一下哈,如果他读一个数据之前,它加上毒索了。他这个毒索又到事务提交才放OK,那中间别人还能修改它嘛,那肯定是改不了了,因为别人要再改的话,要加写锁,他只要是拿到毒索之后,别人的协索是加不上的。所以他这他这他这就他这就保证了这个东西是不会被修改了,而且这个boss也是这个位磁锁和这个一般的这个毒锁都是到最后放,也是别人你拿到这个位词所不放的话,别人要是比如你刚才读出来十行,你拿着VA等于十啊,A等于十这个条件,你拿到A等于十的条件的话,这个条件上的锁的话,别人再想插入A等于十的这么一行,它是插不进去的。
20:05
啊,所以说这个是那个,这个最终实现的是那个隔离级别,就是C尔拉多,它有一个具体的具体的详细的证明,就是证明为什么这么加锁,最后是那个可换隔离级别,大家可以在那个一般的数据库教材上就能找得到。嗯,最后我们来看一下这个able。这个。这个repeatable readid是比这个readid committee的强,比这个laborable都弱了,那么这个repeatable readid就是RR啊,这个rr repeatable,它比这个RC比强在哪呢?大家看的话,它其实就是这个。它是一般的毒索,它是要最后放的,也是事务提交的时候放,就比如我读一个啊,一行记录,我读上来之后,我拿到这个毒索,我最后放。但是它这个未磁索,它还是当时就是当时就放了,就是A等于十的这种条件的就放了,哎,大家把这个把这个第三把这个RR的隔离体别和这个RC的一比,跟这个就R1比就能比出来了。
21:11
这个就是比这个啊,这个RR可重复读,就是比这个读一提交就多了一个我读所罪犯。然后我这可重不足比可创新化就是少了一个。啊,为此所最后放它其实就是在这个上面放松了一个,就是放松了条件了,所以说这个repeatable是有可能有换读的。OK,基于锁的并发控制,我们先介绍到这里。啊,刚才我们不说那个。解决那个冲突有两种方式嘛,一个从时间上,一个是空间上,时间上就是基于锁的,那么空间上就是基于版本的。极端版多版本的闭环控制,它是一个数据库对象,就比如说一行啊,你看外面逻辑上看起来是一行,但是它的一行是有多个不同的版本,它每一个版本都关联一个时间戳,这个时间戳你就可以理解成它是一个生命周期。
22:04
啊,就。你可以也可以理解成这一个版本的生日。他只要是后面有人把它更新了的话,那他就就多了一个版本嘛,它那个生它那个那个版本的生日会比前面那个版本的生日会再会更新一些。那么一个事物在访问数据的时候啊,就会使用一个快照,选出合适的版本,它比如都是这一行,但是就这一行的不同时间的,比如十个状态在十个时间上啊哎,可能有的人用这个地图哈,那个。就那个地图里面有个街景的东西,它有个什么时光机,就说我这个照片是几几年拍摄的,它有那么个东西,就这个就跟那个差不多,就是说你要到底要访问哪一个版本,这个快照就是来决定,就是来决定那个我要到底选哪个版本的。啊,就是这个多版本并发控制,就叫MVCC哈,叫version control control。就是MVC,它的好处是啥呢?就是读写互不阻塞。
23:00
你看我读的时候,我可以根据你多个版本来读其中一个合适的,写的时候直接从后面再追加一个版本就行了。它的好处就是读写互补,阻塞。OK,我们刚才说了,它每一个版本,它上面会关联一个时间戳,对吧。然后到底这个时间是怎么选呢。啊,主流的其实是有两种方式。第一种方式就是使用这个,使用这个事物的开始时间。啊,这它就属于这一类的系统。啊,就是直觉上想一下啊。如果一个事物它开始的越晚的话。他可能就是结束的越晚,然后它产生的这个版本,它就越有可能把事物开始的比较早的那些产生的版本给覆覆盖掉,也是有可能这个事物开始的越晚,那么它产生的这个版本就越新。这只是直觉上的话,但是不一定他有的,就是可能这个事物开始的晚。但他结束的早。责任就存在这种特例,它为了排除这种特例哈,然后这个post里面,这个它这个快照里面,它这个快照里面就会有一个活跃的事物链表。
24:02
这个列表里面就就告诉我这个事物啊,到底哪一些事物产生了修改,对我这个快照是不可见的。他这个本质原因还是因为他这个。呃,这个版本上面,它这个数据的版本上面是用的这个事物开始的这个时间,它就有它它有这么个问题,他这个快照是比较大。呃,另外一种,另外一种这个时间上的选择,就是选这个事物结束的时间,比如像这个TVC考。那么你一个事物结束的早,那肯定它产生的这个数据就会被后面的事物看到了呀。所以说这类的系统快照法。一般就只需要一个时间戳啊,就大部分情况下就只是一个整数,比如64位的一个整数就搞定了。OK,我们讲了这个实验之后,我们就看看在这种多基于多板实现闭环控制底下,典型的这么几种隔离级别。他这个典型的隔离级别里面就三种,第一就是这个read committed。
25:01
Committee就以提交嘛,它其实有三种实现的,实现的方式。第一种实现的方式就是对每行数据来讲,我随意。读取一个已经提交了的版本。那么我这个肯定是满足定义的呀,因为我这个注的committee的,根据这个根据这个标准的话,我只要是不脏图就可以了,我只要是保证我读的这个数据是已经提交了的就可以,不是正在运营中事物的数据就可以,它这个是满足定义的。但是这个它有个问题哈,它有可能读的太老,假如说你你在你这个数据库在里面读了一个,读了一个版本,他就读了十年前的数据,它虽然是已经提交了,但是又有什么意义呢?所以说这个哈,虽然满足定义,但是在实际的系统里面,大家根本就不用。啊,第二种情况就是我在读的时候,我就读这个数据,最后提交了。这个肯定也是满足RC的定义的。就已经提交了最新的,每一行都是最新的。但是这这么实验的话,它是有一个读偏序的问题,实际系统里面其实也基本上不用。
26:03
啊,这个什么叫读篇序呢?就是说,你只读到了一个事物的部分结果。啊,我这举个例子啊。就比如T1,你这个手机更新了两行数据。但是T2呢?你每次读都读最新的,那有可能T2它是读到了,他只读到了T1更新的其中一行数据,另外一行它可能读不到。因为他可他这个T1更新的时候,他肯定一行一行的更新嘛,你这个T2读的时候也是一行一行的读啊,他有可能T2读第一行的时候发现第一行。没有更新,那么他读到第二行的时候发现啊这个呃,读第一行的时候发现T1没有提交,那他不应该读到,他读到第二行的时候发现H t1提交了,他就读到了,那那这不就是读到了T1的部分更新的数据吗?呃,读到了T一部分部分的结果,这其实这就是读读篇序。啊,就是这个题一对这个数据的修改,我只读到了一半,我没有完全的读到。这个是图片序。
27:00
就是你如果对每行数据都直接读它最后提交的版本的话,它就是有这么一个问题,实际系统里面用的也不多。啊,第三种实现是这个系统里面大家用的都比较多了。它的实现方式是这样啊,每一条C语句开始的时候。他都会去获取一个最新的快照,然后他用这个快照去选一个合适的版本。他这么他用的同一个快照去选的话,他就不会有这个读片序的问题了,他如果在你就再拿刚才这个例子,T1更新了两行,他如果第一行读不到,他到第二行就算T1提交了,但是T1它的T1对这个快照是不可见的,所以说他读不到第二行。他还是读这个老的,他就这个不会有图片区的问题,这个系统里面是比较常用的。啊,这个是针对读来说了哈,呃,针对写的话,如果你在这个LC的隔离级别底下,你要去修改一行。那么肯定是修改最新的版本,你修一个修改一个老的版本的话,它没有意义,它就等于分叉了。
28:00
他要么分叉了,要么就覆盖了。对,这是RC的级别,就是committee,我们他这个第二个比较典型的隔离级别,就多版本控制下啊。叫isol,叫SI,大家细心的话可以注意的,这个SI根本在那个C标准定义那个四种隔离级别里面,根本就没有,这个根本就没有。它跟这个SRSRC考跟前面定义的那个四种的隔离级别都不一样,它都不等价。他这个啊。这个的实现方式就是说事物开始的时候,它获取一个快照啊,通常都是最新的快照了哈,然后他这个事物整个的执行过程中,它就用这么一个快照,它要保证可重复读啊,你要是用要你跟这个RC的话,它每个每条C语就用一个快照,它读出来东西是不一样的,你如果只用这一个快照去读的话,快照就像是你给这个系统照了一个照片一样嘛,你读的话肯定读出来都是那个照片里面的东西。
29:01
呃,你不管这个一个事物里面到底有多少条信号语句。他都有这一个快照。然后这是针对图,然后针对修改的话。你如果去修改一行,你要马上要修改的时候发现,诶这行啊,我要修改的这个这一行已经被其他事物给修改掉了,OK,那我就不能改了。我就直接about掉了,否则的话,他不满足我这个snap isolation,它就不满足我这个隔离级别的要求了。啊,对,这个是没有,对这个没有那个脏读的问题,也没有不可重复读的问题。也没有换读的问题,因为你新插你新插入进来的问题的话。啊,你新插入进来的数据的话,它是不满足这个快照的,这个快照是看不到的,所以说刚才那个刚才说的那那三个。异常脏,毒不毒这个都没。
30:03
但是它有个写程序的问题。所以它所以说它不是可能性化嘛,这个写篇序是个什么问题呢?就是说哈,假如这个两个事物同时读了相同的数据,但是同时又改了不一样的部分。他其实他们两个都能提交成功啊,根据这个SI的这个定义。它其实是有可能导致异常的,如果这如果他们读的这个数据都做一些啊计算的话,有可能导致异常,就比如这个里面有两行数据。这两个事物都读了这两行数据了,但是第一个事物他改了第一行,第二个事物改了第二行。那么根据这个SI的定义的话,这两个事物都能提交成功,但是很有可能会导致有问题,大家直觉上一想就就有可能有问题。毕竟。我改的东西他读到了,他改的东西我读到了。啊,所以说这个S不是可化的。然后第三种隔离级别就是ssi,就是able sle的isolation,大家从这个名字上能看到,它比这个SI应该是多了东西了,那它多了什么东西呢?它上面这个是跟SI是一样的哈,这这一行就是是开始一个快照。
31:08
啊,对,它通常最新的,但是对只读事务来讲的话,他可能会拿一个不那么新的快照,它是为了满足这个。呃,为了满足这可创业化的这个定义。但他不会太老。然后他改数据的话,跟他一样,如果发现被其他改了,他也不到了,他多的东西就多到这了。在读数据的它的,呃,如果这个数据库工作的SS的I这个隔离级别的话,它前它是上面都跟SR一样,但它多了一个,我在读数据的时候。我会把我读的哪一些数据我记下来。我到底读了哪些,我记下来。然后我会进行检测。如果我检测到不可进化了。就about。就比如像刚才那个例子,一共两行数据。然后第一个事物,他们两个事物都读了这两行,但是改的不一样了哈,其实他们是能检测到的,他们能检测到,哎,我读的东西已经被其他人改掉了,他其实是能检测到这个东西了,他检他如果能检测到,就有可能导致不可中性化了,它就会报掉。
32:12
他的这个这个算法,它有可能会有误判,就可能会有误差,但是肯定会有遗漏。啊,这个SSR肯定是没有写程序的问题了,它就是把这个写程序的问题解了,所以说它才是个可程新化的隔离级别。啊,上面我们说的大部分都是与那个读写有关系的,关于斜斜冲突的处理。基于多版本实现的话,会有两种实现的方式。第一种就是谁先提交,谁就胜出了。就是说谁最后能把这个数据写到我这个数据库里面来,谁就赢了,谁就胜出了。这是一种实现的方式。另外一种实现的方式就是谁先往数据库里面写,但是他不一定提交谁先往里面写。他就会阻塞后面那个写的人,就是第一个更有可能赢,就是谁先写谁赢,这两种其实是有细微的差别,他有细微的差别,这个是最后谁谁先提交,谁谁赢,这个是谁先写,他会阻塞后面的写,谁先写谁胜出。
33:15
啊,这是基于多版本的实现并发的控制,然后我们下面就具体分析一下两个数据库吧,就是分析一下myc和C他们的隔离级别是怎么做的。啊,马克的隔离级啊,在分析马克隔离级别之前,我们先看一下啊,马克里面有有个当前读和这个快照读的概念。当前读就是我读这个数据的最新版本,我读的时候是需要加速的,这个也叫lock瑞,在那个马的文档里面有。我读这个数据,我读的一定是最新的那个版本,我读的时候要加锁,读写的时候都要加锁。另外一个就是快照度,就像我们刚才讲的。用这个快照选一个合适的版本,这种读其实是不加锁的,我们先了解这两个东西哈,因为下面需要用的。
34:02
Myc,确实这C标准支持的,呃,C标准定义的四种隔离级别它都支持。它是怎么实现的呢?他瑞安committee的直接都不加锁,它有可能脏毒嘛,这个这个咱们根本就不用。然后看一下这个committed。啊,Committed。这个它是分开实现的,这个对select来讲,这个select是不包括这个for select for object和select这种模式哈,这种模式其实是要加的是当前。这个select的话,它使用快照组,每个C考获取一个快照。啊,这个应该不用再多解释了,刚才已经解释够多了。关于这个写的话就是in update这种的话。它其实它用的是当前读,就是我要读最新的版本,我要加锁,独家读锁加下锁,这时候它是不不用盖P锁的,这个盖P锁就是我们前面讲的那个predict lock,跟那个位锁其实是类似的,我们S里面还有个叫什么next k锁吧。
35:03
他们都是都是同一种类型的,同一种类型的锁,就是我这个锁。我用完了直接就放了,他这个什么时候会发生呢?就是说假如说我我把一行读出之后,发现这一行不满足外表条件,我就把这行给。把这个把这个毒素就给放掉。然后写索是是提交的时候再放吧,啊大家要是大家应该还记得跟刚才那个那个那个表格里面其实是能对应上的。我们再看一下它的这个S,它的S的话其实就是纯两阶段锁的实验,就跟前面那个表格一样,然后就是都使用当前读,都读最新的版本,然后独家读锁,写加写锁,然后会有这个未知锁,会有盖锁,然后这个锁都是到最后的时候才放。就实现了这个。就实现了这个隔离级别了。啊,我们最后再来看,哎,你大家可能话会发现我这个我介绍的时候正好到了这个地方,会该他那个隔离跟那个C标准定义的这个这门会反着,我会最客来介绍它,其实就是感觉到这么介绍的话,可以可能大家会更容易理解一些,因为这两个对比很明显嘛,然后再看第看这个他这个吕皮瑞他是在这LA上面,他放松了什么呢。
36:15
它其实就是放松了一个select的限制。他就把这个select用成了这个read的里面这个快照读了。MY里面的这个。Repeat和拉就差这么一个东西,就差一个select,限制其他的。就跟这个其实是一模一样的。诶,细心的同学可能就发现了他其实这样的话。他其实这样的话,他就把这个快照读这个当前读在这个瑞的,在这read的这个格雷基B底下,它其实就混用了。他混用了之后,如果你要仔细分析的话,它可能就会出现一些比较奇怪的现象。啊。比如下面有人提了一个bug哈,这个官方马赛官方回来说这不是bug,这就是非。
37:03
其实你只要理解了这个这一页PPT里面它这个实现实现的原理的话,就很容易分析它这个里面这个这个行为是怎么样的,虽然看起来奇怪,但是是可以解释的,就就是因为它是这么实现导致的。OK,我们现在来看一下。隔离级别是怎么实现的?C就是基于多版本实现的,其实这个MYCL看起来它更像是基于两间的锁跟那个。跟那个多版本的一种结合,那么post的话,它就是基本上是就是纯那个MC纯多版本,它就是写写冲突的时候,它可能会有类似于行色的东西。啊,Post gra隔离级别它支持,它只支持三种,就是read committed,你如果把这让这个post gra c考,你把这个read I commit,你给他设置一个read and committed的级别的话,它其实使用的就是IC,就是committed,你在里面它是永远不可能的。
38:04
第二个就是SI,就前面讲过的讲过的SI,你如果给设这个able的话,它实际使用的其实是SI。第三个就是SI,就是。呃,Post里面它是没有当前读的,它全部都是快照读。OK,我们看一下它这三个隔离是怎么实现的。RC就是committed,它每条C和鱼它都会用一个最新的快照。这跟前面讲的那个多版本那个其实是一致的,它的update是怎么做的呢?他肯定是先把底下那一行扫出来,就是我要改哪一行,他把那行给扫出来,扫出来之后经过一些计算,OK,我最后要把它改成什么样的值。他计算完了之后,他再去改。哎,当他再去改的时候,如果发现。那一行已经被别人在中间改掉了,在我第一次读完了之后,算算的过程中被人被别人改掉了,然后我再去改的时候发现已经被改了。然后这个P会怎么做呢?他就会用别人已经改过了那个版本,重新再跑一遍刚才的C号语句重新,比如计算过滤条件,计算结果等等,然后他跑他重新跑完了这个这个查询以后。
39:12
他再去尝试更新最新的那一行,如果不满足过滤条件的话,可能就直接放弃更新了啊。啊,这个过程在里面就被称为这个EPQ。I,我们现在看一下这个不是这个snaphotol。这个快照隔离,它就是这个整个事物使用图,一个快照更新的时候,如果发现这个数据已经被别人改了,就直接about,你在那个代,在post代码里面就明显能看到,他如果发现被改的时候,他经常会有个FL分支,他判他当前的快照,他就判他当前隔离级别是否是大于。呃,SI如果大于的话,它就直接about掉了,如果小于的话就是RC嘛,它就会跑EPQ那个代码里面很明显。啊,最后我们看一下SISI就是在S的基SI的基础上使用的SI瑞的缩,其实就是就是就是一个记录的东西,SI就是S雷什么就是瑞的缩,就是我记下来我到底读了哪些东西,它的题的内部也把它叫predict。
40:09
在那个代码里面的readb里面有这么一个东西,在代码里面也是这么叫。他记,他把我自己读的那些东西他记下来了,他如他会检测,他如果检测到我,可能这个系统会导致不可创新化了,他就直接把这个事物给报销掉了。这个实际这个加速的范围哈,它是与这个执行计划是有关系的。如果要是这个真心计划选的不好,选了一个sequ sc就是顺序扫描,扫全表就全面扫描的话,他没办法,他只能我我我只要是所有读过去的,我就得记下来,我因为我哪知道我读的到底是不是这么有用啊,没用了,他只要是读了他就得记下来,这个时候他加锁的范围就比较大,他就加了这他就是把这个整个表。给给。整个表给加上了,等于记下来了,你这个时候你如果别人在对对这个表进行任何的修改,他都感觉到,哦,我有人动了我这个地盘了,他就都能感觉到。
41:03
就他这个加的范围太大了。然后你用干的话,可能就只需要加一部分的行就可以了。MY里面其实也是类似的,它它中间加锁的时候,它也是对它它扫过去那个范围其实是有关系的。扫的越少,加的越少,加的数越少。啊,就是这个SI热的锁啊,与PG里边其他的读锁解锁其实是互不相冲,互不冲突的,它这个你你把它叫做锁,它其实并不是一般意义上的锁,它更像是一种标记,它记下来了,这个读写学所它是真正的锁,它与它它是另外的一套东西,所以说他们根本就就不会冲突。啊,Post里面关于这个写写冲突,它是用的这个谁先写谁胜出,就是post里面谁先写了,它其实类似于有个行锁的标记在那儿,但是它不是内存里面的行锁哈。他是直接记在那个记录上面的。他记了一个他的记录上标做了一个标记,等于加了一个锁,这个时候别人是改不了的,所以说就是谁先写谁胜出。
42:02
啊,我们现在看一下这个po,这个PG里面这个SSPG,就是po c就PG里面这个ssi,它这个检测的原理是怎么检测的,它其实SS检测就是为了消除这个sssi,这个检测就是为了消除这个SI里面这个写偏序这个异常。它主要就是检测这个系统里面是不是有下面这种危险的结构,它把下面这个这种结构叫一个危险的结构,它这个结构是长什么样的,就是T1,就是第一个事物吧。第一个是我读了一个,读了一个数据,然后T2把它给改了,把它读的东西给改了。这就形成了一个读写的这么一个依赖,我就从这个地方换了一个箭头,从同样这个箭头也这样,然后T2又读了一个东西,然后T3把这个东西给剪了,OKT2又这么依赖于T3。所有有可能导致这个不可串行化的这么一个调度里面啊。这个闭环的事物,这调度里面肯定会存在这么一个结构。
43:01
然后PT检测的时候,就是就是检测是不是有这种结构啊,大家应该在比如那个死锁检测呀,还有还有什么检测那个。进行串可创业化调度,大家可能会都会换成一个图,换成一个到底就检测那个图里面是不是有环,那个检测图是不是有环,其实那个代价还是蛮大的。那么它只是检测这种结构的,它代价就小了呀,而这个SS它那个初衷也是说要用尽可能小的代价来把这个写片区的问题给检测出来嘛,其实它就是检测这种。他这个S检测有没有这个结构。啊,这个是我截的论文里面的一张图,他就是说。这个里面这个意思就就是跟我刚我刚才说的意思一样,就是有这种结构,他他说进一步的,进一步的话,其实T3必须是第一个提交啊,我们就不关心这个东西了。啊,大家要是对这个SI感兴趣的话,我在参考文献里面列了两篇论文,我等会会说一下。OK,我们做对这个第二部分做一个小结哈,这个讲的好像比较时间比较长了。
44:05
我们讲了一下这个C标准定义的这四种隔离级别,然后介绍基于所实现的隔离级别。啊,基于多版本实现的隔离级别,Myc隔离级别的实现,PG隔离级别的实现,这个小节就讲了这么多啊,我。我看看直播间有人问题哈,我看这个问题是啥。是什么意思啊,你是说我刚才。你是说这里面,你是说这个地方。刚才我只是讲了这四个,你你是问这一个。这个其实翻译过来,它叫油标稳定。这个油标稳定的话。它是某种数据库里面的一种隔离级别。它是个什么意思呢?就是说你用定义一个油标declare一个什么什么的一个东西。
45:02
然后你一行。你一行之后,他就会把那一行给你上一个锁。他上传那个锁之后。你这个只要是不移动,别人是改不了的,然后你就可以进行一些操作。他就唯一的做,他唯一的实现,它就是就是就是这么回事。你是这个邮编一移动,比如move forward了,OK,他把刚才那个锁就放掉了,刚才那行上的锁就放掉,然后给下一个那个再加上。再加上个锁。啊,MYCL会用当前。和快照读导致什么奇怪的问题哈。这个我就我看一下,我举一个简单的例子吧哈。首先这个机构的问题是在这个repeat底下实现的,呃,才有的。
46:04
假如你这个表里面我看哈,只有。假如说只有两行数据吧。啊,只有一行数据吧,只有一行数据,这一行数据里面全是,就是有两列是一一。然后你发一个select这个表,然后你其实读出来的是一。对吧,因为这来是快照读嘛,读出来是一一。然后这个时候你再开一个其他的,然后你把这个一改掉,比如你把。啊,不对不对不对,我重新来啊,错了。这个表,这个表里面有两行数据是一一。是一,一你select的星,我们t where a等于二。你其实是读不到这个一一的,因为你不A等于二嘛。OK,下面你开,你开另外一个窗口,你去把它给更新一下,Update这个表,你把它改成二二。
47:00
改成二之后你回来。你再了形from t等于二,你其实还是读不到的,因为是快照读。这个时候看起来是满足这个repeatable这个隔离级别了,对不对。OK,你下面你发一个update。你发一个update。然后后面VA等于二。你就能看到他更新成功了一行。哎,这就有意思了,你最开始你发了两个select,然后Y等于二都读不到。然后又是用的快照读,然后你update,诶按理来讲的话,你刚才读不到A等于二了,你update一个YA等于二了,按理来讲也是不可能的嘛,但是因为这个时候他update用了快当前读了,他读到最新版本了,所以说。它就更新成了公的一行,然后你再发一个s select符TY等于二,诶,你就会发现你把你那个。
48:01
你刚才更新的那行,你已经读到了。他这是怎么实现的,就是说我这个本事。是一定能读到本事的修改了,所以这个时候看起来它就不是这个repeatable read了。就是如果你的应用你这个满信号,你在这个。可重复图,在这个RR的隔离级别底下,你是不能像我这我刚才举的这个例子这么用的,你要是这么用的话,它会有问题。啊,这个bug上面底下有介绍的有哈。啊P运行一遍E之后,发现数据又被更新了,重复跑EP。啊,会会的会的。他如果他发现,如果要是。他更新你你说的应该是这个地方。他如果发现他更新了之后。呃,他去更新的时候被别的更新了,重新跑,跑了之后回来更新又被又被更新了,他就继续跑,所以说这会去,如果在大地方更新同一行的时候,这个地方有可能会,呃,会不停的试哈。
49:00
我看看还有什么问题。问一下Oracle的锁锁,Oracle。Select的话也是快照读嘛,它它的快照读,而PG需要加锁,PG的读是不需要加锁的呀,PG是不需要加锁的,PG这个写写冲突的话,谁先写了,他会在那条记录上标一个标记,他其实就是打那个标记,它类似于所的东西,但是它处理写写冲突嘛,他没办法了。PG都是不需要加的,PG是用那个MVC实现的。OK,我们由于时间关系,我先回答这么几个问题吧。啊,第二部分我们先讲到这儿,我们先先来看一下这个第三第三个部分,第三个部分是那个PDC考事务。一致性的实现。
50:01
啊,下面我们先讲一下这个TD的架构。啊TTC这个目标就是让这个业务啊,更更容易使用数据库,就像是使用一个单机数据库一样,来使用这个分布式的数据库。这个功能的特性。就是满C完全兼容,然后全局一致性,就是我这个扩容缩容是业务是没有感知的,呃,完全就是在线的,完全原生的,这个在线的表结构的变更,就是table。然后这个存储引擎是一个分布式的KV系统,它提供了这个事物和这个自动扩缩容的能力啊。呃,在这看一下这个TTC,它是一个存储计算分离的架构,它是有三个组件。它是计算层的组件,叫CN,这个是计算层,然后分布式存储层。呃。这叫TD star,这是这是这是存储,然后还有一个原数据管理层,原数据管理层叫TD class叫TDMC,简称就DMC。
51:01
然后这个计算层上是完全没有状态的,它里面没有任何的数据,所有的数据都是存在这个存储里面去的。你这个这个业务是通过C接进来的,这个业务通过满C协议接到C,然后这个C在上面计算,计算的时候如果需要数据,他就去底下这个存储上面去取。然后底下这这这不是个KV的系统吗?KV的分布式存储,然后你就你既然分布式,你你得给它切了嘛,你得给它切成一块一块了。呃,这个T切的时候是用这个按瑞来划分的,就是一个就代表了一个。一个K的一个范围,我这个图里面这是这是有四个region,这REGION123 region4,这是四个region,它每一个region其实是有三个副本。你看这个瑞一这一,这瑞一这一吧,它有三个副本。这个副本之间啊,它是用这个rap的协议来保证他们这个一致性的。
52:01
OK,这个切的时候就是根据范围来切了嘛,这举个例子,比如零零。啊到0X30,这属于6.1,这这后面这这我就时间关系这就不说了,这个比较简单。啊切的话。OK。这三个副本之间使用。然后这个红色的表示的是leader,这个是leader,这个其他颜色这个是。我下面就简单介绍一下这个rap的协议吧。呃,TT就是用这个rap的协议来保证,保证它的高可用,以及这个多副本之间的一致性,就一个region,它有三个副本嘛,这三个副本之间怎么保证一性,就是用rap的协议来保证的啊,具体的详细的rap的实现大家可以去读一下rap的论文,介绍的还是蛮清楚的,我在这儿只是简单介绍一下。他那个也是根据那个论文来,他论文里面就介绍了这么几个,就介介绍了这么几项,介绍了三项加一个配置变更这个三项,上面这三项就是选主。就这个rap的协议,它跟他们这个不一样,Rap的协议它是强,它是一个强的协议,它必须要有一个leader。
53:06
系统才能对外提供服务啊,它里面会会有一关于有一个部分来有一个章节来介绍我怎么选,才能保证选出来的这个leader是唯一的,第一个就是选,你既然选出主来之后。然后就开始这个日志复制了rap的协议,里面的日志是只会从leader到follower单向复制的。这个日志没有其他的流动方向。这个日志只会从从上复制到follow上面去。然后第三个它那个论文里面叫safety叫安全,安全它其实就是保证。第一步里面这个选址的时候,你选出来的leader包含最多的日志,你就避免说他成为leader之后,他发现它日志不全,还要到其他节点上去拉日志,就是这种现象在这个rap协议里面是不会存在的,如果他的日志不够多,他就不可能成为leader。他成为一的之后。
54:00
他的日志就最多,他就不需要其他节点拉日志。呃,主要关键的就这么就这么三点,然后他最后他介绍一下这个配置变更,这个配置变更的意思就是说就是其实就是增加节点,增减节点,增加了节点了,还有减的节点了,就是这啊你这个。集群里面到底有多少个节点,它?把它叫做一个配置。叫做一个配置,你这个变了之后,你你这个节点变了,就它就认为配置变了,这叫节点变更,节点变更的是怎么是怎么变的呢?它是说为了就是说为了避免产生双主哈,它你这个系统里面没有主不要紧。就是没有主播要紧,但是产生双主就麻烦了,产生双主这个数据就不一致了,你这一个系统里面只能有一个,只能有一个节点说了算,不能有两个都能说了算了,就一定不能产生双主。呃,你就是为了避免产生双组的话,增减节点的时候,它是用了两个阶段来完成的,论文里面介绍的很清楚啊,如果使用一个阶段的话,它是它就是会有那么一种会,就会有可能产生双组,就为了避免产生双组,就用了两个阶段,两个阶段就是第一个阶段的话,就是旧配置是生效的。
55:06
然后从这个第一个阶段过渡到第二个阶段,这个时候是新配置和旧配置同时生效。然后到了再到第三个阶段是新配置升降。啊,我就在这简单介绍一下rap协议哈。呃,感兴趣的同学可以直接就去翻那个论文。下面我来介绍一下这个TTC的变化控制。啊,TD的变化控制其实就是基于这个时间的,基于时间的版多版本的并发控制。啊,他这有一个。全提供这个全局时间戳的这么一个服务嘛,就是那个TDMC。保证这个时间戳是全局单调递增的,其实就是一个逻辑的时间戳,你就可以认为就是一个计数器,就是加一加一,加一加一就一直往后加一。每个事物开始的时候呢,他会拿一个这个starts,这就是这个start ts,就是我前面介绍的那个快照。就是一个整出一个快照,你读数据的话,你这个数据项上面它是关联那个时间抽了嘛,你读数据的话,你就读那个。
56:05
数据上面第一个就是小于达TS里面最大时间出的那个。这个选的时候就那么就那么选,就是说我我读出去的时候,我一定不能读到比的TS更大了,因为。这这是3TS,是我师傅开始的时候拿了一个时间戳,所有那个数据,只要是那个关联的时间戳比三的TS大的话,就代表那个数据是后面的事物提交的,我不应该可见,所以它不应该可见,他应该就选比它小的,但是比它选比它小的的话,那也一定要选尽量最大的,是最新的嘛,所以说就选第一个小于等于4TS的那个K。啊,这个T它用的这个是right on right on,这什么意思呢,就是说我这个事物最开始这个对这个。对这个数据的修改啊,我是先缓存到我这个事务空间的本地的,我是不会往上面,我在事物运行的过程中,我只要不提交,我是不会往存储上面写。
57:04
这个是这么个意思,然后事物的可比的TS就写到数据项上了,这就是关联的那个数据项上面的时间戳。我这举了一个例子。啊,这个例子就是update,就是SA等于A加五,就是我取出来一哈,取出来这里面的A给他A加五。我们看他怎么做了啊,首先这个是这个15空间里面,他首先数开始,开始之后,我现先拿一个时间戳呀,然后这个时候去TDMC拿tdmc OK把四给了他了,OK,然后他就去这边去这边选选我到底应该读到哪一行,然后他就发了一个get a的IPC过来。后来RPV过来之后,这个时候其实这里面只有三,只有三只有两行,就是只有两个,就是A和B嘛,但是有三个版本,A是有两个版本的。A,有一个版本是这个时,这个时间说是一,有一个版本这个时间说是三。那么我现在我要用这个TS等于四这个实验出去取的话。
58:01
那很明显我要把这个三的读出来嘛,因为这个一的是比较旧的,我要读这个最新的。所以说他把这个把这时间数是三的这一行这个版本给读出来,这个十他就读到了,读了之后他就算A就十加五等于15嘛,然后就他就把这个a put a等于A等于15放到它这个15哦,他自己的这个就本地你可以理解它一个临时的地方,它自己放到一个地方去了,但是这不他自己的地方,自有的地方,但是它不是这个公共的这个存储,它先放在这儿。放在之后,这后面就没了,后面就开始就这个提交了嘛,就开始commit了,这commit的话,他就再去拿一个时间戳,他拿这个时间说是干啥用的呢,就拿这个时间戳,就就给他新产生的这个版本挂一个时间戳。关联的说就是关联,他现在拿到的这个OK,他拿到这个五,他就把这个A等于15。关联上这个时间戳五,然后存到这里面来了。
59:00
这个是一个简单的例子。现在我们看一下。他这个并发控制。这个并发控制是啥呢?就是两个事物同时做。A等于A加五,其实就冲突了嘛。啊,我们先看一下这个十五二,它大get a等于十,这个我就不重复了哈,他把十读到了,然后他put a等于15,这个put a等于15肯定是放在本地了。放到他自己本地。这个时候这个是我。左边这个事物。也读到了第一个版本。不叫第一个版本就是V3的这个实验,V3这个版本读到十。然后他自己也是算,A等于A加55放到他本地,OK,下面开始提交。下面提交的话。右边这个事物就得去检查一下。他其实是检检查一下。我这个存储上面,别人有没有先于我。
60:05
往里面插入我。他就再读一下这个A最新的版本,上面关联的时间串,它读出来A是三。这就代表啥呀,小于等于我这个四,他一看,诶小于我这个四,他说诶我读完了之后,别人没有修改。诶,我就可以把它放进去了。然后这个它这右边这个事物,把这个A等于15,它放进去了。然后他就提交成功了。左边这个。他先把这个库的A等于15,他这一步都算成功了,因为他本地算它放在这儿了,他也要提交之前他也要检测,他再来检测的时候,他就再去这个存储上面读。读这个A最新的版本关联的时间戳,哎,它读出来是六。啊,这个六就是这个,呃,这个六就是那个,就是这个15刚放进去的嘛,这个15刚放进去的是六,然后他从这边读到六,我刚才大TS是五,现在他是六说明啥。
61:04
说明我读完了算的过程中,别人把这个数据改了。那么我就不能再提交了。这个所以说这个提交commit是这个提交就失败了。作者总结一下,就是这个TTC,把它这个变化控制,就是用了一个乐观的事物模型,就提交之前做这个充值检测。他这个冲突检测的时候,他并没有说挨个的去去找他就是。只是比较简单的,这这个简单还是为了节省力气嘛,为了检测快啊。就是把我之前读到的所有的K存的时间图拿出来,就跟我这个RTS1比就搞定了,如果如果都小,都小于我这个RTS就就允许提交,否则的话就会关。哎,可能有的同学就会问,哎,这个检查的时候会不会出现那么一个啊临界区,就是我两个都检查成功,哎,这个是不不可以的,这个检查这是串行执行的,这他们两个只要是液内检查的话,是不会有有那种那种那种离液那种很小微小的实验冲出,他们都检测成功之,不会出现这种问题。
62:08
啊,通过刚才的介绍啊,大家应该就比较清楚的看到,其实他这种工作方式。这个隔离级别就是快照隔离,我最开始拿了一个快照,我自始至终都是用这一个快照。然后。提交的时候来做检查嘛,这就这个是快照隔离,就是snap的OL,它是这个隔离级别,它处理冲突。很明显了嘛。就是谁先写。啊,不是谁先提交谁就胜出了。你谁先写不算数,你写的这个,因为你写到本地,你这个写,他这个写没有写到这个存储上,别人看不见,所以说他自己写到本地,他不会阻止别人,谁最后提交到这个系统里面,谁才胜出的。这个是跟那个PG不一样的地方,这个他这个PP这是用了谁谁先提交谁谁胜出的这么一个策略。
63:02
OK,刚才讲了是那个一致性,现在我们就看看这个这个提交,因为你在分这个分布式事务提交的时候,它必须也要保证就是。就是它这个分布式事物就是这个。多个节点都参与嘛,大家都做了一点事,大家做的所有的事情综合起来才是一个整个的事务,最后大家说提交的时候,你要么都提交,要么就都不提交,否则的原子性都满足不了。他这是。就是TT号也是使用两阶段提交来。呃,来完成的,其实这个两件的提交哈,它就是一个共识算法的一个特例,就是一个特例就是p two PC to commit,就两件的提交to p two PC。它是一个特例,这个to PC哈,它对这个环境是其实是有一些要求的,有它有一些假设。它就有这么几个假设,第一个假设就是这个每个节点都是必必须有一个带日志的持续化的存储,它没有节点会发生这个永久性的故障,这个假设就太强了。
64:02
如果你一个参与者。比如宕机了。或者是怎么样坏了,永久的离开这个系统了,那就永久故障了,那么这个to PC的根本都work都不work了,都不能工作了。所以说这个假设是很强的一个假设。啊,第三个假设就是预显示日志永远不丢这个log不丢这个都都无所谓了,就任何节点之间都可以通信。其实大家就能看出这个BC啊,它就是一个。就是从学术来讲的话,它就叫一个阻塞性的协议。就是如果你这个协调者如果发生了永久性的故障。那么这个分布式的事物就没法推进了,这个协调者就是这个two PC里面它会分协调这个参与者嘛,协调者就是一个类似于类似于一个leader,类似于这个事物的leader一样,他会指导着大家一起OK prepare就是大家都准备好了没有,他会挨个去问,接着者挨个去去问准备好了没有,准备好了没有,他需要问一圈。然后大家如果都回答说OK准备好了,他收的都准备好了之后,他就开始发,一个发就告诉大家OK提交,他就挨着告诉大家提交。
65:09
如果他这个协调者,就像这个组织,他自己要是永久性故障了,那么这个事物就卡在那儿,他就推进不了了。这个事物它还有可能持有其他的资源呢,比如现一些锁呀,一些东西,它这些东西都是不能释放的呀,就就卡在那了。所以说如果你要这个东西,要是携带者要是故障的话,有一种有的系统的解决方式,就是那种。他会定期的去检查,定期的去检查我这个,我这个整个系统里面会不会有那种因为协调者故障而残留的那些事物,他会检,他会去检查,如果有的话,他会替那个已经死掉的那个协调者去给他推进的。但TTC里面它是没有这么做的。PTC里面这个计算层。就C是完全没有状态了。他把这个TTC,把这个协调者是下沉到这个存储TD上面。
66:01
只要是这个C高验点,这个计算层C高点发出这个可密的命令之后。好,C关,这就不用管了,所有的提交都是由T自己完成的。道会让第一个参与者自动成为协调者,比如你这个事有十有十个有十个。协调者参与,他们自动让第一个协调者,呃,第一者参与者成为协调者,就第一个人,你就管他事。这个协调者还一定是某个rap手上的leader,这个是显而易见的,就是因为只有这个rap rap leader才对外提供这个读写的服务吧,这个协调者一定是这个一个一其中的一个leader。如果这个协调者故障了。就等于这个这个主死了吧。然后rap的协议,它会自动的选出来一个新的里诶。这个新的例子,它就会通过回放这个日志,它就构造出旧例子上那个事故的上下文了,就说你那个旧的那个协调者去已经故障了,就是没了,无所谓,我又出来一个新的,你那个旧的上面的事物他都能搬过来,就发现诶,我又又出来了,然后这个新的leader子,他就可以继续推进旧协调者上没有完成的事务。
67:13
这个推进是有可能是有可能是about了,这个就是根据。这个就得看,就得看看你那个协调者到底是在哪一个阶段。呃,故障的,它总总之那个新的leader的是能把这个事物能推下去的,能继续推进,就不会残留在那儿,卡在那儿。通过这种工作方式哈,其实就能看出来,这个T就对外界来来看的话,它其实满足了这个two PC的这个第二个假设,刚才我们不说这个假设太强了嘛,没有节点发生用矩行故障吧,诶它这么移动的话,这就等于看起来就是像是满足的这个第二个假设一样。啊,我们最后再来看一下这个一致性图,这个一致性图其实还是解决那个读偏序的问题。
68:01
就是你大部分的这个使用两阶段提交这个系统啊,都是先让这个所有的节点完成prepare。所有的节点都准备好之后,在提交前的那一刻。这个协调者才会去获取一个时间串。就是这个提交的这个时间串。TD也是这么做的。就是为了保证这个一致性图,就是不发生读片续航。你如果一个事物来读这个一个数据的话。如果你发现这个事物,产生这个数据的那个事物,它成功了。但是他还没有commit。你说这个事物,他现在应该怎么办呢?他其实他就只能等着,因为他不知道这个事最后是提交还是失败。他就不能,他万一要是说对他可见了,他万一要是认为这条。他万一要是认为这个元素可见,那最终他要说那个是不是要about了呢。
69:00
它就它不就等于脏毒了吗?如果他要是不直接不可见过去了,那有可能就少读东西了吗?所以说他只能等着,等着这个这个事物结束之后,才能确定对本事物是不是可见,诶这个TDC在这个地方是有是有一个优化的。他就为了减少这种等待哈,这个TTC考在这个prepare的时候,他就先给这个事物分配了一个时间戳。这个时间戳就跟那个,可那个其实是从一个序列里面产生出来的。就等于我先借给你一个,就类似于这种意思啊,你先用这个临时的。Prepare ts,你先用这个临时的,等到提交的时候,等到等到提交的时候,我再给你分配一个正式的,他其实就是这么个意思。因为他们都是从同一个序列里面产生出来的这个可密TS,它就一定会大于等于这个。TS。你有了这个设计之后,其他的事物。如果遇到刚才刚才说的那种。刚刚完成prepare。
70:01
但是没有提交的事物的话,你是能看到那个原始那条记录上面那个prepare ts的。如果我这个事物读那条数据的那个S。比这个TS还要小的话。它就一定会小于这个,呃,就一定会小于这个事物最终的那个可逆的,所以说那个那个数据它是一定对我是不可念的。OK,在这种情况下。我那个读的那个事物,他就不需要等待这个prepare的事物最终结束了,他直接就不看这条记录就行了。就可以避免这个不必要的一些等待。OK,到这个第三部分就讲完了,就做一个小结,这一部分我们是。讲了这个TDC考的架构,讲了这个多副本的一致性是用rap来保证的,讲了这个并发控制协议,就分布式的控制协议,在To Bc,在这个上面有个优,有个优GDC,在To Bc这个地方有个优化,就是把那个协调者下沉到。
71:01
到那个存储上面去了,这个一致性读这个地方有个prepare ts的优化。我看看。呃,第三个部分就讲完了,我看看他现在有什么问题哈。在线表结构变更是说。D不影响业务吗?啊对这个这个就是。Region的数量会变化吗?这会。这这个数量,这是如果你这个随着数据数据增多的话,它会自动分裂的。啊,我这再多说一句吧,啊,如果这个时候它如果分裂的话,它肯定会产生一个新的主吧,对吧,你就从一个从一个变成两个的话,它每个都是自己的列。都是一个rock的组,它就有会有两个组嘛,啊,它有两个组的话,它可能第一个上面刚才可能会有事物。它那个事物的话,它其实是不会断的,它会通过这日志回放过去,这个事物是不是不会是不会被杀掉的,是能够能够。
72:03
能够那个迁移到那个后面那个事物上面去了。呃,能迁移到后面那个新的分裂出来那个上面去的,你这个业务是感知不到的。请问什么场景会出现start小于prepared?应该说的是这个地方吧。Start小于啊,这个场景就还是比较简单构造的。你就想象一下。一个事物,他。他已经开始了。比如他拿了一个是十。然后他就去扫描这个数据库,在他扫的过程中,他其实扫他其实还没扫完呢。这个时候可能有并其他并发的事物来插入数据,来插入数据并且提交成功了,那个后面那个啊啊啊,对他插入数据,插入数据之后他要prepare。他prepare的时候。那个时候他那个时候就已经标了一个prepare ts,他那个prepare ts是一定比我这个。
73:05
我刚才这个事物这个自然TS要小的嘛。上的leader在各个节点上分布不均衡,会重新分布吗?啊,对,这个会重新分布的。哎,在哪呢?在这会重员工分部业务啊,业务是不受影响的。哦,现在已经八点啊,我们已经超时了将近20分钟了啊,由于时间的关系,要不这个问题我今天回先回答这么多啊,我看这个上面后台还还问过这个不少的问题啊,我们会在这个公众号,大家可以关注我们那个腾讯云数据库的公众号,我可以我医疗提问了,我们保证会有工作人员给您回复的,这个您放心。哦,我介最后介绍一下这个参考文献。
74:01
啊,这个参考文献就是参考了一下这个数据库系统概论,王老师这本书参考了这个。张树杰写了一本事物的书。就主要的那个那个那个隔离级别的那些介绍,都是来自这篇论文,Of什么isolation level,这是一个应该是30年前的一篇论文了吧。这篇论文,呃,这两篇第四和第五是关于那个SI的,就是C关于ssi的。BG里面的。呃,第六个是。里面关于这个,这就是EQEQ的缩写就是一,就是这么一个东西,就是这里参考,大家也可以再看一下这个MY里与加锁相关的一些代码,MYL与那个隔离级别锁的一些文章那个的。呃,我我刚才我提到说那个two PC是那个共识算法的一个特例,在这篇论文里面其实是有讲的啊,这四个假设我是从这个VP上面找的,大家可以参考一下。
75:02
啊,那么我今天的分享就到此结束了哈呃,谢谢各位的参与,欢迎大家关注这个腾讯云数据库公众号,可以解锁更多精彩资讯,我在这里提前祝大家这个国庆节快乐哈,接下来的时间就交给小数,开始我们最后的抽奖环节。
我来说两句