今天来聊聊 MySQL 中的锁。作为一个 Python 开发工程师,掌握 MySQL 锁的基本概念和应用对我们优化数据库性能、避免死锁等问题非常重要。在 MySQL 中,根据加锁的范围和粒度,锁主要可以分为全局锁、表级锁、和行级锁。我们一个一个地来分析。
首先,了解一下全局锁。全局锁是 MySQL 中锁的粒度最大的一种,作用于整个数据库。当你执行 FLUSH TABLES WITH READ LOCK
命令时,整个数据库都会被锁定,处于只读状态。这意味着任何其他的操作,比如增删改、修改表结构等,都无法进行,必须等到这个锁释放后才能继续。
那么,这个全局锁具体用在哪里呢?最常见的场景就是做数据库备份。因为你想确保在备份过程中数据库中的数据不被修改或者发生结构性变化,避免备份出来的文件不一致。举个例子:
FLUSH TABLES WITH READ LOCK;
执行这个命令后,你就可以开始执行数据库备份任务了。在备份的过程中,所有对数据库的修改都会被阻塞。备份完成后,释放锁:
UNLOCK TABLES;
再来看表级锁,MySQL 提供了两种常用的表级锁。第一种是我们常见的表锁。当你需要对表进行操作时,可以使用 LOCK TABLES
来加锁,这个锁会锁住整个表,限制其他线程对该表进行任何操作,包括读和写。表锁是一种非常粗粒度的锁,它会影响到该表的所有操作。
例如:
LOCK TABLES your_table WRITE;
这样,其他任何线程都无法对 your_table
表进行读写操作,直到你释放锁为止:
UNLOCK TABLES;
除了表锁,还有一种特殊的锁叫做元数据锁(MDL)。当你对数据库表进行 CRUD(增删改查)操作时,MySQL 会自动给这张表加上 MDL 锁。MDL 锁有两种:读锁和写锁。当你进行数据操作时,会加上读锁,而进行结构修改(比如 ALTER TABLE)时,会加上写锁。
对于 MDL 锁,你并不会显式地加锁,而是数据库内部在执行这些操作时自动加锁。例如,执行 SELECT
查询时,MySQL 会给目标表加读锁,阻止其他线程修改该表的结构。同样,执行结构性修改时,MySQL 会加写锁,防止其他线程对表进行数据修改。
接下来,我们聊聊意向锁。意向锁是一个比较独特的锁,主要用于判断某个表中的行是否被加锁。意向锁分为意向共享锁(IS)和意向排他锁(IX)。
当你执行像 INSERT
、UPDATE
、DELETE
等操作时,系统会首先对表加一个意向锁,然后再对具体的记录加独占锁。这样做的目的是让数据库可以快速判断表中是否有行已经被锁定,减少锁冲突。
为什么要有意向锁?假设有多个线程同时对表中的数据进行操作,通过意向锁可以快速判断某个表的锁状态,避免对整个表加锁进行判断,从而提高效率。
接下来是行级锁。行级锁的粒度比表级锁要细得多,InnoDB 存储引擎支持行级锁,而 MyISAM 存储引擎则不支持行级锁。行级锁通常用于提高并发性,因为它只会锁定具体的某一行数据,而不是整个表。
行级锁分为两种:记录锁和间隙锁。我们首先说记录锁,它直接锁住某一条记录。记录锁分为共享锁(S 锁)和排他锁(X 锁),这两者具有互斥性,意思是一个线程可以对一行数据加共享锁,另一个线程可以对这行数据加排他锁,但是它们无法同时加锁。
例如,假设你有一张用户表,记录如下:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
INSERT INTO users (id, name) VALUES (1, 'Tom'), (2, 'Jerry');
如果你对某一条记录加了排他锁,其他线程就无法再对该记录进行任何修改:
SELECT * FROM users WHERE id = 1 FOR UPDATE;
这会锁住 id=1
的记录,直到当前事务结束。其他线程如果试图修改该记录,会被阻塞。
接下来是间隙锁。间隙锁锁定的是“空隙”,它只存在于可重复读(REPEATABLE READ)隔离级别下。间隙锁的目的是解决幻读问题。
当事务在执行查询时,其他事务不能在查询的结果集范围内插入新的数据。比如,假设你正在查询 id
在 10 到 20 之间的记录,间隙锁就会锁定 10 和 20 之间的“空隙”,防止其他事务在这个区间插入新记录。
最后,MySQL 还使用了 Next-Key Lock。Next-Key Lock 是记录锁和间隙锁的组合,它不仅锁定某一条记录,还锁定记录之间的空隙。Next-Key Lock 的作用是为了避免幻读,确保查询过程中数据的一致性。
如果你执行了类似以下的查询:
SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;
Next-Key Lock 会锁住 id 为 10 和 20 的记录,以及它们之间的空隙。这样,即使有另一个事务试图插入新的记录,锁也会避免这个操作,从而防止幻读的出现。
总的来说,MySQL 中的锁机制从全局锁到行级锁,提供了从粗粒度到细粒度的多种选择。通过合理的加锁策略,能有效地提高并发性能,避免死锁等问题。然而,使用不当可能会导致性能下降,因此理解不同锁类型的应用场景非常重要。
最后如果面试官问你:MySQL 中的锁有哪些,分别是什么作用?
你的回答:
MySQL 中的锁可以分为全局锁、表级锁、和行级锁三类。
全局锁:作用于整个数据库,常用于备份场景。通过执行
FLUSH TABLES WITH READ LOCK
来将数据库置为只读状态,阻止数据修改,直到锁被释放。表级锁:包括表锁和元数据锁。表锁通过
LOCK TABLES
语句加锁,阻止其他线程对表的操作;元数据锁(MDL)用于防止对表结构的修改与数据操作冲突。行级锁:只锁定特定的行数据。InnoDB 引擎支持行级锁,分为记录锁、间隙锁和 Next-Key Lock。记录锁锁定具体的记录,间隙锁防止在某个范围内插入数据,Next-Key Lock 是记录锁和间隙锁的组合。
合理使用这些锁能有效提高数据库的并发性,但也要注意避免死锁和性能问题。
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。