您的位置:首页 > 数据库

数据库事务原子性、一致性是怎样实现的?

2016-06-14 15:05 417 查看
转自知乎,原文链接:https://www.zhihu.com/question/30272728/answer/71927112

讨论数据库的事务原子性,先看最极端的情况,即全局一把锁,所有事务排队执行,这种情况下没有原子性问题,因为所有事务看到的都是在自己之前已经提交的数据。

为了提高性能,充分利用多核,我们需要让多个事务能够并行的执行,但是还要保证这些事务“看起来”是串行执行的(external consistency)。这里需要考虑事务的三种关系,即读与读的关系,写与写的关系,读与写的关系。

读事务与读事务的关系最简单,因为不对数据进行修改,因此读与读之间可以直接并行

写事务与写事务的关系,对同一条记录的修改,需要保证串行,不能出现lost update的情况,一般通过行锁(oracle/mysql/oceanbase),或者事先通过SQL分析将可能冲突的事务排队后执行(calvin/oceanbase)

读事务与写事务的关系,这个最复杂,因为数据库操作中,几乎没有只写的情况,一般都是“read-modify-write”,比如最简单的update ... where 还有 update set c=c+1 whete,绝大部分写事务都是在读取一些数据之后,再修改数据,我们称这类事务为“读写事务”。因此读与写关系,会涉及两类关系:

只读事务与读写事务的关系,一般使用多版本的方式保存数据,每个事务分配一个全局唯一且递增的“事务版本号”,更新数据时将事务版本号也保存在数据中。在全局维护一个“最大已提交的”版本号(committed version),一般就是一个64或128位的整数,每个只读事务开始时,原子的读取这个commited version,读取数据时,只读取版本号小于等于它的内容。多版本的实现方式各家不尽相同;数据存储方面oracle与innodb都是使用data block + undo block的方式,历史版本保存在undo
block中,oceanbase内存引擎则简单的将一行所有的修改历史串成反向链表;对于事务版本号,oracle与oceanbase都在事务提交时生成版本号,可以保证版本号的大小严格遵守事务提交顺序,但是需要在事务提交时(oceanbase)或提交后(oracle)将事务版本号回填到数据内容中,mysql则简单的在事务开启时生成版本号,因此读取逻辑相对复杂,需要过滤掉开始事务时尚未结束事务对数据的修改。

读写事务之间的关系,这里需要考虑的是一个读写事务T1在执行过程中,它刚刚读过的数据被其他事务修改的情况,这种情况下T1需要回滚重做(单语句事务)或报告事务冲突(交互型事务),一般的做法是在T1提交时对涉及到的行加锁后检查版本号或内容,在read committed隔离级别下这个特性并非标准所要求,但是oracle/mysql/oceanbase都在语句级别实现了,也成为了事实标准,按oracle的叫法叫做transaction set consistency
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: