前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >闲话聊聊事务处理(中)

闲话聊聊事务处理(中)

作者头像
哒呵呵
发布2018-08-06 14:22:50
4340
发布2018-08-06 14:22:50
举报
文章被收录于专栏:鸿的学习笔记鸿的学习笔记

上面提到了multi-object事务,但是要完美的处理multi-object事务并不容易。因为我们必须要面对并发问题导致的bug,而隔离性要求数据系统必须要向使用者把并发问题隐藏起来,让使用者因为只有自己一个人使用。在实践中,这个并不容易做到,完美的隔离性要付出相当大的性能代价,所以大多数的数据库提出了Weak Isolation Level的概念,虽然弱化版的隔离性还是会导致各种潜在的问题,但是这个代价相对于性能的巨大提升是可以接受的。那我们来看看几种不同的Weak Isolation Level。

  1. Read Commited

ReadCommited实际上可以分为两点,一是没有脏读(在你向数据库发送读请求时,你只能读到已经commit的数据),二是没有脏写(在你向数据库写入数据时,你只能覆盖已经commit的数据)。这不是非常严格的隔离了两个并发操作,仅仅只是要求读和写操作只能针对已经commit的数据。如果不解决脏读,对于类似update-after-read的事务,意味着可能会导致不正常的操作,并且如果一个事务失败了,需要roll back,那么发生了脏读的事务该如何处理呢?同样的,如果不解决脏写,两个事务针对同一个object进行了修改,两个事务都没有commit,那么最终结果该选谁?

既然脏读和脏写会出现这些问题,我们的解决办法该是如何呢?最常见的避免脏写的方式是选择一个low-level锁:当事务试图修改一个特定的object时,首先得获得一把锁,直到事务commit或者abort,锁才会被释放。其他的事务想修改这个object,必须等待持有锁的事务释放锁。同样的,脏读也可以通过这样的锁去解决这个问题,不过,显得有些过于杀鸡用牛刀了,更好的办法是采用版本的概念,将数据简单的区分为没有commit和commit了的,最后进行合并。

  1. Repeatable Read

Read Commited避免了事务读写其他事务的中间过程,但是对于并发bug依然存在。比

如,有两张表A和B,A存入了数据500,B存入了数据500,现在A向B转入了100,这样我们需要使得A减去100,B增加100,此时有一个观察者C,在A减去100时,向A发送请求,读到A=400,又同时向B发送请求,问题来了,B增加100的请求在B还没生效,于是读到B=500。在观察者而言,第一次读到的A+B=900,但是过了一会儿,再次发送请求,会发现A+B=1000。这种现象便就是Read Skew。

初看之下,这个似乎没什么大的问题,只要retry,数据便会正常了,但是我们要注意到遇上某些情况,Read Skew这种暂时状态便会无法令人接受了。例如,对于数据备份,我们会备份一个错误的数据,那么数据恢复也就不可靠了,对于数据分析而言,一次性会读入数据然后进行分析,读入错误数据也会导致数据分析失败。

这个问题的解决办法就是snapshot isolation。简单来讲,每个事务的数据读取都来源于数据库的consistent snapshot,换句话说,在每个事务开始前,都会读入数据库里所有已经commit的数据。即使某个数据会被另一个事务修改,每个事务都会载入数据库某个时间节点的老数据。

在前面的脏写使用了锁的概念,而snapshotisolation的核心在于读不会阻塞写,写不会阻塞读。最为出名的实现snapshot isolation的技术就是多版本并发控制(MVCC)。通过区分不同的版本来处理数据。

  1. Preventing Lost Updates

Read Commited和snapshot isolation都只是保证了只读的事务在并发情况下不会出问题,

而写的事务,我们也仅仅时讨论了脏写的情况,而对于并发写的情况,还有一种叫做Lost Update,这个的问题会出现的如下的情况:如果一个事务需要先读取数据,然后修改数据,再将数据写回(read-modify-write cycle),倘若两个事务并发进行,那么其中一个修改需要被放弃,保留一个数据。在现实生活,这个很常见,比如共同编辑同一个文件,给购物车添加东西。

