SQL Server In Memory OLTP - MVCC 事务管理

讲讲SQL Server Memory Optimized Table的MVCC.

从单个只访问memory optimized table的语句来讲,snapshot隔离机制本质上是MVCC,因此read uncommitted 和 read committed提供的隔离功能,它都实现了。或者说snapshot的读并不会被其他session的写给阻塞。所以可以理解为它的起点比较高,memory optimized table的访问不支持锁,想要在访问memroy optimized table时候,保证事务前后一致性,并不是不可以。比如一个只访问memory optimized table的事务,开始时候获得一个timestamp,那么在事务中,只要持续访问这个timestamp的snapshot版本即可,或者是由本事务生成的row.那这个事务就是repeatable read或者serializable事务了。

对于既访问memory optimized table和disk-based table的语句,disk-based table是全支持5种隔离级别的,而memory optmized table只支持snapshot, repeatable, serializable.所以他们是一个大事务下的两个小事务,各自维护一份时间序列。

为memory optimized table指定事务隔离级别的时候,有两种可能:

Native Compiled Modules: 在Atomic语句下,指定三者之一;
在 T-SQL 语句下,使用 table hint 或者memory_optimized_elevate_to_snapshot。

我们需要讨论在这两种模式下,repeatable read, serializable是怎么作用在memory optimized table上的?我猜测也就是读取事务开始的timestamp,取begin timestamp和end timestamp之间的数据行。任何begin timestamp大于这个事务开始时候获得的timestamp,都不会被读取;任何end timestamp小于这个事务开始时候获得的timestamp,也不会被读取。

serializable的锁模式,锁作用范围,锁作用时间。锁作用范围是数据页,索引页,也就是说在事务范围内,锁定的索引,表值都不允许变,其他事务只能等待,作用时间一直到事务结束。那么对于memory optimized table来说,始终使用MVCC来访问的,就是在事务开始时到事务结束,始终访问一定范围内的snapshot数据行。在访问memory optimized table的事务中,假设一开始访问了符合某一个条件的数据集,过了一段时间后,再访问符合这个条件的数据集,而这个过程中,有其他事务新增了符合这个条件的数据集,那么在本事务最后一次访问符合条件的数据集时,能不能访问到其他事务新增的数据集呢?

这取决于事务的定义:如果是单条语句,那么一条语句就是一个事务,事务是显式自动提交的,两个语句就是两个事务,中间如果有其他事务做了新增,最后一个事务肯定是能访问到新增数据的。因为最后一个语句的timestamp是比新增数据的begin timestamp要新鲜的多。如果是多条语句组成的事务,那么time stamp会在事务开始就指定,此事务就只能访问begin timestamp比事务开始时获取的timestamp小,并且end timestamp比当前获得的time stamp大的数据行。而在这个事务执行过程中,新增的数据行肯定是更大的begin timestamp, 因此不能被此事务读取。

因此repeatable read, serializable事务隔离,正好都满足snapshot的读取逻辑,因此他们是被memory optimized table支持的。

Memory optimized table的更新: 新增,更新,删除操作

新增操作:在memory optimized table上增加新的数据,除了新增row之外,在range index 的leaf page上,如果是新增的row索引值是新的,会在leaf page上新加一行,指向新row的物理地址,如果leaf page已经满额了,可能还会新增一个index leaf page;如果新增的row是原有的索引值,就会被添加到原先的索引数组链的末端;在hash index上,就简单一些,要么在原先的hash table bucket里存储一个新指针,指向新增row,要么在原先的hash table bucket链末端的row,增加一个指针指向新row。

删除:就是新增的反操作。唯一不同的是,在提交删除操作的最后阶段,会加上end timestamp, 标记删除。其他事务会读这个end timestamp,判断是否读取。memory optimized table的row都是通过index来串联存储的,所以删除一个row肯定是先从Index搜索,找到对应的索引,再删除链接,标上end timestamp的标记。

