数据库锁

1 分类

(1) 乐观锁

通过版本号和时间戳检测和处理并发冲突

(2) 悲观锁

1) 表锁和行锁

1‘ 表锁

对表加锁,开销小,没有死锁问题,但并发弱

2’ 页锁

开销、粒度和并发介于表锁和行锁之间,会出现死锁问题

3‘ 行锁

对数据行加锁,并发强, 但开销大且有死锁问题

注意:

  • 更新锁,为了解决行锁升级(共享锁升级为排他锁)的死锁问题。
  • MyISAM只能使用表锁,Memory支持表锁,BerkeleyDB支持表锁和页面锁,而InnoDB可以使用表锁和行锁。行锁通过对索引上的索引项加锁完成,只能在通过索引条件查询的场景中使用,否则使用表锁。
  • 为了提升InnoDB表锁和行锁的效率,产生了意向锁(意向共享锁和意向排他锁)。
4‘ 行锁矩阵

行锁具有以下几种精确模式,用于控制锁粒度,减少冲突

  • 意向锁(Insert Intention Lock)

    InnoDB即支持表锁,又支持行锁。为了处理两种锁间的冲突,需要检测并避免。

    1 判断表是否被其他事务加锁

    2 遍历判断是否目标行被加锁 - > 效率低

    意向锁标记表中是否存在某一行被锁住,申请表锁或行锁前需要获取表的意向锁

  • 间隙锁(Gap Lock)

    只锁索引之间的间隙,只针对辅助索引,不包含唯一和主键。只阻塞插入

  • 记录锁(Record Lock)

    只锁记录

  • Next-Key锁(Ordinary Lock)

    同时锁记录和间隙

image-20201023232451157

四种模式的写入兼容性,+表示兼容。

列名代表已有的锁,行名表示尝试获取的锁

2) 共享锁和排他锁

表锁和行锁都分为共享锁和排他锁。

共享锁排斥其他写,排他锁排斥其他读写。

1‘ 共享锁

事务执行SELECT时尝试加锁。

逐行锁定,读取后立即释放。

允许其他共享锁和更新锁。

2’ 排他锁

事务执行INSERT、UPDATE或DELETE时尝试加锁

排斥其他锁。

3‘ 更新锁

事务执行UPDATE时尝试加锁,用于避免两个持有共享锁的事务同时更新数据导致的死锁。

与共享锁兼容,与其他共享锁或排他锁互斥。

数据库锁

2 数据库隔离级别

未提交读:不使用锁,可能出现脏读

提交读:使用排他锁,可能出现重复读取数据不一致

可重复度:使用共享锁和排他锁,可能出现幻读

串行化:使用事务,性能较差

3 避免死锁经验

  • 事务间按照相同的顺序访问数据,避免交叉访问引起的锁等待或死锁
  • 尽量细化事务,减少事务占用的资源
  • 一次性锁定所需的所有资源,避免死锁
  • 降低隔离级别,减少使用锁
  • 添加索引,为了使用更细粒度的行锁

参考资料

【数据库】数据库的锁机制及原理

MySQL数据库锁介绍

数据库两大神器【索引和锁】