MySQL应用技术3 — MVCC

MySQL应用技术1 — MySQL架构简介
MySQL应用技术2 — 事务简介
MySQL应用技术4 — 数据类型选择
MySQL应用技术5 — 约束与范式
MySQL应用技术6 — 数据库中的锁
MySQL应用技术7 — 性能优化简析


简单说下,前几章将大部分都是概念介绍,其实我也在想,如何才能将自己掌握的东西表达出来,而又保障知识点的准确性,前两章采用了【斜体】的方式表明了哪些是自己的见解。但是总体看下来,个人见解部分还是太过少了。本章开始将不再局限于当前题目的知识点,可能会涉及到更多的内容,不过可能有的东西就没有太过考证,如有疏漏,还望大家不吝赐教


在前一章讲到了数据库的事务,然而在大多数支持事务的MySQL引擎的实现并不是简单的行级锁。基于提升并发性能的考虑。他们一般都实现了多版本并发控制(MVCC)。其实包括Oracle,PostgreSQL在内的数据库均实现了MVCC,但各自的实现集中都不尽相同,因为MVCC并没有一个统一的实现标准。

其实MVCC可以认为是行级锁的一个变种,它在大多数情况下避免了加锁操作,因此竞争开销更低。大部分MVCC的实现机制都做到了非阻塞的读操作,写操作也只锁定必要的行
【InnoDB中写操作只锁定必要的行其实是有歧义的,或者说对“必要的行”可能没有大家想象的这么美好。因为当使用Update语句时,所锁定的行其实还是要看where条件是否命中索引,如果update语句命中了索引,那么ok只会锁受影响的行。否则将是整个表的写锁(tip1:不过读操作并不会收到影响,毕竟是非阻塞的读操作。tip2:新版本的MySQL中在将数据从存储引擎中取到MySQL服务器后,过滤出要更新的行之后,那么将释放那些没有收到影响行的锁。并不会等到事务结束后再进行释放(tip2.1 其实这里就违反了两段锁协议))】

InnoDB中的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存了行的过期或删除时间。当然存储的并不是时间的时间,而是系统版本号(system version number)。每开启一个新的事务,系统的版本号都会自动递增。事务以当前版本号为基准进行比较。下面来看下在可重复读的隔离级别下,MVCC是具体是如何操作的。

SELECT

InnoDB 会根据两个条件来检查每行记录:

  • InnoDB只查找版本(DB_TRX_ID)早于当前事务版本的数据行(行的系统版本号<=事务的系统版本号,这样可以确保数据行要么是在开始之前已经存在了,要么是事务自身插入或修改过的)
  • 行的删除版本号(DB_ROLL_PTR)要么未定义(未更新过),要么大于当前事务版本号(在当前事务开始之后更新的)。这样可以确保事务读取到的行,在事务开始之前未被删除。
INSERT

InnoDB为新插入的每一行保存当前系统版本号作为行版本号

DELETE

InnoDB为删除的每一行保存当前的系统版本号作为行删除标识

UPDATE

InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识

【其实这里无论update操作还是delete操作,都是做了删除标识操作,可能大家就在想,那我不断的update,岂不是会有很多旧的记录,这么多会不会浪费存储空间,我个人理解的是会在一定时间后物理删除那些被标记为删除的行。然后此时的MVCC机制则通过 undoLog来实现。至于undoLog ,我发现了一篇比较好的文章,大家想要了解的可以去读下: 一文了解InnoDB事务实现原理 中的第二部分 undoLog】

通过保存这两个额外系统版本号,使大多数读操作都可以不用加锁【不知道为啥是大多数,书上这么想的,我也没想到那种还得加锁,除了显示声明加锁】。这样设计是的数据库读操作变得很简单,性能很好,也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及额外的维护工作。

对于上一章说道的四个隔离级别,只有读已提交,和可重复读是由MVCC机制实现的。读未提交则是每次都读最新的行数据,串行化则是对所有读取的行也都会加锁。(MySQL应用技术2 — 事务简介)

文中【斜体】部分多为个人理解,不足支持还望指正。

欢迎注明出处及本文链接的转载。

你可能感兴趣的:(MySQL应用技术3 — MVCC)