3

事务模型:两个时间戳,或是一个时间戳

 3 years ago
source link: https://www.zenlife.tk/two-tso-or-one-tso.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

事务模型:两个时间戳,或是一个时间戳

2020-07-15

TiDB 和 CockroachDB 在事务模型方面,有一个很根本的不同的地方。TiDB 使用了两个时间戳,而 CockroachDB 只使用一个时间戳。

其实都是 MVCC + CAS,时间戳代表的是版本,事务修改前是一个版本,事务修改后是另一个版本,然后如果保证能够 CAS 成功,事务就提交,不能就回滚。 这样子已经保证了原子性了,也就是 ACID 里面的 A。再然后 CAS 的成功与否,其实取决于是否会破坏隔离级别,也就是 ACID 里面的 I。

隔离级别有 repeatable read, snapshot isolation, serializable 等等,各自有不同的要求。CockroachDB 说要实现 serializable 必须是一个时间戳,TiDB 里面默认是 snapshot isolation,如是有场景需要,由用户去 select for update。反正都至少是要求 repeatable read。

我们看一个例子:

  • 事务1 的时间戳 100,事务2 的时间戳 200
  • 事务2 请求先到达,用时间戳 200 读到了 key 的值 v1
  • 事务1 后到达,它修改了 key 的值,并用时间戳 100 提交,key 的值为 v2
  • 事务2 用时间戳 200 再读一次,这次读到的 key 的值是 v2

事务2 读了两次,读到的结果不一样,说明这样的操作违反可重复读了。问题是出现在第三步。事务1 先读了一个值,另一个事务2 再来写这个值,由于读事务 没有读到修改,只能让写事务发生在读事务后面。 也就是第三步里,必须让写事务1 晚于读事务2。

时间戳越大的,逻辑上就是越晚的事件,现在读事务2 的时间戳是 200,大于写事务1 的时间戳 100。这意味着如果写事务1 直接按时间戳 100 提交,读事务2 会晚于写事务1。

这不符合要求,怎么办?

使用两个时间戳是如何处理这个问题的。

引入了 startTS 和 commitTS,比较两个事务的先后顺序,是按一个事务的 startTS 跟另一个事务的 commitTS 去比较。

事务1 的 startTS 是 100,但是我们可以让它的 commitTS 是 201,这样它就大于事务2 读的时间戳 200 了,完整的逻辑上的顺序如下:

  • 事务1 startTS = 100
  • 事务2 请求先到达,以 startTS = 200,读到 key 的值 v1
  • 事务1 修改了 key 的值,并以时间戳 201 提交,key 的值为 v2
  • 事务2 用 startTS = 200 再读一次,这次读到会 key 的值依然是 v1

这个关键点在于,引入两个时间戳以后,不再是两个事务的 startTS 的比较,而是一个 startTS 跟另一个 commitTS 去比较。只要保证写操作的 commitTS 是大于所有读过当前值的 startTS 的,则是满足可重复读的。

读事务2 虽然大于写事务1 的 startTS,但是它小于写事务1 的 commitTS,所以逻辑上,读事务2 在前面,写事务1 在后面,事务2 不读到事务1 的改动。

注意这个模型里面,读是不上锁的,也不会阻塞写操作。因为只要保证最后的写事务 commitTS 一定大于所有读事务的 startTS 就行了。取两次时间戳,第二次取到的 commitTS 一定大于所有的已分配的时间戳,也就大于所有的读事务的 startTS 了。

使用一个时间戳是如何处理这个问题的。

回到最初的问题,第三步,有一个读事务,用较大的时间戳读了,再来了一个写事务,用较小的时间戳写。必须要让写事务发生在读事务后面,才能保证正确的隔离级别。两个时间戳的时候,可以通过取 commitTS 来调整顺序,让写事务晚于读。那只有一个时间戳的时候,怎么处理?

这时读需要至少在内存层加锁,这样写事务才知道,有一个时间戳在它后面的读操作,已经读过了,写事务不能再执行写操作了,也就是检测到这种情况。

然后呢?接下来写事务要把自己的时间戳往后“推”,也就是重新取一次时间戳,这样写事务的时间戳就大于读事务的时间戳了,也就是让写重排序到读的后面。或者换个说法,就是读事务会不停 “推” 写事务,遇到读写冲突的时候,读没有读到一个写事务的修改,那么读会把写的时间戳往后推,让写发生在自己后面,就可以读不到。

