Google Spanner事务机制解析: 我的事务为什么被中止?Transaction was aborted

目前公司几乎所有的项目都使用Spanner,在我们部署的项目中发现偶尔会有Transaction was aborted的情况,报错如下:

Transaction was aborted

很多同学可能以为是死锁才会导致Transaction被中止,其实并不是,是一个比死锁更宽泛的情况——事务读写冲突,读锁被aborted,因此事务被aborted。

下面解释读锁为什么会被aborted。

阅读本文之前最好能够理解事务隔离级别,下面不会详细讲解隔离级别的知识。

Spanner事务隔离

Spanner的事务是可串行化的(Serializable),可串行化是最高隔离级别,每个事务看起来像是串行执行的,也就是每个事务从外部看起来是有顺序的,这就是可串行化。并且外部观察到的执行顺序与每个事务的commit timestamp顺序相同,这就是Spanner所说的外部一致性(External Consistency)

如何保证外部一致性

外部一致性 = 可串行化 + commit timestamp决定事务顺序

先说说可串行化,大家熟悉的InnoDB是使用加悲观锁的方式实现可串行化,读操作加读锁,写操作加写锁,事务A如果要写已经被事务B加上锁的数据,则需要等待事务B释放锁,MySQL对锁100%是采取等待的方式,这也是为什么会出现死锁,因为双方互相等待,InnoDB中事务的读写冲突本身不会导致事务被中止。

那么Spanner有什么不同呢?

Spanner不是100%采取等待的方式,它可能会abort别的事务的锁,锁被aborted的事务就会中止。这就是Spanner文档中所说的伤停等待(wound-wait),abort锁导致事务中止就是伤停。我们知道读锁和读锁是不冲突的,因此只有读锁和写锁才是冲突的,那么说明是一方读另一方写时可能会造成某一方被aborted。

是哪一方会被aborted呢?

年轻的事务

怎么区分年轻和年老?

越早启动的事务越年老,越晚启动的事务越年轻,什么叫启动?每个事务第一次进行读写操作时,Spanner会为其生成一个start timestamp,即为启动时间。这里注意,是进行第一次读写时的,而不是begin一个Transaction时

在Spanner中,只有Read操作会被马上执行并获取锁,Write操作都会被缓存在client本地,并没有真的Write,也不会获取锁,只有commit被调用后才会一次性发送到server,尝试执行并获取锁,因此一个pending的事务是没有写锁的,只有读锁。那么读写冲突的产生就一定是在一个事务pending另一个事务commit时,pending的事务持有读锁,而commit的事务想要获取写锁,此时:

  1. 如果commit事务比pending事务年轻,那么它需要等待pending事务主动释放读锁,才能获取写锁,此时采用等待策略。
  2. 如果commit事务比pending事务年老,那么它会直接abort掉pending事务的读锁,成功获取写锁并提交, 此时采用伤停策略,pending事务被aborted。

举个栗子

等待策略(年轻事务等待年老事务释放锁)

先说一下我们最熟悉的等待策略,也是InnoDB的锁策略。

  • 首先我们begin两个事务,注意:此时并不会给事务生成start timestamp,因此begin的顺序是不影响结果的。
  • 在左边的事务(下称事务A)中select * from ID为0的数据,此时事务A进行了第一个读操作,Spanner为其生成start timestamp。
  • 紧接着在右边的事务(下称事务B)中select * from 同一行数据,此时事务B进行了第一个读操作,生成start timestamp,那么这个timestamp一定是晚于事务A的,因此事务A更年老、事务B更年轻。
  • 然后事务B立即更新同一行数据的LastName列,并且commit。
  • 由于事务B更年轻,因此其commit将不会返回成功,而是一直等待,需要等待年老的事务A释放锁。


    年轻事务等待年老事务释放锁

伤停策略(年轻事务被aborted)

  • 首先我们begin两个事务,注意:此时并不会给事务生成start timestamp,因此begin的顺序是不影响结果的。
  • 在左边的事务(下称事务A)中select * from ID为0的数据,此时事务A进行了第一个读操作,Spanner为其生成start timestamp
  • 紧接着在右边的事务(下称事务B)中select * from 同一行数据,此时事务B进行了第一个读操作,生成start timestamp,那么这个timestamp一定是晚于事务A的,因此事务A更年老、事务B更年轻。
  • 事务A更新这一行的LastName
  • 事务A commit,此时A会获取LastName列的写锁,而发现B已经占有读锁,对比timestamp发现B更年轻,因此直接abrot B事务的读锁,最后成功提交
  • 事务B在A提交后也进行update,发现自己已被aborted,结束。


    年轻事务被aborted

需要注意的是,Spanner获取锁的粒度是列,不是行,因此冲突是在列上,报错将会是

conflict on keys in range (xxx), column LastName in table Singers

总结

可以看出,如果同一个包含读写冲突的事务代码在短时间内被执行两次,且先执行的先commit了,就会出现后执行的那个事务被aborted的情况,这也是开头讲到的,我们会收到Transaction was aborted的原因。

或者是,有两个不同的事务代码,紧接着被执行,且它们有读写冲突,年老的事务先commit,就会造成年轻事务aborted。

Spanner的SDK都有提供事务重试,根据Spanner文档,重试的事务将会以旧的timestamp重启,因此事务不会出现饿死的现象,最终一定有机会被执行成功。

更佳实践

但是我们还是应该思考,这些冲突的事务是真的需要每一个都得到执行,还是只是不小心被重复调用,只执行其中一个就能满足业务?如果执行一次就能满足业务,那么其他的重复事务会造成Spanner的资源浪费,因此被aborted的事务会重试,如果多个事务一起重试,还是可能会出现aborted,然后再次重试,因此需要尽量避免短时间内的无意义的重复调用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349