【MySQL】锁机制
前情提要什么是事件?
定义:事件是一组必须全部乐成或全部失败的数据库操纵(例如:转账需要同时完成扣款和收款)
ACID 特性:
[*]原子性(Atomicity):事件要么全部执行,要么全部回滚
[*]一致性(Consistency):事件执行后数据库必须处于正当状态(如账户余额不能为负数)
[*]隔离性(Isolation):事件之间互相隔离,通过锁机制实现
[*]长期性(Durability):事件提交后数据永世生存
为什么需要锁?
典型题目:两个用户同时购买最后一个商品,无锁会导致超卖
锁的作用:像“单线程”操纵关键数据,防止并发辩论
锁的分类
按锁的粒度分类
全局锁(Global Lock)
定义:锁定整个 MySQL 实例,所有数据库的写操纵会被壅闭。
使用场景:全库逻辑备份(如 mysqldump)。
语法:
FLUSH TABLES WITH READ LOCK;-- 加全局读锁
UNLOCK TABLES; -- 释放锁
注意事项:
[*] 在 MySQL 里并没有像全局读锁 FLUSH TABLES WITH READ LOCK 那样专门用于全局写操纵的全局写锁语句
[*] 全局锁会导致业务停摆,备份保举用 mysqldump --single-transaction(仅限 InnoDB)
表级锁(Table-Level Lock)
定义:锁定整张表,分为读锁(共享锁)和写锁(排他锁)
使用场景:
[*]MyISAM 引擎默认锁机制
[*]修改表结构(如 ALTER TABLE)
语法:
LOCK TABLES table_name READ; -- 加读锁
LOCK TABLES table_name WRITE;-- 加写锁
UNLOCK TABLES; -- 释放锁
代码示例:
-- 会话A
LOCK TABLES orders READ; -- 会话A给orders表加了读锁
-- 会话B
INSERT INTO orders VALUES (...); -- 会话B给orders表添加数据被阻塞
行级锁(Row-Level Lock)
定义:锁定单行或多行数据(InnoDB 核心特性)
使用场景:高并发下精确控制数据访问
语法:
SELECT * FROM users WHERE id=1 FOR UPDATE; -- 排他锁
SELECT * FROM users WHERE id=1 FOR SHARE;-- 共享锁
注意事项:
[*]行锁必须通过索引实现,否则退化为表锁
[*]示例:UPDATE users SET age=20 WHERE name='张三'(若 name 无索引,锁整个表)
页级锁(Page-Level Lock)
定义:锁定命据页(16KB),仅 BDB 引擎支持
近况:已被镌汰,了解即可
按锁的类型分类
共享锁(S Lock)
定义:答应多事件读同一数据,制止写操纵
类比:多人同时阅读同一本书,但不能修改
兼容性:与共享锁兼容,与排他锁辩论
排他锁(X Lock)
定义:独占锁,制止其他事件读写
类比:你独占了书桌写作业,其他人不能看也不能改
兼容性:与所有锁辩论
意向锁(Intention Lock)
定义:表级锁,快速判断表中是否有行锁
[*]IS 锁:事件计划加行级共享锁
[*]IX 锁:事件计划加行级排他锁
作用:制止逐行检查锁状态,提升效率
按锁的实现方式分类
悲观锁
定义:假设并发辩论会发生,先加锁再操纵
实现:SELECT ... FOR UPDATE
实用场景:写操纵频繁(如秒杀)
乐观锁
定义:假设无辩论,提交时检查版本号
实现:
UPDATE products
SET stock=stock-1, version=version+1
WHERE id=100 AND version=current_version;
实用场景:读多写少(如文章阅读量统计)
InnoDB 行级锁的四种实现
记录锁(Record Lock)
定义:锁定索引中的一条记录
触发条件:精确匹配唯一索引(如 WHERE id=1)
代码示例:
-- 锁定 id=1 的行
SELECT * FROM users WHERE id=1 FOR UPDATE;
间隙锁(Gap Lock)
定义:锁定索引记录之间的间隙(如 id 在 (5,10) 之间)
触发条件:范围查询或查询不存在的值
作用:防止其他事件插入数据(解决幻读)
仅生效于:REPEATABLE READ 隔离级别
临键锁(Next-Key Lock)
定义:记录锁 + 间隙锁,锁定左开右闭区间
触发条件:非唯一索引的范围查询
代码示例:
-- 锁定 age 在 (20,25] 区间
SELECT * FROM users WHERE age > 20 AND age <=25 FOR UPDATE;
插入意向锁(Insert Intention Lock)
定义:插入前标志目的位置的间隙锁
特点:不壅闭其他插入意向锁,但壅闭间隙锁
事件隔离级别与锁的关系
隔离级别脏读不可重复读幻读锁机制特点Read Uncommitted可能可能可能不加锁,直接读最新数据(可能脏读)Read Committed制止可能可能行锁,无间隙锁Repeatable Read制止制止可能默认使用临键锁,防止幻读Serializable制止制止制止所有 SELECT 主动转为 FOR SHARE 发起:默认使用 REPEATABLE READ,高并发写入场景可尝试 READ COMMITTED
锁的监控与死锁处理处罚
检察锁状态
代码示例:
-- 当前持有的锁
SELECT * FROM information_schema.INNODB_LOCKS;
-- 锁等待关系
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 事务状态(包含锁信息)
SELECT * FROM information_schema.INNODB_TRX;
死锁主动处理处罚
InnoDB 主动检测死锁,回滚代价较小的事件
手动处理处罚:
SHOW ENGINE INNODB STATUS;-- 查看死锁日志
KILL ; -- 终止事务
制止死锁的黄金法则
按固定顺序访问资源(如按主键排序更新)
只管使用短事件,快速提交
为高频查询字段添加索引
常见题目
为什么我的 SQL 执行卡住?
可能原因:锁等待超时
解决步骤:
[*] 查询壅闭事件:
SELECT * FROM information_schema.INNODB_TRX;
[*] 停止壅闭者:
KILL 12345; -- 替换为实际事务ID
操纵案例
索引缺失导致锁升级
[*] 创建无索引表:
CREATE TABLE test (name VARCHAR(20));
INSERT INTO test VALUES ('Alice'), ('Bob');
[*] 会话A:
BEGIN;
UPDATE test SET name='Charlie' WHERE name='Alice'; -- 无索引,触发表锁
[*] 会话B:
UPDATE test SET name='Dave' WHERE name='Bob'; -- 被阻塞!
结论:无索引的更新操纵会锁住整张表
总结
[*] 索引优化:
[*]所有 WHERE 条件字段只管加索引
[*]制止全表扫描(如 LIKE '%xxx%')
[*] 事件计划:
[*]事件只管简短,制止在事件中执行网络请求
[*]批量操纵分批次提交
[*] 锁超时设置:
SET innodb_lock_wait_timeout = 5; -- 等待5秒自动放弃
[*] 锁替代方案:
[*]乐观锁:通过版本号控制
[*]队列机制:异步处理处罚高并发请求
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]