InnoDB 中的锁
开发多用户、数据库驱动的应用是,最大的一个难点就是:一方面要最大程度地利用数据库的并发访问,另一方面还要确保用户能以一致的方式读取和修改数据。为此,便有了锁机制。InnoDB 在 MySQL 存储引擎中脱颖而出,一部分原因就是它在这一方面技高一筹。
lock 与 latch
在数据库中,lock 与 latch 都被称为“锁”,但是它们两者拥有着两种截然不同的含义。
latch 一般称为“闩锁”,它是一种轻量级锁,因为其要求锁定的时间必须非常短。若持续时间长,则应用的性能会非常差。在 InnoDB 中, latch 可以分成 mutex(互斥量)和 rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检查机制。
lock 的对象是事务,用来锁定数据库中的事务对象,如表、页、行。并且 lock 的对象仅在事务 commit 或者 rollback 后进行释放(对于不同的事务隔离级别释放的时间可能不同)。此外,lock 是由死锁机制的。
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 提供了一种额外的锁方式,称为意向锁。意向锁锁的是多个层次。
如果对上锁对象看成一个树,那么对最下层的对象上锁,也就是对粒度最小的的对象上锁,首先要对上层对象上锁。举例来说,在对记录 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 存储引擎》