写事务一旦时间戳被“推”过了,它需要确认,用新的时间戳和旧的时间戳,两者读到的值是一样的,这样子才能够满足可重复读。所以它需要把旧的时间戳读过的值存下来,当时间戳被推过之后,重新读取新的值再做比较。如果这时发现有数据变了,就是不满足可重复读了,事务就需要 abort。

完整的逻辑如下:

  • 事务1 startTS = 100,读过一些值 x y z
  • 事务2 请求先到达,以 startTS = 200,读到 key 的值 v1
  • 事务1 想要修改了 key 的值,发现被事务2 读于,于是修改时间戳为 201
  • 事务1 提交,由于“推”过时间戳了,它需要用时间戳 201 重新读 x y z,判断跟时间戳 100 读的结果没有变化,否则 abort
  • 事务2 用 startTS = 200 再读一次,这次读到会 key 的值依然是 v1

两个时间戳由于读操作不加锁,有可能发生一个事务读A写B,另一个事务读B写A,最后都成功的“写偏”问题。 一个时间戳其实读是加锁了,读操作阻止时间戳比它小的写操作,破坏了上面的条件,达到可串行化。

这里只说了 DML 的影响。如果遇到了 DDL 变更,就发生了奇妙的化学反应。

两个时间戳的事务模型,它会在 startTS 取一次 schema 快照,在 commitTS 再取一次 schema 快照。也就是如果 startTS 和 commitTS 之间,schema 发生了变化,就 abort 掉事务。这会导致,异步 schema 变更算法引起 DML 失败的情况。

一个时间戳的事务模型,相当于事务发生的时间点 schema 是确定的,提交的时候就是按取到的 schema 算的。不过 DDL 遇到进行中的事务的时候,似乎也是有交互的?


Recommend

  • 35
    • bridgeforyou.cn 6 years ago
    • Cache

    Spring的声明式事务模型

    组内每周都有技术分享,轮着来,人人有份。 上次分享了 Spring的统一事务模型 ,这次聊聊Spring的声明式事务模型。 和上次一样,周五分享完,周末把keynote整理...

  • 36

    作为普通的开发人员,我们会遇到对于时间分配的思考,没有金标准,只有某些看起来也未必靠谱的“最佳实践”。不同人眼中对于整体的时间分配也有自己的看法...

  • 4

    想要实现高吞吐、高可靠的大型数据库应用系统; 想要从其他数据库迁移到MySQL; 想要进行MySQL性能调优; 那么学习和掌握InnoDB的锁和事务模型就非常有用。 14.7.1 InnoDB中的锁 本节介绍 InnoDB 中各种类型的锁。 路过...

  • 12

    局部时间戳能否实现分布式事务(下) -- 混合逻辑时钟2016-12-14接上篇,由于逻辑时钟不具备一个全局的性质,我们无法用某个时间戳拿到一个快照。混合逻辑时钟可以用...

  • 17

    局部时间戳能否实现分布式事务(上)2016-11-27刚进公司的时候,听同事说有个喷子经常黑我们,网上各种活跃,但技术实在low,所以大家都不屑于跟他辩,只是以逗他为乐:人被狗咬了,不可能咬回去,是吧?出于好奇心我去关注了一下,要看看才能做...

  • 12
    • zhuanlan.zhihu.com 3 years ago
    • Cache

    分布式事务中的时间戳

    分布式事务中的时间戳Eric Fu数据库开发者。欢迎关注官方号 @PolarDB-X时间戳(timestamp...

  • 4

    稳定币监管日益严格的背后,或是一个”主流采用时代“的开端 白泽研究院 原创 2021-10-13 06:17 热度 494190 分享 微信扫一扫:分享...

  • 5

    充满正能量的人,会喜欢一个负能量或是没有那么正能量的人吗?会觉得待在一起累吗?如果充满正能量的人没法跟不是正能量的人在一起,那么负能量的人该要怎么办啊? 后面好多人都回答说遇到一个坏小子之类的...

  • 3

    字节二面,两个事务执行 SQL 语句的过程中,导致死锁-51CTO.COM 字节二面,两个事务执行 SQL 语句的过程中,导致死锁 作者:小林coding 2022-08-04 15:31:45 如果对 MySQL 加锁机制...

  • 4

    在实际运营工作中,梳理用户路径,优化运营思路是运营人员需要考虑的主线。拉新活动作为互联网C端产品的重要运营版块,其逻辑也很重要。那么该如何运营模型来指导实践呢?本文用一个模型、两个漏斗来进行拆解分析和运营落地。希望对你有所启发。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK