欢迎光临护送网
详情描述

SELECT FOR UPDATE 是 MySQL 中用于行级锁的语句,主要作用是锁定查询到的行,防止其他事务修改这些行,确保数据的一致性。

基本语法

SELECT * FROM table_name WHERE condition FOR UPDATE;

使用场景

1. 悲观锁实现

在事务中先锁定数据,再进行更新操作:

START TRANSACTION;

-- 锁定符合条件的行
SELECT * FROM accounts WHERE user_id = 123 FOR UPDATE;

-- 执行更新操作(其他事务无法修改被锁定的行)
UPDATE accounts SET balance = balance - 100 WHERE user_id = 123;

COMMIT;

2. 防止库存超卖

START TRANSACTION;

-- 锁定库存记录
SELECT stock FROM products WHERE id = 1001 FOR UPDATE;

-- 检查库存并扣减
UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock > 0;

COMMIT;

重要特性

1. 事务中生效

FOR UPDATE 只在事务中有效:

-- 正确用法
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 其他操作
COMMIT;

-- 错误用法(非事务中无效)
SELECT * FROM users WHERE id = 1 FOR UPDATE;

2. 锁等待超时

默认会一直等待锁释放,可以设置超时时间:

-- 设置锁等待超时为5秒
SET innodb_lock_wait_timeout = 5;

START TRANSACTION;
SELECT * FROM orders FOR UPDATE;

3. 锁定范围

  • 有索引时:锁定符合条件的行(行级锁)
  • 无索引时:可能升级为表锁
  • 主键查询:锁定指定的主键行

实际应用示例

示例1:转账操作

START TRANSACTION;

-- 锁定两个账户
SELECT * FROM accounts WHERE id IN (1, 2) FOR UPDATE;

-- 执行转账
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;

COMMIT;

示例2:订单处理

START TRANSACTION;

-- 锁定用户和商品
SELECT * FROM users WHERE id = 100 FOR UPDATE;
SELECT * FROM products WHERE id = 5 FOR UPDATE;

-- 创建订单
INSERT INTO orders (user_id, product_id, quantity) 
VALUES (100, 5, 1);

COMMIT;

与其他锁的比较

锁类型 语法 作用
排他锁 FOR UPDATE 阻止其他事务读写
共享锁 LOCK IN SHARE MODE 允许其他事务读,阻止写
乐观锁 版本号/时间戳 通过版本控制实现

注意事项

1. 性能影响

  • 锁定的行越多,性能影响越大
  • 可能导致死锁
  • 增加锁等待时间

2. 避免死锁

-- 按相同顺序锁定表,避免死锁
-- 事务1和事务2都按 id 升序锁定
SELECT * FROM accounts WHERE id IN (1, 2, 3) ORDER BY id FOR UPDATE;

3. 锁升级

当锁定的行过多时,InnoDB 可能将行锁升级为表锁。

4. 隔离级别影响

不同隔离级别下 FOR UPDATE 的行为:

隔离级别 FOR UPDATE 行为
READ COMMITTED 锁定实际存在的行
REPEATABLE READ 锁定实际存在的行和间隙

最佳实践

尽量缩小锁定范围

-- 不好:可能锁定多行
SELECT * FROM orders WHERE status = 'pending' FOR UPDATE;

-- 更好:精确锁定 SELECT * FROM orders WHERE id = 1001 FOR UPDATE;


2. **尽快释放锁**
```sql
START TRANSACTION;
-- 业务操作尽量放在锁外
SET @amount = 500;

-- 锁定后立即完成更新
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - @amount WHERE id = 1;

COMMIT;  -- 立即提交释放锁

使用超时机制

SET innodb_lock_wait_timeout = 3;  -- 3秒超时

考虑替代方案

  • 乐观锁(版本控制)
  • Redis 分布式锁
  • 消息队列削峰填谷

常见错误

-- 错误1:非事务中使用
SELECT * FROM users FOR UPDATE;  -- 自动提交,锁立即释放

-- 错误2:锁定过多行导致性能问题
SELECT * FROM large_table WHERE create_time > '2023-01-01' FOR UPDATE;

-- 错误3:没有WHERE条件(锁定全表)
SELECT * FROM users FOR UPDATE;  -- 非常危险!

SELECT FOR UPDATE 是处理并发数据更新的重要工具,但需要谨慎使用,避免过度锁定影响系统性能。

相关帖子
海口市救护车长途跨省运送病人-设备齐全,价格公道
海口市救护车长途跨省运送病人-设备齐全,价格公道
海口市救护车出租公司-出租120救护车
海口市救护车出租公司-出租120救护车
如果一年内没有使用门诊报销,次年真的能提高支付比例吗?如何计算?
如果一年内没有使用门诊报销,次年真的能提高支付比例吗?如何计算?
不同身高体型的人,在寻找适合自己的正确坐姿时需要注意什么?
不同身高体型的人,在寻找适合自己的正确坐姿时需要注意什么?
如何查询某段高速在往年免费期间的拥堵历史,以辅助行程决策?
如何查询某段高速在往年免费期间的拥堵历史,以辅助行程决策?
为什么山火现场要尽量避开山谷、陡坡和反斜面,哪些地形更容易积聚浓烟?
为什么山火现场要尽量避开山谷、陡坡和反斜面,哪些地形更容易积聚浓烟?
异地就医备案选
异地就医备案选"长期"和选"临时"到底有什么区别,会影响结算方式吗?
为什么很多端午习俗集中在“五月五日前后”,这和农历历法推进方式有关吗?
为什么很多端午习俗集中在“五月五日前后”,这和农历历法推进方式有关吗?
门诊统筹的
门诊统筹的"年度限额"是按自然年清零还是按医保年度算,跨年前后去就诊会影响额度吗?
榆林市精准获客助手@网站运营服务,专业开发团队
榆林市精准获客助手@网站运营服务,专业开发团队
聊城市安卓app开发#企业网站建设公司,一站式服务
聊城市安卓app开发#企业网站建设公司,一站式服务
年终奖的数额和发放时间,在劳动合同中应该如何约定才能避免争议?
年终奖的数额和发放时间,在劳动合同中应该如何约定才能避免争议?
石家庄市高效获客软件@模版网站开发设计,定制建站
石家庄市高效获客软件@模版网站开发设计,定制建站
租客想用租房合同来填报个税专项附加扣除,会不会反而把房东的税务信息牵出来?
租客想用租房合同来填报个税专项附加扣除,会不会反而把房东的税务信息牵出来?
公约中提及的“饲养烈性犬、大型犬”的相关限制,具体标准是什么?
公约中提及的“饲养烈性犬、大型犬”的相关限制,具体标准是什么?
如何区分“高温天气”下的法定津贴与企业自愿提供的防暑降温福利?
如何区分“高温天气”下的法定津贴与企业自愿提供的防暑降温福利?
延长后的育儿假,其中的工资或补贴计算标准与发放渠道是怎样的?
延长后的育儿假,其中的工资或补贴计算标准与发放渠道是怎样的?
车位编号、测绘面积和合同面积对不上常见吗,普通业主能用什么办法初步核对?
车位编号、测绘面积和合同面积对不上常见吗,普通业主能用什么办法初步核对?
签订婚前协议后,如果家庭财务状况发生重大变化,协议可以修改吗?
签订婚前协议后,如果家庭财务状况发生重大变化,协议可以修改吗?