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如何处理和验证JSON类型的数据库字段  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案  魔方云NAT建站如何实现端口转发?  Laravel怎么清理缓存_Laravel optimize clear命令详解  魔毅自助建站系统:模板定制与SEO优化一键生成指南  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  如何确保FTP站点访问权限与数据传输安全?  打开php文件提示内存不足_怎么调整php内存限制【解决方案】  WEB开发之注册页面验证码倒计时代码的实现  昵图网官网入口 昵图网素材平台官方入口  如何在新浪SAE免费搭建个人博客?  大型企业网站制作流程,做网站需要注册公司吗?  如何在IIS7上新建站点并设置安全权限?  Android滚轮选择时间控件使用详解  Laravel如何使用Sanctum进行API认证?(SPA实战)  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  零基础网站服务器架设实战:轻量应用与域名解析配置指南  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】  高防服务器租用首荐平台,企业级优惠套餐快速部署  焦点电影公司作品,电影焦点结局是什么?  Mybatis 中的insertOrUpdate操作  通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】  Laravel Fortify是什么,和Jetstream有什么关系  如何快速使用云服务器搭建个人网站?  制作公司内部网站有哪些,内网如何建网站?  Laravel中的withCount方法怎么高效统计关联模型数量  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  高防服务器如何保障网站安全无虞?  活动邀请函制作网站有哪些,活动邀请函文案?  如何快速配置高效服务器建站软件?  如何在局域网内绑定自建网站域名?  php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  如何在建站之星绑定自定义域名?  java中使用zxing批量生成二维码立牌  Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置  Laravel如何配置和使用缓存?(Redis代码示例)  Internet Explorer官网直接进入 IE浏览器在线体验版网址  大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?  如何彻底删除建站之星生成的Banner?  如何快速建站并高效导出源代码?  大连网站制作公司哪家好一点,大连买房网站哪个好?  ,交易猫的商品怎么发布到网站上去?  IOS倒计时设置UIButton标题title的抖动问题  ,怎么在广州志愿者网站注册?  Swift中swift中的switch 语句  详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)