三、MySQL并发控制

当有多个连接对MySQL表中数据进行并发读写时,就会产生并发问题。为了避免并发问题,MySQL中引入了相关的锁。

1、MySQL的锁机制

1.1、读写锁

当多个客户端同时读取表中的数据时,不会产生并发问题。但是当有客户端在写入数据时,其他客户端来读取数据就会产生并发问题。为了提高并发程度,MySQL中使用了读写锁的机制。

读锁(共享锁):读锁是共享的,读锁之间是相互不阻塞的。多个客户端在同一时刻可以共同读取同一资源。

写锁(排它锁):写锁是互斥的,一个写锁会阻塞其他的写锁和读锁。

一个用户加了写锁之后,其他用户不能写入,也不能读取;一个用户加了读锁后,其他用户可以读取,但是不能写入。

1.2、锁粒度

通常来说,锁的粒度越大,锁住的资源越多,并发程度越低,但是越安全。MySQL中每个存储引擎都各自实现了自己的锁策略,其中InnoDB的锁机制包括表锁和行锁。

表锁:锁定整张表,是最基本的锁策略。MySQL的存储引擎层和服务器层都实现了表锁。

行锁:锁定某一行,可以最大程度的支持并发。行锁只在存储引擎中实现,而服务器层没有实现。

2、MySQL多版本并发控制(MVCC)

InnoDB中,为了提高并发程度,除了实现表锁之外,还提供了多版本并发控制(Multi Version Concurrency Control, MVCC)。MVCC可以理解为是一种特殊的行锁,且类似于乐观锁。在很多情况下能够避免加锁操作,实现非阻塞的读,而写入时也只需要对指定的某一行进行加锁。

MVCC是通过保存数据在某个时间点的快照来实现的。因此,在同一个事务中,能够保证多次读取的数据是一致的,不会出现不可重复读的问题,实现了可重复读的隔离级别,这也是MySQL(InnoDB)中事务默认的隔离级别。

对于MVCC,不同存储引擎可以有不同的实现。InnoDB实现MVCC的基本原理如下:在每行数据的后面,保存两个隐藏列,这两列中分别存储了这一行的创建时间、过期时间(删除时间)。这两列存储的并非是实际的时间的值,而是存储的一个系统版本号。也就是说,InnoDB为每行数据保存了两个版本号:创建版本号、删除版本号

每开始一个事务,系统版本号都会自动递增。同时,事务开始时刻的系统版本号会作为这个事务的版本号,事务版本号用于查询时和系统版本号进行比较。

InnoDB事务默认的隔离级别是可重复读,在这个隔离级别下,InnoDB在增删改查时基本原理如下:

查询(select)

创建版本号:只会读取创建版本号小于或等于当前事务版本号的数据行。这样能够保证事务中读取到的数据要么是在事务开始前就已经修改过的,要么就是在本事务中被修改的。而事务开始后,被其他事务修改后的数据是不会被读取到的,因为它们的创建版本号一定是大于当前事务版本号的。删除版本号:只能读取到删除版本号是无效值,或者删除版本号大于当前事务版本号的记录行。否则,说明该行在本事务之前已经被删除。

插入(insert):将当前的系统版本号保存到行的创建版本号中。

删除(delete):将当前的系统版本号保存到行的删除版本号中,作为删除标识。

更新(update):新插入一条记录,并将当前的系统版本号保存到新行的创建版本号中;同时,将当前的系统版本号保存到原来的行的删除版本号中,作为旧行的删除标识。

得益于额外保存的这两个版本号,InnoDB中对于大多数操作都可以在不加锁的情况下进行。并且能够保证读取的数据都是正确的。缺点就是InnoDB需要为这两列做一些额外的维护操作。

InnoDB中,MVCC只能在读已提交和可重复读这两个隔离级别中起作用,而与其他两个隔离级别不兼容。因为读未提交永远都是读取最新版本号的行,不会用到这两个版本号;而串行化则会为所有读取的行都进行加锁,也没有必要使用这两个版本号。

你可能感兴趣的:(数据库)