MySQL MVCC实现及其机制
Multi-Version Concurrency Control 多版本并发控制
大多数的MySQL事务型存储引擎,如InnoDB,Falcon以及PBXT都不使用一种简 单的行锁机制。
事实上,他们都和和另外一种用来增加并发性的被称为“多版本并发控制(MVCC)”的机制来一直使用。
MVCC不只使用在MySQL 中,Oracle,PostgreSQL以及其他一些数据为系统也同样使用它。
你可将将MVCC看成行级别锁的一种妥协,它在许多情况下避免了使用锁,同时可以提供更小的开销。
根据实现的不同,它可以允许非阻塞式读,在写操作进行时只锁定必要的记录。
MVCC会保存某个时间点上的数据快照。
这意味阒事务可以看到一个一致的数据视图,不管他们需 要跑多久。
这同时也意味着不同的事务在同一个时间点看到的同一个表的数据可能是不同的。
如果你从来没有过种体验的话,可能理解起来比较抽象,但是随着慢慢 地熟悉这种理解将会很容易。
各个存储引擎对于MVCC的实现各不相同。
这些不同中的一些包括乐观和悲观并发控制。
我们将通过一个简化的InnoDB版本的行为来展示MVCC工作的一个侧面。
InnoDB:通过为每一行记录添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行 数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。但是InnoDB并不存储这些事件发生时的实际时间,相反它只存储这些事件发生时的系统版 本号。这是一个随着事务的创建而不断增长的数字。每个事务在事务开始时会记录它自己的系统版本号。每个查询必须去检查每行数据的版本号与事务的版本号是否 相同。让我们来看看当隔离级别是REPEATABLE READ时这种策略是如何应用到特定的操作的: SELECT InnoDB必须每行数据来保证它符合两个条件:
1、InnoDB必须找到一个行的版本,它至少要和事务的版本一样老(也即它的版本号不大于事务的版本号)。这保证了不管是事务开始之前,或者事务创建时,或者修改了这行数据的时候,这行数据是存在的。
2、这行数据的删除版本必须是未定义的或者比事务版本要大。这可以保证在事务开始之前这行数据没有被删除。
符合这两个条件的行可能会被当作查询结果而返回。
INSERT:InnoDB为这个新行记录当前的系统版本号。
DELETE:InnoDB将当前的系统版本号设置为这一行的删除ID。
UPDATE:InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为当前的系统版本号。
它同时也会将这个版本号写到旧行的删除版本里。
这种额外的记录所带来的结果就是对于大多数查询来说根本就不需要获得一个锁。
他们只是简单地以最快的速度来读取数据,确保只选择符合条件的行。
这个方案的缺点在于存储引擎必须为每一行存储更多的数据,做更多的检查工作,处理更多的善后操作。
MVCC只工作在REPEATABLE READ和READ COMMITED隔离级别下。
READ UNCOMMITED不是MVCC兼容的,因为查询不能找到适合他们事务版本的行版本;
它们每次都只能读到最新的版本。
SERIABLABLE也不与 MVCC兼容,因为读操作会锁定他们返回的每一行数据[1]。
编辑本段说明
通过使用MVCC(Multi-Version Concurrency Control)算法自动提供并发控制。MVCC维持一个数据的多个版本使读写操作没有冲突。也就是说数据元素X上的每一个写操作产生X的一个新版 本,GBase 8m为X的每一个读操作选择一个版本。由于消除了数据库中数据元素读和写操作的冲突,GBase 8m得到优化,具有更好的性能。特别是对于数据库读和写两种方法,他们不用等待其他同时进行的相同数据写和读的完成。在并发事务中,数据库写只等待正在对 同一行数据进行更新的写,这是现有的行锁定方法的弱点。同时MVCC回收不需要的和长时间不用的内存,防止内存空间的浪费。MVCC优化了数据库并发系 统,使系统在有大量并发用户时得到最高的性能,并且可以不用关闭服务器就直接进行热备份。编辑本段比锁定的优势
使用MVCC多版本并发控制比锁定模型的主要优点是在MVCC里, 对检索(读)数据的锁要求与写数据的锁要求不冲突, 所以读不会阻塞写,而写也从不阻塞读。 在数据库里也有表和行级别的锁定机制, 用于给那些无法轻松接受 MVCC 行为的应用。 不过,恰当地使用 MVCC 总会提供比锁更好地性能。编辑本段GBase8的特性
在 GBase 中的查询功能通过 MVCC 提供的一致性非锁读(在下文我们简称为一致性读),就是提供通过数据库在一个时间点上的快照来实现信息的查询。查询只是对那些在这个时间点之前提交的事务 所做的变更,而并不关注在时间点之后的变更或未提交的事务。当然,若是该事务自身进行的变更,对于查询是可见的。 GBase 的默认级别是 READ COMMITTED ,在该隔离级别下事务中的查询语句,使用当前时间戳进行一致性读,每次查询的时间戳是不相同的。 但对 REPEATABLE READ 隔离级别,在同一个事务中的所有一致性读,使用的时间戳均是第一个查询的时间戳,这样读取的也就是由该事务第一次读建立起来的数据快照。用户只有通过提交当前事务,并发出一个新的查询才会得到新的数据快照。 一致性读是 GBase 在 READ COMMITTED 和 REPEATABLE READ 隔离级别下,处理 SELECT 语句中使用的默认模式。一致性读在它读的数据上不设置任何锁,因此在一致性读某个表的同时,其它用户均可以修改这个表。 注意在 DROP TABLE 和 ALTER TABLE 运作时,一致性读无效 。一致性读在 DROP TABLE 上无效是因为 GBase 不能使用已经 drop 的表,该表已经删除。一致性读在 ALTER TABLE 上无效是因为 GBase 会在事务内,重新创建一个新表并从旧表向新表插入记录。这样当用户再次执行一致性读时,在新表中将看不到任何行,因为在新表中的数据都在第一次一致性读的 快照之外。- 参考资料
-
-
1
《高性能MySQL》第二版
-
1