# 前言
前面聊到的是数据库自带的那些并发控制机制。最后我们来聊聊在数据库自带的事务、锁、多版本并发控制等的基础上,自己写的锁。
- 乐观锁,又称乐观并发控制(Optimistic CC, OCC)
- 悲观锁,又称悲观并发控制(Pessimistic CC, PCC)
- 多版本并发控制(Multi Version CC, MVCC)
乐观锁和悲观锁不是具体的并发控制方法,而是两种并发控制的思想,由这两种思想会产生基于这两种思想的很多种方法。
# 系列目录
# 悲观并发控制
悲观并发控制(又称悲观锁)是悲观地假设,事务间的读写冲突/写写冲突很多。因此,在并发控制设计的方法上,就会倾向于在冲突很多的场景下提升性能。
悲观并发控制的一种实现就是利用数据库锁,在数据库层对数据进行上锁,别的进程就不能读写上了锁的数据,非常简洁地保证了一致性。其缺点是并发度会降低,在写多读少的情形下为了保证一致性,降低并发度是必要的,但在读多写少的情形下性能损失就会很大了。且可能发生死锁。
一个简单的实现如下:
-- 加锁
SELECT * FROM methodLock WHERE method_name = xxx IN SHARE MODE;
SELECT * FROM methodLock WHERE method_name = xxx FOR UPDATE;
-- 提交事务,自动解锁
COMMIT;
# 乐观并发控制
乐观并发控制(又称乐观锁)是乐观地假设,事务间的竞争没有那么多。乐观并发控制多用于读多写少的场景,由于没有上锁解锁(此处指数据库锁)的过程,读的性能会很高;但在读少写多的场景,由于需要反复回滚重做,所以效率会变低。
乐观并发控制的一种实现是基于版本号。为每一个表加上一个 version
字段,在读出一条记录时会一并读出 version
。修改完数据后、提交事务时,需要检查一下 version
值是否变化:如果 version
值没变,说明事务开始后没有别的事务修改了该数据并进行提交,本次提交成功;如果有就放弃本次修改并回滚,准备重做。
-- 定义数据表时需要增加一行 version
SELECT * FROM mothodLock WHERE method_name = xxx;
-- 更新时比对 version 是否正确
-- 如果更新了 0 行,表示更新错误,需要回滚重做
UPDATE methodLock
SET val = 'xxx', version = version + 1
WHERE method_name = xxx AND version = 'xxx';
# 乐观锁、悲观锁和 MySQL MVCC 的关系
MySQL MVCC 可以说是 MySQL 内核实现层面上的概念。MySQL 利用 MVCC 和锁实现了数据库层面上的 Read Committed、Repeatable Read 和 Serializable 事务隔离级别。
我们在使用乐观锁和悲观锁,一般指的是在业务代码里使用乐观锁和悲观锁,是在业务层面上的概念。如果使用悲观锁的话,就需要在项目代码中启动 MySQL 事务。使用乐观锁的话,就不需要借助 MySQL 事务,直接进行读写操作就行。当然我们也可以实现一个 MVCC,但是显然没有这个必要哈哈哈哈。
# 并发控制方法对比
并发控制方法 | 乐观并发控制(以版本号实现为例) | 悲观并发控制(以锁为例) |
---|---|---|
读多写少的情形下 | 避免读时的加锁过程 | |
避免加锁、解锁的过程 | 避免更新失败回滚的过程 | |
其他 | 可能会反复回滚 | 可能会死锁 |