更新:是上面两个操作的合并,先执行新增操作,再执行删除。

在所有的操作中,提交数据,并不是只有commit这一步。在commit时候,获取了timestamp,然后做validation,做完validation,写log , 写完log, commit才算正真结束,然后做post process. Post process主要是将transaction id转换成end timestamp或者begin timestamp

repeatable read和serializable的读一致性,是怎么保证的?仅仅靠share lock,保证的是事务当中没有其他写操作可以作用到share lock的数据上,但是其他的只读事务,依旧可以读取这份数据,当事务顺序写入的时候,就会和事务开始读取的数据,不一致。这个时候,这两种事务怎么辨别事务的读一致性已经被破坏了?

在sql server in memory OLTP internals for sql server 2016中举了一个例子:

T1正在更新row {Jane,Helsinki},将之更新为{Jane, Perth}.
此时,有个并发的事务T3,读取的是{Jane,Helsinki},用读取的Helsinki去更新另一个人的city。
在T1提交更新之后,T3也提交,此时自动报错为violation repeatable read isolation.
注意,在T1事务上,采用的是serializable read, 而在T3事务上,用的read committed 隔离,而针对{Jane,
Helsinki}表T1,采用的是repeatable read. 这里有几个疑问:

1 T3是repeatable read, 针对访问memory optimized table采用repeatable read 会与访问disk-based table采用的repeatable read不同吗?因为disk-based table的repeatable read是用的share lock一直锁到事务结束,而针对memory optimized table采用repeatable read, 采用的方式,难道还用锁吗?前面提到memory optimized table的访问方式只可能是MVCC级别的访问。而MVCC级别的访问,从一开始就是可以满足事务重复读,一致性读的要求。所以这里虽然指定了repeatable read但是并没有用锁,导致T1的更新才不会阻塞而成功了。

2 那么T3指定了repeatable read事务,在提交的时候,怎么会被发现是与原先版本冲突了呢?这里也是和MVCC机制有关。因为MVCC是在更新的时候会自动检验,更新的副本,是不是已经被致上最新的end timestamp了。被致上最新的end timestamp不仅仅是被删除了,还有可能是被更新了,更新的步骤就包含了新建和删除。仅仅是读事务,一点问题没有,但如果是写事务,整个事务(所有涉及的表,不管只是被读,还是被写)都必须是事务前后版本一致。这是MVCC自动校验,而不需要人为去实现这么一个校验的操作。

3 假如将T3的事务设定成serializable,效果应该也一样。

当一个事务既访问了memory optimized table 也同时访问了disk-based table, 这个事务就被叫做cross-container transaction. 我们可以将这种事务看成是一个具有两个子事务的大事务。在每段事务的开始,就给这段事务指定一个timestamp.所以如果事务被设置为snapshot,就会有问题了,这个时候,每个子事务都有不同的timestamp,而最终不知道哪个timestamp会被作为更新的判断标准。

在MVCC模式下,repeatable read和serializable访问表,用的肯定不是锁,特别是share lock来锁定特定的表数据行。两者的实现在MVCC模式下与disk-based table访问使用的原理不一样。repeatable read防止的是不一致读,没有漏读,没有脏读,前后数据一致,而serializable防止的是Phantom,在事务开始之后,提交之前,有别的事务新增了数据,这部分数据是可以被读到的,但是在serializable隔离级别下,就设置为读不到,目的是为了保证数据的serializable.这个时候,用的是共享锁(share lock). 其他更新或者新增的事务会被阻塞。

而在MVCC隔离模式下,repeatable read, serializable的实现就不一样了。只读取事务开始时,begin timestamp比当前timestamp小,并且 end timestamp比当前timestamp大的数据。因此这两者事务通通支持,并且由MVCC在commit阶段,进行validate,决定是否可以提交成功。

欢迎关注个人微信公众号【有关SQL】

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dbLenis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值