Innodb中的MVVC

MySQL中有读锁和写锁,在没有引入MVVC之前,读锁是允许共享读,但是如果一行记录事先被上了写锁,那么就不允许其他事务进行读,现在的大部分应用都具有读多写少的特性,为了进一步增加并发的性能,于是引入了MVVC。

基础知识

四种隔离级别
  1. serializable (串行化):可避免脏读、不可重复读、幻读的发生。
  2. repeatable read (可重复读):可避免脏读、不可重复读的发生。
  3. read committed (读已提交):可避免脏读的发生。
  4. read uncommitted (读未提交):最低级别,任何情况都无法保证。

什么是MVVC

MVVC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)是一种基于多版本的并发控制协议,只有在InnoDB引擎下存在。MVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,可以认为MVCC是行级锁的一个变种, 但是它在很多情况下避免了加锁操作, 因此开销更低。当然,这种乐观锁只在事务级别提交读和可重复读有效。MVCC最大的好处:读不加锁,读写不冲突。在读多写少的OLTP(联机事务处理)应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。

MVVC的实现机制

多版本并发控制仅仅是一种技术概念,并没有统一的实现标准, 其的核心理念就是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。虽然字面上是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。

数据库的事务有时需要进行回滚操作,这时就需要对之前的操作进行undo。因此,在对数据进行修改时,InnoDB会产生undo log。当事务需要进行回滚时,InnoDB可以利用这些undo log将数据回滚到修改之前的样子。

MVVC下的CRUD

  • SELECT

读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的。

  • INSERT

将当前事务的版本号保存至行的创建版本号

  • UPDATE

新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号

  • DELETE

将当前事务的版本号保存至行的删除版本号

(1)在插入操作时:记录的创建版本号就是事务版本号。
比如我插入一条记录, 事务id 假设是1 ,那么记录如下:也就是说,创建版本号就是事务版本号。

idnamecreate versiondelete version
1tom1

(2)在更新操作的时候,采用的是先标记旧的那行记录为已删除,并且删除版本号是事务版本号,然后插入一行新的记录的方式。

比如,针对上面那行记录,事务Id为2 要把name字段更新。

update table_name set name= 'new_value' where id=1;
idnamecreate versiondelete version
1tom12
1tom22

(3)删除操作的时候,就把事务版本号作为删除版本号。比如:

delete from table_name where id=1;
idnamecreate versiondelete version
1tom23

(4)查询操作:从上面的描述可以看到,在查询时要符合以下两个条件的记录才能被事务查询出来:

  • 删除版本号 大于 当前事务版本号,就是说删除操作是在当前事务启动之后做的。
  • 创建版本号 小于或者等于 当前事务版本号 ,就是说记录创建是在事务中(等于的情况)或者事务启动之前。

这样就保证了各个事务互不影响。从这里也可以体会到一种提高系统性能的思路,就是:通过版本号来减少锁的争用。MVCC只在 READ COMMITTEDREPEATABLE READ 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

1 人推荐

声明:本文原创发布于加藤非博客,转载请注明出处:加藤非博客 jiatengfei.com 。如有侵权,请联系本站删除。

加藤非博客
请先登录再发表评论
  • 最新评论

  • 总共0条评论