MySQL 悲观锁
前提
- MySQL存储引擎:InnoDB
概述
名词解释:
- 本系统当前的其他事务:每次数据库连接 CONNECTION(物理上) 会产生一次会话 SESSION(逻辑上) ; 每个会话 SESSION 中可产生多个 事务 TRANSACTION ,而一个 事务 TRANSACTION 只能在一个 会话 SESSION 中;
- 本系统当前的其他事务,指的是当前会话中的其他事务或者其他会话中的事务
实现:
- 基于MySQL提供的锁机制,保证数据访问的排他性
- 悲观锁本质是排它锁,允许获得排它锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁
- 通过常用的
select … for update
排它锁来实现悲观锁。当数据库执行select … for update
时会获取被select中的数据行的行锁,因此其他并发执行的select … for update
如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select … for update
获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用
使用条件:
问题解释
- 为什么要支持事务?因为事务是一个连续的一组数据库操作,它是一个单一的工作单元进行;事务是访问并更新数据库中各种数据项的一个程序执行单元;举例:A转账给B,A账户扣款,B账户增款,是一个连续的数据库操作
- 为什么要关闭自动提交,即设置 autocommit = 0 ?因为MySQL默认使用autocommit模式,默认是允许自动提交,即当执行一个更新操作后,MySQL会立刻将结果进行提交;而悲观锁需要将被修改的数据锁定,如果是一组操作,要等到这一组操作都完成后再整体提交,如果失败则整体回滚到初始状态,而不是每一步操作提交一次;举例:A转款B,A扣款成功,但B收款失败,所以A扣款操作应 rollback 回滚,而不是 A扣款这一个子操作步骤成功
应用描述
对于仅有一个数据操作的事务
- 设置 autocommit = 0 后,对数据更新操作后,需要手动 commit
- 不需改动,保持 autocommit = 1 即允许自动提交即可
对于连续的数据操作的事务
- 更新操作前设置
set autocommit = 0
- 开启事务
begin / begin work / start transaction
- 一组连续的数据操作,
select * for update || select * lock in share mode
;使用共享锁或排它锁,在读取此行数据时不允许其他事务进行修改 - 手动提交
commit / rollback
- InnoDB 存储引擎,事务的隔离级别为可重复读,配合MVCC 出现一致性非阻塞,即读操作没有添加共享锁,读取的是本次事务开始时的数据快照
- 事务提交完毕后,更新设置 autocommit = 1 ,事务的隔离级别为可重复读时每次事务开启时更新ReadView,保证读取到其他事务对数据进行的修改可见
悲观锁应用排它锁时行锁与表锁的区别
场景:
- 因为悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响了程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是对长事务而言,这样的开销往往无法承受