MySQL-select ... for update语句详解
分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程https://www.captainai.net/troubleshooter
基本语法
SELECT ... FOR UPDATE [NOWAIT | SKIP LOCKED]核心作用
行级锁:对查询到的行加上排他锁(X锁)
阻止修改:其他事务无法修改这些行(UPDATE/DELETE),也无法加共享锁
阻塞读取:普通的
SELECT不受影响,但其他SELECT FOR UPDATE会阻塞
使用示例
-- 开启事务 START TRANSACTION; -- 锁定id=1的行 SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 基于锁定数据进行操作 UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 提交事务释放锁 COMMIT;关键特性
| 特性 | 说明 |
|---|---|
| 锁范围 | 根据WHERE条件锁定行,无索引时可能锁表 |
| 释放时机 | 事务提交或回滚时释放 |
| 隔离级别 | 通常在REPEATABLE-READ或READ-COMMITTED下有效 |
高级选项
-- NOWAIT:立即返回错误而不等待 SELECT * FROM products WHERE id = 1 FOR UPDATE NOWAIT; -- SKIP LOCKED:跳过已被锁定的行 SELECT * FROM queue WHERE status = 'pending' ORDER BY priority DESC FOR UPDATE SKIP LOCKED;典型应用场景
1.防止超卖
START TRANSACTION; SELECT stock FROM products WHERE id = 1 FOR UPDATE; IF stock > 0 THEN UPDATE products SET stock = stock - 1 WHERE id = 1; INSERT INTO orders (product_id) VALUES (1); END IF; COMMIT;2.读取后修改
-- 获取当前余额并更新 START TRANSACTION; SELECT balance FROM accounts WHERE user_id = 100 FOR UPDATE; -- 在应用层计算新余额 UPDATE accounts SET balance = 900 WHERE user_id = 100; COMMIT;3.队列消费
START TRANSACTION; SELECT * FROM task_queue WHERE status = 'todo' LIMIT 10 FOR UPDATE SKIP LOCKED; -- 处理任务 UPDATE task_queue SET status = 'processing' WHERE id IN (...); COMMIT;⚠️ 注意事项
需要事务:必须在
START TRANSACTION或关闭自动提交后使用性能影响:锁持有期间阻塞其他事务,避免长事务
死锁风险:多个事务互相等待对方释放锁
锁定升级:全表扫描时会锁全表,务必使用索引
释放时机:事务结束立即释放,不要依赖连接关闭
与其他锁对比
| 锁类型 | 语句 | 用途 |
|---|---|---|
| 共享锁 | SELECT ... LOCK IN SHARE MODE | 读数据,禁止修改 |
| 排他锁 | SELECT ... FOR UPDATE | 准备修改数据 |
最佳实践
-- ✅ 正确:使用索引 SELECT * FROM users WHERE id = 100 FOR UPDATE; -- ❌ 错误:可能锁全表 SELECT * FROM users WHERE name = '张三' FOR UPDATE; -- ✅ 推荐:尽量短的事务 START TRANSACTION; -- 快速操作 COMMIT;