锁的机制
目录
- 一、为什么需要锁?
- 二、全局锁
- 三、表级锁 [重点]
- 四、行级锁(InnoDB 独有)[重点]
- 五、总结
一、为什么需要锁?
锁的本质是解决并发冲突:
- 多个客户端同时读写同一份数据时,必须有规则保证数据一致性。
- 锁就是这套规则的实现,不同的锁对应不同的并发控制策略。
加锁范围分为全局锁、表级锁和行锁3类。
二、全局锁
-
作用:锁住整个数据库,让它变成只读。
-
命令:
- 加锁:
flush tables with read lock; - 解锁:
unlock tables;(或断开会话自动释放)
- 加锁:
-
缺点:所有写操作(insert/update/delete/alter)都会被阻塞,业务几乎停摆。
-
典型场景:全库逻辑备份(比如 mysqldump 不加参数时),防止备份过程中数据被修改,导致备份文件不一致。
-
替代方案:InnoDB 下用
mysqldump --single-transaction,利用 MVCC 做一致性快照,不阻塞业务。
三、表级锁
表级锁有四种,逐个分析:
1、表锁(lock tables)
-
共享读锁(read):
lock tables <table_name> read;- 自己能读,不能写;别人能读,不能写。
- 自己也不能访问其他表。
-
独占写锁(write):
lock tables <table_name> write;- 自己能读能写;别人啥都干不了。
-
释放表锁
unlock tables; -
使用场景:MyISAM 引擎备份、维护;InnoDB 尽量别用,因为粒度太粗,并发性能差。
2、元数据锁(MDL)
- 特点:自动加的,不需要手动调用。
- 读锁:CRUD 操作时自动加,防止表结构被改。
- 写锁:alter table 等 DDL 时自动加,防止别人读写数据。
- 在事务提交后释放MDL
- 坑点:长事务持有 MDL 读锁时,DDL 操作会被阻塞,进而阻塞后续所有查询。
- 解决办法:变更前先查长事务,必要时 kill 掉。
3、意向锁
- 作用:快速判断表中是否有记录被加了行锁,避免加表锁时全表扫描。
- 加行级共享锁前 → 先加「意向共享锁」
- 加行级独占锁前 → 先加「意向独占锁」
- 意向锁之间不冲突,只和显式表锁冲突。
4、AUTO-INC 锁(自增锁)
-
作用:保证自增主键(AUTO_INCREMENT)的值是连续递增的。
-
传统模式:插入语句执行完才释放锁,并发插入会被阻塞。
-
轻量模式(MySQL 5.1.22+):
- 通过
innodb_autoinc_lock_mode控制。 - mode = 0:采用auto_inc锁,sql执行结束后释放锁。
- mode=2:申请到自增值就释放锁,并发性能最好,但主从复制时 binlog 格式要设为
row,否则会数据不一致。
- 通过
四、行级锁(InnoDB 独有)
行级锁是 InnoDB 高并发的关键,粒度小,只锁需要的记录。
1、共享锁(S 锁)与独占锁(X 锁)
-
共享锁(S):读读兼容,读写互斥。
-- 对读取的记录加共享锁 select ... lock in share mode; -
独占锁(X):读写、写写都互斥。
-- 对读取的记录加独占锁 select ... for update; -
注意:必须在事务中使用,事务提交后锁才释放。
2、三种行锁类型
- Record Lock(记录锁) :锁单条记录,比如
where id = 1 for update。 - Gap Lock(间隙锁) :锁一个范围的记录(不包含记录本身),防止幻读。
- Next-Key Lock:Record Lock + Gap Lock 的组合,是 InnoDB 默认的行锁算法,在可重复读隔离级别下用来解决幻读。
五、总结
-
全局锁:全库只读,备份用,InnoDB 尽量不用。
-
表级锁:
- 显式锁:
lock tables,MyISAM 用,InnoDB 少用。 - MDL:自动加,防表结构和数据操作冲突,长事务是坑。
- 意向锁:行锁的「预告」,加速表锁判断。
- 自增锁:管自增,轻量模式性能好但要注意主从。
- 显式锁:
-
行级锁:InnoDB 高并发核心,S/X 锁 + 三种行锁类型,解决并发和幻读。