# 前言

前面聊到的是数据库自带的那些并发控制机制。最后我们来聊聊在数据库自带的事务、锁、多版本并发控制等的基础上,自己写的锁。

  • 乐观锁,又称乐观并发控制(Optimistic CC, OCC)
  • 悲观锁,又称悲观并发控制(Pessimistic CC, PCC)

乐观锁和悲观锁不是具体的并发控制方法,而是两种并发控制的思想,由这两种思想会产生基于这两种思想的很多种方法。

# 系列目录

# 悲观并发控制

悲观并发控制(又称悲观锁)是悲观地假设,事务间的读写冲突/写写冲突很多。因此,在并发控制设计的方法上,就会倾向于在冲突很多的场景下提升性能。

悲观并发控制的一种实现就是利用数据库锁,在数据库层对数据进行上锁,别的进程就不能读写上了锁的数据,非常简洁地保证了一致性。其缺点是并发度会降低,在写多读少的情形下为了保证一致性,降低并发度是必要的,但在读多写少的情形下性能损失就会很大了。且可能发生死锁。

一个简单的实现如下:

-- 加锁
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';

# 基于唯一索引的锁

并发控制方法 乐观并发控制(以版本号实现为例) 悲观并发控制(以锁为例)
读多写少的情形下 避免读时的加锁过程
避免加锁、解锁的过程 避免更新失败回滚的过程
其他 可能会反复回滚 可能会死锁