MYSQL(一)并发控制

并发控制

读写锁

在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种类型的锁通常被称为共享锁(shared lock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(write lock)。

读锁时共享的,是互相不阻塞的。多个客户在同一时刻可以同时读取一个资源,而互补干扰。

写锁时排他的,一个写锁会阻塞其他的写锁和读锁。

锁的粒度

一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。

表锁(table lock)

表锁时MYSQL中最基本的锁策略,并且时开销最小的策略。它会锁定整张表,一个用户在对表进行写操作(插入、删除、修改等)前,需要获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取用户才能获得读锁,读锁之间是互不阻塞的。

行级锁(row lock)

行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。

多版本并发控制(MVCC)

MYSQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。

可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此,开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC 的实现

MVCC是通过保存数据在某个时间点的快照来实现的。也就是锁,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表同一时刻看到的数据可能是不一样的。

不同存储引擎MVCC的实现是不同的,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。

InnoDB的MVCC

InnoDB的MVCC,是通过在每行记录侯面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存了行的过期时间(或者删除时间)。当然存储的并不是实际实际时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

在REPEATABLE READ 隔离级别下,MVCC的具体操作:

  • SELECT

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

    1. InnoDB只查找版本遭遇当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前也存在,要么是事务自身插入或者修改过的。

    2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

      只有符合上述两个条件的记录,才能返回作为查询结果。

  • INSERT

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

  • DELETE

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

  • UPDATE

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

    保存这两个额外的系统版本号,使大多数读操作都可以不用加锁。

MVCC 只在REPEATABLE READ 和READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁。