无论何时,只要有多个查询需要在同一个时刻修改数据时,就会有并发问题。MySql主要在
服务器层
与存储引擎层
进行并发控制。
假设数据库中国一张邮箱表,每个邮件都是一条记录。如果某个客户正在读取邮箱,同时其他客户试图在删除邮箱表中的某一条数据。这个时候,读取的结构就是不确定的了。在MySql中会通过锁定防止其它用户读取同一数据。大多数时候,MySQL锁的内部管理都是透明的。
MySQL锁的粒度
- 每种MySql引擎都可以实现自己的锁策略和锁粒度,将锁粒度固定在某个级别,可以为某些特定的场景提供更好的性能。
表锁(table lock)
- 表锁是mysql中最基本的锁略,并且是开销最小的策略。它会锁定整个表,一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的。
- 在特定的场景中,表锁也可能有良好的性能。例如,
READ L0CAL
表锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此-一个写锁请求可能会被插入到读
锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前
面)。 - 尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同
的目的。例如,服务器会为诸如ALTER TABLE
之类的语句使用表锁,而忽略存储引擎的
锁机制。
行级锁(row lock)
- 行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。
- 在
InnoDB
和XtraDB
,以及其他一些存储引擎中实现了行级锁。 - 行级锁只在存储引擎层实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。
MySQL的事务
事务特性
- A(原子性)事务的各步操作是不可分的,保证一系列的操作要么都完成,要么都不完成;
- C(一致性)事务完成,数据必须处于一致的状态;
- I(隔离性)对数据进行修改的所有并发事务彼此之间是相互隔离,这表明事务必须是独立的,不应以任何方式依赖或影响其他事务;
- D(持久性)表示事务对数据处理结束后,对数据更改必须持久化,不管是事务成功还是回滚。事务日志都能够保持事务的永久性。
事务的隔离级别
- SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )
- 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
- 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
- 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交的更改对其他事务是不可见的
- 串行化:对应一个记录会加读写锁,出现冲突的时候,后访问的事务必须等前一个事务执行完成才能继续执行。最高的隔离级别
MySQL中的事务
- MySQL提供了两种事务型的存储引擎:
InnoDB
和NDB Cluster
。另外还有一些第三方
存储引擎也支持事 - MySQL默认采用自动提交(AUTOCOMIT) 模式。如果不是显式地开始-一个个事务,则每个查询都被当作一事务执行提交操作。在当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交模式:
- InnoDB采用的是两阶段锁定协议(two-phase locking protocol)。在事务执行过程中,随
时都可以执行锁定,锁只有在执行COMMIT
或者ROLLBACK
的时候才会释放,并且所有的
锁是在同一时刻被释放。 - InnoDB也支持通过特定的语句进行显式锁定
SELECT ... LOCK IN SHARE MODE
和SELECT FOR UPDATE
些语句不属于SQL规范
多版本并发控制MVCC
MVCC
是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。- MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同张表,同一时刻看到的数据可能是不一样的。
InnoDB
的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。- MVCC只在可重复读和读提交的隔离级别生效。其它两个级别都不兼容
在可重复读(
REPEATABLE READ
) 隔离级别下,MVCC具体是如何操作的。
SELECT查询操作时
InnoDB会根据以下两个条件检查每行记录:
- InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
- 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
INSERT
- InnoDB为新播入的每-一行保存当前系统版本号作为行版本号。
DELETE
- InnoDB为删除的每-*行保存当前系统版本号作为行删除标识。
UPDATE
- InnoDB为插入-行新记录,保存当前系统版本号作为行版本号,同时保存当前系统
版本号到原来的行作为行删除标识。