并发是指在同一时刻,多个操作并行执行。MySQL主要应用两种机制来处理并发——“锁”和“多版本控制”。锁分为读锁和写锁两种,也称为共享锁和排它锁。因为多个读操作不会同时破坏数据,读锁是共享的,多个读操作可以同时进行,互不干扰。为了防止多次写操作破坏数据,写锁是独占的。一个写锁会阻塞其他的写锁和读锁,从而保证任何时候对同一个资源只能进行一次写操作,防止其他用户读取正在写入的资源。在锁粒度上,MySQL包括表锁和行锁两种。锁的粒度越小,越有利于数据库操作的并发执行。但是管理锁会消耗更多的资源。如果系统花费大量时间来管理锁而不是存储数据,那么系统的性能也会受到影响。表锁锁定整个表,是成本最低的策略。诸如ALTERTABLE之类的语句使用表锁。行锁在最大程度上支持并发操作,但也带来了巨大的开销。InnoDB实现了行锁。锁不仅仅用于维护MySql中的并发控制。事务隔离级别transaction的概念这里就不介绍了。在我看来,事务也可以看作是并发的一部分——事务包含一组操作,事务之间可以并行执行事务。事务与事务之间的并发也会像普通的并发操作一样共享资源,这样并发执行的事务就会相互影响。根据事务之间不同程度的影响,提出了事务隔离级别的概念,即READUNCOMMITTED、READCOMMITTED、REPEATABLEREAD、SERIALIZABLE。READUNCOMMITTED意味着一个事务对共享数据的修改可以立即被另一个事务感知到。其实对于修改操作并没有什么特别的处理。SERIALIZABLE通过锁定强制执行事务的串行执行,这可以避免幻读。但是这种方式会带来很多锁争用的问题。READCOMMITTED和REPEATABLEREAD是基于MVCC实现的。MVCC多版本并发控制MySql实现事务间的并发控制并不是简单的使用行级锁。MySql在读操作时不加锁,写操作时只加锁修改的资源。MVCC保存了数据资源在不同时间点的多个快照。根据事务的开始时间,每个事务看到的数据快照版本是不同的。InnoDB中的MVCC实现:存储引擎全局维护一个系统版本号,每开启一个新的事务,系统版本号就会递增。交易开始时的系统版本号将用作交易本身的版本号。在每一行记录中,存储引擎在每一行后面保存两个隐藏列,分别保存该行的起始版本号和过期版本号。在REPEATABLEREAD隔离级别下,MVCC的具体操作如下:INSERT存储引擎为每一个新插入的行保存当前系统版本号作为该行的起始版本号。UPDATE存储引擎会插入一行新记录,当前系统版本号为新记录行的起始版本号。同时将原线路的过期版本号设置为当前系统版本号。DELETE存储引擎将删除行的过期版本号设置为当前系统版本号。当SELECT读取一条记录时,存储引擎会选择满足以下两个条件的行作为读取结果。1、读取记录行的起始版本号必须早于当前事务的版本号。也就是说,这条记录在当前事务开始之前就已经存在了。事务开始后插入的行不会被事务看到。2、读取行的过期版本号必须晚于当前事务的版本号。也就是说,当前事务开始时,这条记录还没有过期。在事务开始之前已经过期的数据行将不会被事务看到。通过上面的描述,我们可以看出,在存储引擎中,一个数据行的多个版本是同时存储的。每个事务都会根据自己的版本号和每个数据行的起始和过期版本号,选择读取合适的数据行。MVCC只作用于READCOMMITTED和REPEATABLEREAD这两个层次。
