SQL 生产环境中导致锁等待 / 死锁 / 超时 / OOM 的 Top 15 写法错误

发布时间 - 2026-01-27 00:00:00    点击率:
SQL性能事故主因是15类高危写法,如WHERE未走索引(含隐式类型转换)、函数包裹字段、OFFSET深分页等,修复后QPS稳定、P99延迟降30%~90%。

绝大多数生产 SQL 性能事故,不是因为数据量突增或硬件故障,而是几行看似无害的写法在高并发下被放大成锁、死锁、超时甚至 OOM。下面这 15 类写法,在真实线上环境反复复现过,且修复后 QPS 稳定、P99 延迟下降 30%~90%。

WHERE 条件未走索引(含隐式类型转换)

MySQL / PostgreSQL 中,user_id = '123'(字段是 BIGINT)会触发全表扫描 + 隐式转换,导致该行锁升级为表级锁等待;SQL Server 的 WHERE name = 123nameVARCHAR)同样失效索引。这类语句在压测时可能不显眼,但上线后一并发就卡住。

  • 检查执行计划:MySQL 用 EXPLAIN FORMAT=JSON,确认 keyrows 字段是否合理
  • 禁止字符串和数字混用:id = '1' → 改为 id = 1
  • 函数包裹字段:如 WHERE DATE(created_at) =

    '2025-01-01'
    → 改为 WHERE created_at >= '2025-01-01' AND created_at

UPDATE / DELETE 无 LIMIT 或无主键条件

在 MySQL 中,UPDATE orders SET status = 'done' WHERE user_id = 123user_id 无索引,会锁全表;即使有索引,若匹配行数达数万,也会持锁时间过长,阻塞后续事务。PostgreSQL 虽无“锁表”概念,但大范围 UPDATE 会显著延长 tuple 锁持有时间,引发等待链。

  • 所有线上 UPDATE/DELETE 必须带 LIMIT(如分批处理)或确保 WHERE 含唯一/主键字段
  • 批量更新优先用 IN (id1, id2, ...) 替代模糊条件,单次不超过 500 行
  • 避免 WHERE status != 'done' 这类无法利用索引的谓词

SELECT ... FOR UPDATE / LOCK IN SHARE MODE 在非事务块中执行

Spring Boot 默认 @Transactional 传播行为是 REQUIRED,但若开发者在非事务方法里直接调用 JdbcTemplate.queryForObject("SELECT ... FOR UPDATE", ...),MySQL 会自动开启隐式事务,且不自动提交——锁持续到连接关闭或超时(默认 8 小时),极易堆积成锁等待雪崩。

  • 强制所有 FOR UPDATE 语句包裹在显式事务中,且事务粒度尽量短
  • 禁止在 MyBatis 的 @Select 注解中写 FOR UPDATE,改用 @Update + 显式事务控制
  • PostgreSQL 中对应的是 SELECT ... FOR UPDATE NOWAIT,必须加 NOWAIT 并捕获 SQLState 55P03 异常

大字段(TEXT / BLOB)参与 SELECT * 或 ORDER BY

MySQL 的 innodb_buffer_pool_size 默认只缓存索引和热数据页,一旦 SELECT * 返回含 content TEXT 的百万行,会迅速耗尽内存,触发大量磁盘 I/O,最终导致连接池打满、OOM Killer 杀进程。ORDER BY 时若用到了未索引的大字段,还会触发 Using filesort + 临时磁盘表(tmp_table_size 不够时)。

  • 永远用明确列名代替 *,尤其避开 TEXT/BLOB 字段
  • ORDER BY 只允许出现在已建索引的字段上;若必须按大字段排序,提前生成摘要字段(如 content_md5)并建索引
  • 应用层做分页时,禁用 OFFSET 大值(如 OFFSET 100000),改用游标分页(WHERE id > last_id LIMIT 50

真正难排查的不是慢 SQL,而是“看起来快、并发一上来就崩”的 SQL。比如一个 UPDATE 平均 20ms,但锁持有时间取决于扫描行数而非执行时间;又比如一个 SELECT FOR UPDATE 在单线程下秒返回,但 50 并发时锁等待队列指数增长——这些细节,往往藏在执行计划的 Extra 列和 INFORMATION_SCHEMA.INNODB_TRXTRX_ROWS_LOCKED 里。


# mysql  # js  # json  # ai  # 隐式类型转换  # 隐式转换  # red  # sql  # spring  # spring boot  # mybatis  # for  # select  # date  # format  # 字符串  #   # using  # 线程  # delete  # 类型转换  # 并发  # postgresql  # 分页  # 隐式  # 这类  # 线上  # 死锁  # 的是  # 主键  # 行数  # 大成  # 也会 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在阿里云高效完成企业建站全流程?  Laravel如何实现多对多模型关联?(Eloquent教程)  如何在不使用负向后查找的情况下匹配特定条件前的换行符  如何在阿里云购买域名并搭建网站?  Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】  武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?  深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?  如何在建站宝盒中设置产品搜索功能?  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  标题:Vue + Vuex 项目中正确使用 JWT 进行身份认证的实践指南  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  js代码实现下拉菜单【推荐】  JS实现鼠标移上去显示图片或微信二维码  西安专业网站制作公司有哪些,陕西省建行官方网站?  html文件怎么打开证书错误_https协议的html打开提示不安全【指南】  C++用Dijkstra(迪杰斯特拉)算法求最短路径  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】  Swift中switch语句区间和元组模式匹配  Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用  如何在局域网内绑定自建网站域名?  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  高防服务器:AI智能防御DDoS攻击与数据安全保障  Linux系统命令中tree命令详解  如何登录建站主机?访问步骤全解析  Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程  消息称 OpenAI 正研发的神秘硬件设备或为智能笔,富士康代工  如何撰写建站申请书?关键要点有哪些?  高端建站如何打造兼具美学与转化的品牌官网?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  如何在Tomcat中配置并部署网站项目?  JavaScript如何实现类型判断_typeof和instanceof有什么区别  在线制作视频的网站有哪些,电脑如何制作视频短片?  详解vue.js组件化开发实践  百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧  Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】  laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  ,在苏州找工作,上哪个网站比较好?  Laravel事件监听器怎么写_Laravel Event和Listener使用教程  Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】  高防服务器如何保障网站安全无虞?  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  如何在橙子建站中快速调整背景颜色?  如何在Ubuntu系统下快速搭建WordPress个人网站?  如何用已有域名快速搭建网站?  如何在云主机快速搭建网站站点?  Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】  Bootstrap整体框架之CSS12栅格系统