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

作者:郁白
链接:http://www.zhihu.com/question/30272728/answer/71927112
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

  1. 读事务与读事务的关系最简单,因为不对数据进行修改,因此读与读之间可以直接并行
  2. 写事务与写事务的关系,对同一条记录的修改,需要保证串行,不能出现lost update的情况,一般通过行锁(oracle/mysql/oceanbase),或者事先通过SQL分析将可能冲突的事务排队后执行(calvin/oceanbase)
  3. 读事务与写事务的关系,这个最复杂,因为数据库操作中,几乎没有只写的情况,一般都是“read-modify-write”,比如最简单的update ... where 还有 update set c=c+1 whete,绝大部分写事务都是在读取一些数据之后,再修改数据,我们称这类事务为“读写事务”。因此读与写关系,会涉及两类关系:
    1. 只读事务与读写事务的关系,一般使用多版本的方式保存数据,每个事务分配一个全局唯一且递增的“事务版本号”,更新数据时将事务版本号也保存在数据中。在全局维护一个“最大已提交的”版本号(committed version),一般就是一个64或128位的整数,每个只读事务开始时,原子的读取这个commited version,读取数据时,只读取版本号小于等于它的内容。多版本的实现方式各家不尽相同;数据存储方面oracle与innodb都是使用data block + undo block的方式,历史版本保存在undo block中,oceanbase内存引擎则简单的将一行所有的修改历史串成反向链表;对于事务版本号,oracle与oceanbase都在事务提交时生成版本号,可以保证版本号的大小严格遵守事务提交顺序,但是需要在事务提交时(oceanbase)或提交后(oracle)将事务版本号回填到数据内容中,mysql则简单的在事务开启时生成版本号,因此读取逻辑相对复杂,需要过滤掉开始事务时尚未结束事务对数据的修改。
    2. 读写事务之间的关系,这里需要考虑的是一个读写事务T1在执行过程中,它刚刚读过的数据被其他事务修改的情况,这种情况下T1需要回滚重做(单语句事务)或报告事务冲突(交互型事务),一般的做法是在T1提交时对涉及到的行加锁后检查版本号或内容,在read committed隔离级别下这个特性并非标准所要求,但是oracle/mysql/oceanbase都在语句级别实现了,也成为了事实标准,按oracle的叫法叫做transaction set consistency
参考文献:
[1] Thomson A, Diamond T, Weng S C, et al. Calvin: fast distributed transactions for partitioned database systems[C]//Proceedings of the 2012 ACM SIGMOD International Conference on Management of Data. ACM, 2012: 1-12. MLA
[2] Berenson H, Bernstein P, Gray J, et al. A critique of ANSI SQL isolation levels[C]//ACM SIGMOD Record. ACM, 1995, 24(2): 1-10.
[3] LI Kai,HAN Fu-Sheng. Memory transaction engine of OceanBase[J]. Journal of East China Normal University(Natural Sc, 2014, 2014(5): 147-163.
[4] Lewis J. Oracle Core: Essential Internals for DBAs and Developers[M]. Apress, 2011.
 
作者:韩忠康
链接:http://www.zhihu.com/question/30272728/answer/95908054
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原子性:一个事务内的所有SQL操作是一个整体。都执行成功才算整个事务成功。如果某个失败,则必须要会退到事务执行之前的状态,执行成功的SQL需要被撤销。

innodb通过undo log和redo log来实现。


事务中,每当执行一条SQL语句对数据产生了影响,就会记录下来与之相反的操作到undo log(撤销日志)中,例如,更新会记录之前的状态,删除会形成insert,添加会形成delete,一旦事务被回滚,则执行undo log中记录的操作,来完成恢复到之前的状态。这里是个 逻辑恢复哦!
同时,每当执行一条事务中的SQL,会将操作记录到redo log中,此时事务一旦被提交,就将该redolog中的操作,持久化到磁盘上,数据就持久的记录下来了(ACID的D)。

PS:还有,undolog才是原子性的关键。提供redolog,应该主要目的是提升磁盘的IO开销吧,如果直接写入磁盘,IO开销,会很大。如果先将操作记录到redolog中,可以顺序的记录,批量的记录,再一起同步到磁盘上,速度会比直接写磁盘快些。 mysql在生成redolog时,会使用 innodb log buffer,先缓冲到内存中,再同步到redolog上。速度会更快

另外关于,一致性,应该是个整体概念,保证所有的mysql对象(数据,索引,约束,日志,用户)在事务执行前后都具有完整的特性,应该是mysql所有的功能都为此服务吧!
http://www.zhihu.com/question/30272728

猜你喜欢

转载自m635674608.iteye.com/blog/2302225