数据库事务--串行化

之前提到了不同的数据库隔离级别,有一些问题需要提出:

  • 隔离级别不容易理解,在各个数据库中有不同的实现和保证;
  • 从应用代码角度很难判断一段代码在特定隔离级别下是否安全;
  • 竞态条件不容易检测到;

从应用开发者角度来看,串行化隔离可以说是最高的隔离级别了。串行化指并行事务执行结果和串行执行结果一致的保证(避免各种形式的竞态条件)。

实际串行执行

最简单的串行化保证就是真实的串行执行操作。这种方式看似牺牲了多线程执行的并行能力,实际上在最近,这种想法已经被很严肃的考虑:

  • 内存价格下降,使得内存全量数据存储变得可能,大大降低了单个事务的执行时间;
  • OLTP型事务通常不会有过多读写操作;

这种思想已经被VoltDB/H-Store,Redis和Datomic所采用。
串行执行有以下特点:

  • 每个事务需要短小快速,以防止阻塞其它所有事务;
  • 写操作执行速度由cpu来决定,或者通过数据分片来提升多核单机的并发能力;
  • 如果执行了分片策略(几乎所有分布式数据存储都需要分片),跨片的事务执行效率会很低;

过于单线程串行化的数据库,RedisVoltDB都非常值得学习。

两段锁

之前说过,锁可以有效避免脏读脏写。两段锁要求多事务可以同时访问某条没有被写锁加锁的数据,如果被写锁加锁,则:

  • 如果A事务要求读X数据,后来的B事务要求写X数据,则B必须在A完成或者回滚之后才能执行;
  • 如果A事务要求写X数据,后来的B事务要求读X数据,则B必须在A完成或者回滚之后执行;

在两段锁的具体实现上:

  • 事务读取数据需要获取数据的共享锁(shared lock),一条数据允许同时发放多个共享锁,除非它已经被互斥锁(exclusive lock)锁定;
  • 事务更新数据需要获取数据的互斥锁(exclusive lock),一条数据同一时间只能发放单个互斥锁;
  • 如果一个事务先读后写,则需要把共享锁升级成互斥锁;
  • 事务获取锁之后,只有在事务完成或者回滚之后才会释放锁(这就是两段锁的含义:一个阶段获取锁,一个阶段释放锁),需要注意的是,两段锁并不能避免死锁,死锁发生时一般会选择先回滚一个事务;

串行化快照隔离(Serializable Snapshot Isolation)

乐观并发控制VS悲观并发控制:

上面提到的两段锁就是典型的悲观锁。串行化执行等价于为每一个事务加上互斥锁,为了减少单个事务持有锁的时间,只能把事务拆分成较小的粒度。与之相反,串行快照隔离是乐观锁。串行快照隔离,字面上来讲还是在读取阶段保证数据来自于一个稳定版本的快照,加上对于线性执行的冲突检测来决策是否应该回滚某一个事务。为了避免可能出现的写倾斜,有下面两种方法:

  • 检测过去MVCC读取(Detecting stale MVCC reads)
    根据MVCC所带有的版本号来检测读取之后发生的数据变更:


    Detecting stale MVCC reads

    如果检测到了这种情况,需要事务回滚重试。以此避免写倾斜。

  • 检测影响先前读取的写入
    这种情况如图所示:


    图示
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点。 原子性:要不全部成功,要不全部撤销 隔...
    jiangmo阅读 1,207评论 0 3
  • 事务的定义 事务由单独单元的一个或多个SQL语句组成,在这个单元中,每个MySQL语句是相互依赖的。而整个单独单元...
    诸葛坚强阅读 1,193评论 0 3
  • 今天一大早起来在家翻译文章,以此纪念孙仲旭先生。 孙先生的译著,我读得不多,但是一直有看他的微博和豆瓣,看他记录儿...
    Finikz阅读 377评论 0 2
  • 午后我又做了那个梦 那个永远不想醒来的梦 梦里我回到了我的童年 回到那个属于80后最淳朴的年代 那条深不见底的胡同...
    道先生视界阅读 441评论 0 1
  • 新闻界出了个“一点资讯”,让诸多写手欲罢不能! 众生眼里有了“二个文化”,让世人取长补短学习! 商界传奇惊现“三驾...
    梁静_4bef阅读 126评论 0 0

友情链接更多精彩内容