基于锁的并发控制,给竞争资源加锁,阻塞读、写操作来解决事务之间的竞争条件,最终保证事务的可串行化。
基础概念
table-level locking
表级锁,所有存储引擎都支持。指标 table_locks_waited
观察表锁。row-level locking
行级锁,InnoDB支持,行锁加在索引上,因此优化器不走索引,会导致锁全表。指标 row_lock_current_waits
观察行锁。S mode
share,共享锁X mode
exclude,互斥锁IS mode
意向共享锁IX mode
意向互斥锁,表级锁,执行 DDL 前快速判断是否允许锁表Optimistic Lock
乐观锁,假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。适用于并发度低的场景。MySQL 无乐观锁实现,可以通过版本号来实现。Pessimistic Lock
悲观锁,假设会发生并发冲突,屏蔽一切可能违反数据完整性的操作。MySQL 的锁都是悲观锁。
Two-Phase Locking
两阶段锁,2PL 和 2PC 是两回事。
InnoDB 实现中,行锁也不是在事务开启时就占用,而是执行到特定语句时才加上,等待事务结束时释放。因此建议在事务中,把最可能造成冲突的锁尽量往后放。
另外,start transaction
不是立刻开启事务,而是执行第一个sql时,事务才真正开启。
deadlock
死锁,并发系统中不同线程出现循环依赖,涉及的线程都在等待别的线程释放资源,导致这几个线程都进入无限等待的状态。
解除死锁有2种策略:
- 默认
innodb_lock_wait_timeout=50
,事务等待50秒,超时回滚; - 默认
innodb_deadlock_detect=on
,开启死锁检测,主动回滚其中一个死锁事务,其它事务得以继续执行。
两个策略都是默认开启,但策略二有性能问题,死锁检测O(n),极端情况下CPU利用率上升,但 TPS 反而下降。
命令 SHOW INNODB STATUS 查询引发死锁的SQL,事务已经获得的锁,正在等待什么锁,以及被回滚的事务等
next-key lock
RR 隔离级别下,使用范围条件查询,间隙锁 可以防止幻读。
SELECT * FROM test WHERE id < 3 FOR UPDATE;
这是当前读,间隙锁将锁住(2,5)这个间隙,增大死锁的概率,因此建议:
- 尽量使用相等条件查询;
- 使用 RC 隔离级别。