InnoDB 中的锁

  开发多用户、数据库驱动的应用是,最大的一个难点就是:一方面要最大程度地利用数据库的并发访问,另一方面还要确保用户能以一致的方式读取和修改数据。为此,便有了锁机制。InnoDB 在 MySQL 存储引擎中脱颖而出,一部分原因就是它在这一方面技高一筹。

lock 与 latch

   在数据库中,lock 与 latch 都被称为“锁”,但是它们两者拥有着两种截然不同的含义。

   latch 一般称为“闩锁”,它是一种轻量级锁,因为其要求锁定的时间必须非常短。若持续时间长,则应用的性能会非常差。在 InnoDB 中, latch 可以分成 mutex(互斥量)和 rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检查机制。

  lock 的对象是事务,用来锁定数据库中的事务对象,如表、页、行。并且 lock 的对象仅在事务 commit 或者 rollback 后进行释放(对于不同的事务隔离级别释放的时间可能不同)。此外,lock 是由死锁机制的。

lock 于 latch 的比较
lock latch
对象 事务 线程
保护 数据库内容 内存数据结构
持续时间 整个事务的过程 临界资源
模式 行锁、表锁、意向锁 读写锁、互斥锁
死锁 通过 waits-for-graph、time-out 等机制进行死锁检测 无死锁检测。仅通过应用程序的加锁顺序保证无死锁情况发生。
存在于 Lock Manager 的哈希表中 每个数据结构对象中

本主要关注的 lock,对 lock 于 latch 有了一个基本了解,接下来正式进入对 InnoDB 存储引擎中锁机制的学习。

InnoDB 锁的类型

  在InnoDB存储引擎中,拥有两种标准的行级锁:

  • 共享锁:S Lock ,允许事务读取一行数据。
  • 排他锁:X Lock,允许事务删除或者更新一条数据。

  并且锁于锁之间是存在锁兼容性问题的。所谓锁兼容性就是指:如果一个事务 T1 获得了行 r 的共享锁,那么事务 T2 可以立即获得行 r 的共享锁,应为读取并没有改变 行 r 的数据。但若是其他事务 T3 想要获得行 r 的排他锁,则必须等待事务 T1、T2 释放共享锁——这种情况成为锁的不兼容。

  此外,InnoDB 还支持多粒度锁定,允许事务在行级别上的锁于表级别上的锁同时存在。为支持在不粒度上进行加锁操作,InnoDB 提供了一种额外的锁方式,称为意向锁。意向锁锁的是多个层次。

alt

  如果对上锁对象看成一个树,那么对最下层的对象上锁,也就是对粒度最小的的对象上锁,首先要对上层对象上锁。举例来说,在对记录 r 加 X 锁之前,已经有事务对表1进行了 S 表锁,那么表1上已经存在 S 锁了,之后事务要对记录 r 在表1加上 IX,由于不兼容,所以该事务必须等待表锁操作的完成。

   InnoDB 支持意向锁设计得比较简练,其意向锁就是表锁。设计目的是为了在一个事务中揭示下一行将被请求得锁类型。支持两种意向锁。

1)意向共享锁:IS Lock,事务想要获得一张表中的某几行的共享锁

2)意向排他锁:IX Lock,事务想要获得一张表中的某几行的排他锁

除了全表扫,意向锁不会阻塞其他任何请求。

行级锁与表锁兼容性
IS IX S X
IS 兼容 兼容 兼容 不兼容
IX 兼容 兼容 不兼容 不兼容
S 兼容 不兼容 兼容 不兼容
X 不兼容 不兼容 不兼容 不兼容

《mysql 技术内幕之 innoDB 存储引擎》