全局锁 → 数据库级别
表级锁 → 表级别
行级锁 → 记录级别
├── 记录锁 (Record Lock)
├── 间隙锁 (Gap Lock)
└── 临键锁 (Next-Key Lock)
-- 加全局读锁
FLUSH TABLES WITH READ LOCK;
-- 释放锁
UNLOCK TABLES;
场景:全库逻辑备份,确保数据一致性
mysqldump --single-transaction-- 手动加表锁
LOCK TABLES table_name READ; -- 共享读锁
LOCK TABLES table_name WRITE; -- 排他写锁
-- 释放表锁
UNLOCK TABLES;
-- 元数据锁,自动加锁
-- 当执行以下操作时自动添加:
ALTER TABLE ... -- 等待所有事务提交
DROP TABLE ... -- 等待所有事务提交
TRUNCATE TABLE ... -- 等待所有事务提交
-- 意向锁是表级锁,用于表明事务打算在表中加行锁
意向共享锁 (IS Lock):事务打算给行加共享锁
意向排他锁 (IX Lock):事务打算给行加排他锁
-- 锁兼容矩阵
| X | IX | S | IS
-----|----|----|----|----
X | 冲突 | 冲突 | 冲突 | 冲突
IX | 冲突 | 兼容 | 冲突 | 兼容
S | 冲突 | 冲突 | 兼容 | 兼容
IS | 冲突 | 兼容 | 兼容 | 兼容
-- 锁定单条记录
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 只锁定id=1的记录
-- 加共享锁
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 锁定一个范围,但不包括记录本身
-- id列有索引:1, 3, 5, 7, 9
SELECT * FROM users WHERE id > 5 AND id < 9 FOR UPDATE;
-- 锁定范围:(5, 7) 和 (7, 9)
-- 防止其他事务插入id=6或id=8的记录
-- 记录锁 + 间隙锁
-- 默认的行锁实现方式
SELECT * FROM users WHERE id > 5 FOR UPDATE;
-- 锁定:
-- 记录锁:id=7, id=9, ...
-- 间隙锁:(5,7), (7,9), (9,+∞)
-- 特殊的间隙锁,表示准备插入
INSERT INTO users (id, name) VALUES (6, 'test');
-- 会在(5,7)间隙上加插入意向锁
-- 会话1
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 持有id=1的排他锁
-- 会话2(会被阻塞)
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 等待会话1释放锁
-- 创建测试表
CREATE TABLE test_gap (
id INT PRIMARY KEY,
val INT,
INDEX idx_val (val)
);
INSERT INTO test_gap VALUES (1,10), (3,20), (5,30), (7,40);
-- 会话1
START TRANSACTION;
SELECT * FROM test_gap WHERE val = 20 FOR UPDATE;
-- 锁定val=20的记录,并添加间隙锁(10,20)和(20,30)
-- 会话2(会被阻塞)
INSERT INTO test_gap VALUES (2, 15); -- 值15在(10,20)范围内
INSERT INTO test_gap VALUES (4, 25); -- 值25在(20,30)范围内
-- 会话1
START TRANSACTION;
SELECT * FROM test_gap WHERE id > 3 FOR UPDATE;
-- 锁定:记录锁id=5,7 + 间隙锁(3,5),(5,7),(7,+∞)
-- 会话2(会被阻塞)
INSERT INTO test_gap VALUES (4, 35); -- id=4在(3,5)范围内
INSERT INTO test_gap VALUES (6, 45); -- id=6在(5,7)范围内
INSERT INTO test_gap VALUES (8, 55); -- id=8在(7,+∞)范围内
-- 会话1
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 1;
-- 持有id=1的锁
-- 会话2
START TRANSACTION;
UPDATE users SET balance = balance - 100 WHERE id = 2;
-- 持有id=2的锁
-- 会话1(尝试获取会话2持有的锁)
UPDATE users SET balance = balance + 100 WHERE id = 2;
-- 等待会话2释放id=2的锁
-- 会话2(尝试获取会话1持有的锁)
UPDATE users SET balance = balance + 100 WHERE id = 1;
-- 等待会话1释放id=1的锁
-- 产生死锁!
-- 查看死锁日志
SHOW ENGINE INNODB STATUS;
-- 设置死锁超时时间
SET GLOBAL innodb_lock_wait_timeout = 50;
-- 自动死锁检测
SET GLOBAL innodb_deadlock_detect = ON;
-- 死锁避免策略:
-- 1. 按相同顺序访问表
-- 2. 使用更细粒度的事务
-- 3. 添加合适的索引
-- 4. 降低事务隔离级别
-- 查看InnoDB锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 查看当前会话的锁
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
-- 查看等待锁的线程
SHOW PROCESSLIST;
-- 查看锁等待统计
SELECT
waiting_trx_id,
waiting_pid,
blocking_trx_id,
blocking_pid
FROM sys.innodb_lock_waits;
-- 查看当前持有锁的SQL
SELECT
r.trx_id waiting_trx_id,
r.trx_mysql_thread_id waiting_thread,
r.trx_query waiting_query,
b.trx_id blocking_trx_id,
b.trx_mysql_thread_id blocking_thread,
b.trx_query blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
-- 不好的写法:全表扫描导致表锁
UPDATE users SET status = 1 WHERE name LIKE '%test%';
-- 好的写法:使用索引字段
UPDATE users SET status = 1 WHERE id IN (SELECT id FROM temp_ids);
-- 避免锁升级:分批更新
UPDATE large_table SET col = val WHERE id BETWEEN 1 AND 1000;
UPDATE large_table SET col = val WHERE id BETWEEN 1001 AND 2000;
# my.cnf配置建议
[mysqld]
# 锁等待超时时间(秒)
innodb_lock_wait_timeout = 50
# 死锁检测
innodb_deadlock_detect = ON
# 锁信息输出级别
innodb_status_output_locks = ON
# 事务隔离级别
transaction-isolation = READ-COMMITTED
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 锁机制 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 不加锁 |
| READ COMMITTED | 不可能 | 可能 | 可能 | 记录锁 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 | 记录锁+间隙锁 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 | 记录锁+间隙锁+表锁 |
MySQL的锁机制是多并发环境下保证数据一致性的核心。理解不同锁的特性、使用场景和相互之间的关系,对于:
设计高性能数据库系统 避免锁冲突和死锁 优化SQL查询性能 保证数据一致性至关重要。在实际应用中,应根据业务需求选择合适的锁策略,并配合适当的监控手段,确保数据库系统稳定高效运行。