不会,select ... for update 只锁扫描到的行,但若 where 未走索引则会全表扫描并加行锁,等效于锁全表;存储过程需显式提交事务,避免锁滞留;get_lock() 不绑定事务且易残留,不可单独用于数据一致性;存储过程解释执行开销大,高并发下应简化逻辑、避免游标。

MySQL存储过程中用 SELECT ... FOR UPDATE 会锁住整张表吗?
不会,但容易误判——它只锁扫描到的行,前提是走对索引。如果 WHERE 条件没命中索引,InnoDB 会升级为表级锁(更准确说是聚簇索引全扫描+行锁,效果等同于锁全表)。
实操建议:
- 调用前先
EXPLAIN存储过程里那条SELECT ... FOR UPDATE,确认key列非NULL、rows值合理 - 避免在
FOR UPDATE查询中用函数包裹字段,比如WHERE DATE(create_time) = '2024-01-01'会让索引失效 - 如果业务允许,优先用主键或唯一索引做条件,比普通二级索引更稳,锁范围更确定
高并发下存储过程里加锁后卡死?大概率是事务没及时提交或回滚
存储过程默认不自动提交事务,一旦开了事务又没显式 COMMIT 或 ROLLBACK,锁就一直挂着,后续请求全堵在 FOR UPDATE 上,表现为“突然变慢”或“超时”。
实操建议:
- 在存储过程开头加
DECLARE EXIT HANDLER FOR SQLEXCEPTION,确保异常时能ROLLBACK - 所有正常路径结尾必须有
COMMIT;若过程只读,干脆别开事务(去掉START TRANSACTION) - 压测时观察
SHOW ENGINE INNODB STATUS\G里的TRANSACTIONS部分,看有没有长时间未提交的事务
用 GET_LOCK() 做应用层互斥,为什么压测时还是出现重复写入?
GET_LOCK() 是会话级函数,锁名相同才互斥,但它不和事务绑定——如果获取锁后执行 SQL 出错没写入,但锁没释放,其他会话等超时失败;更糟的是,如果获取锁后忘记 RELEASE_LOCK(),锁会随连接关闭才释放,而连接池可能复用连接,导致锁残留。
实操建议:
- 绝不单独依赖
GET_LOCK()做数据一致性控制,它只适合短时、低频的轻量协调(比如定时任务防重跑) - 若真要用,必须配超时:
GET_LOCK('my_key', 3),且检查返回值是否为 1 - 写入操作仍需底层行锁兜底,
GET_LOCK()只是多一道提示性屏障
压测发现 QPS 上不去,瓶颈不在 SQL 而在存储过程本身
MySQL 存储过程是解释执行的,每次调用都要解析、校验、生成执行计划(即使没用到动态 SQL),高并发下调用开销明显。尤其当过程里嵌套循环、多次 SELECT、或用了游标,性能衰减会非常陡峭。
实操建议:
- 把能合并的查询尽量写成单条带
JOIN的语句,避免在过程里反复查同一张表 - 游标慎用:每 fetch 一行都是一次引擎交互,万级数据基本不可行;改用临时表 + 批量更新
- 压测时对比「纯 SQL」和「封装成存储过程」的响应时间,如果过程版本慢 2 倍以上,说明逻辑复杂度已超出其适用边界
真正难处理的不是锁怎么加,而是锁住之后那几毫秒里,你写的逻辑有没有隐式放大延迟——比如一次远程 HTTP 调用、一次没索引的子查询、或者一个没设超时的 SLEEP()。