解决办法呢?最简单的办法就是Atomic write,通过排他锁,锁住需要修改的object,直到修改完毕。或者稍微放松点条件,强制使得两个事务有序,让一个事务等待另一个事务执行完毕。如果不使用锁的话,还可以让两个事务同时进行,在一个事务commit时,告诉它这个老数据已经被修改了,这样的操作需要数据系统提供一个检测,识别出并发写正在进行。锁的操作也仅仅限于在只有一个备份的情况,如果数据是多备份的,加锁会严重影响性能,也不好实现,更多的是采取检测并发写和多版本管理,将问题抛出来,由应用端解决,或者retry。

  1. Write Skew and Phantoms

除了上面讨论的情况,我们再看看一些并发bug的情况。想象我们有这么两个事务A和B,由应用端发起的,两个事务模式是一样的,假设有三条数据a,b,c等于1,事务中要保证a,b,c三条数据中至少有两条等于1,A和B都会先select所有数据判断a,b,c有多少等于1,如果发现a,b,c都等于1的话,再将对应的a或b置为0。现在A和B同时发送select语句给了数据系统,发现a,b,c都等于1,A和B便将对应的a,b置为0了,问题来了,前提条件是a,b,c至少有两条等于1,现在因为并发写导致a,b都为0,前提条件失效了。

与前面讨论的不同,这是两个事务修改两个不同的object,这种现象在计算机里被称之为write skew。对于write skew,面临的情况更严重,1.原子写是没用的,因为你需要锁死所有object,不仅仅是一个object,而这对性能的影响,忘了它吧。2.如果使用自动检测并发冲突,需要真正的序列化隔离,而不是简单的判断单个的object。我们又不想锁死所有object,那么该如何解决呢?

首先,让我们分析下write skew的模式,在例子中,我们会注意这个问题的来源于select的判断失误,A和B的select获得了结果,然后应用端基于这个结果做出了判断,再进而对数据做出了修改。我们可以理解为A和B的select出现了“幻读”。Snapshot isolation只是避免了read-only的事务出现问题,而无法解决read-write的事务的问题。

借由4提出的问题,让我们进入到serializability的世界。Serializable Isolation是一种相当严格的隔离。它保证了即使事务出现了并发的情况,也能保证每个事务并不会受到其它事务的影响。说的这么美好,那么我们来看看要实现Serializable Isolation的具体方法。

1.真正的序列化执行

最简单的实现Serializable Isolation的方法就是移除并发,在每个时间段只在一个线程里只执行一个事务。当然听起来并不优雅,但是随着RAM性价比的提升,而且OLTP在每次只会修改少量数据,这个想法在Redis等数据库得到了实现。不过,在单个CPU上的运行,吞吐量终究会是个问题。

Redis等数据库还是很年轻的,那么在更早的数据系统该是如何改善的呢?一个很重要的方法就是存储过程,将一系列的操作包裹在一起执行,再commit。比如上述的write skew问题,可以将所有过程交由数据系统处理,而不用经过应用端的判断,这样的话,可以尽量避免磁盘IO和网络问题。存储过程听起来很美好,本身依然存在很多问题,比如每个数据系统都拥有自己独特的存储过程语言,而不像sql有着标准,并且存储过程运行在数据库之上,不受应用端管辖,那么出了问题也不好解决,最重要的是,数据系统在一般的应用中比应用端更重视性能,存储过程太过于依赖数据系统,性能会更容易遇到瓶颈。

还有一个解决单CPU的吞吐问题方案,那就是将数据分区,不过这是有需要一个协调者去处理分区问题,性能往往比不分区还差。

2.两阶段锁

这是实现Serializable Isolation最为广泛的算法。算法的思想很简单,1.如果事务A向读取一个object,事务B向修改那个object,那么B必须等待A事务commit或者abort才能开始运行。2.如果事务A想修改一个object,事务B想要读取那个object,那么事务B必须等待到A事务commit或者abort才能运行。

在具体的实现中,大部分的数据系统使用了共享锁来处理,每个事务要处理的时候必须要先获得这把锁。在后续的发展也进化出了Predicate锁和Index-range锁来减少两阶段锁带来的性能问题。

并发和性能问题看起来如此难以解决,在08年有人提出了Serializable Snapshot Isolation(SSI),来解决两阶段锁的性能问题。SSI使用了乐观锁和snapshot isolation的结合,在read-write事务中,SSI会先判断write是否过时了,再决定这个事务是否可以commit。由于应用的还不是太广泛,大家可以参考相关的文献来更加深入了解SSI。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档