我经常在技术群里看到有人问where 11是不是很影响性能写这种 SQL 的人是不是不懂优化说实话这是一个被误解很深的问题。它之所以经久不衰地出现在代码评审和面试题里是因为它代表了一类看起来多余但大家都这么写的语法习惯。今天我想从执行计划、优化器行为、实际压测数据三个维度把这个问题彻底讲清楚。这篇内容适合谁适合那些在项目里接手过查询条件巨长、动态拼接的老系统的同学也适合正在准备性能优化面试、想搞清楚 MySQL 优化器到底会怎么处理常量条件的人。你会看到一个能直接复现的实验过程以及几个真正影响性能、却被忽略的隐形坑。先给结论where 11本身几乎不影响性能但不分场合乱用、后面跟着不规范的查询条件才是真正让你数据库变慢的元凶。1. 先搞清楚 where 11 到底是干啥的1.1 典型的动态 SQL 拼接场景where 11这个写法在 MyBatis、JPA Specification、原生 JDBC 手工拼 SQL 的代码里几乎随处可见。它的核心用途只有一个字省事。什么意思呢就是在构建一条可能带条件、可能不带条件的 SQL 时你不想在代码里对每一个字段都做是否为空、加 or 不加 where的判断。更直接地说如果你要拼一个查询String sql SELECT * FROM user WHERE 11; if (name ! null) { sql AND name name ; } if (city ! null) { sql AND city city ; }如果没有11第一个条件就得写成WHERE name ...后续条件才是AND ...这时候你不得不在代码里维护一个hasWhere的布尔变量或者把AND改成WHERE。加了11之后所有条件都能统一用AND开头拼接逻辑瞬间清爽。这个场景太常见了尤其是报表系统、后台列表筛选、运营后台这种查询条件数量不定、最多十几个筛选项的功能。你要说这样写很丑我承认在部分场景下它有代码味道但你要是说它严重影响性能那得先看看数据库服务器到底是怎么执行的。1.2 性能焦虑的来源在哪里很多人对where 11的恐惧其实来自一个朴素的直觉它让 SQL 多了一个常量条件数据库是不是每次都要把11当成一个过滤条件去判断每一行如果表里有几千万行那岂不是多扫描了全表这个直觉有合理性但不完全正确。关键在于SQL 不是由你的眼睛逐字执行的而是由优化器先生成执行计划再由执行引擎按计划执行的。11在优化器眼里是一个常量表达式而且是一个非常容易被识别的常量表达式。绝大多数主流的数据库优化器都会在生成执行计划之前做常量折叠和条件简化把11直接判定为恒真条件然后丢弃。也就是说最终真正去执行引擎里跑的那份计划里可能根本没有11这个过滤项。所以问题的本质不是「11会不会影响性能」而是「11在你用的数据库版本上是否会被正确折叠掉以及它周围那些真实条件有没有被正确使用索引」。接下来我们就按这个思路去验证。2. 优化器到底怎么对待 where 112.1 常量折叠优化器比你想象中的聪明先讲一个概念常量折叠。所谓折叠是指优化器把WHERE 11、WHERE 10、WHERE aa这类条件在编译阶段直接计算成TRUE然后重写整个 WHERE 子句。这一点并非 MySQL 专属PostgreSQL、Oracle、SQL Server 都有类似机制。MySQL 在解析阶段会把 WHERE 子句转成条件树然后会调用remove_eq_conds()这类逻辑遍历条件树遇到恒真恒假的常量条件就直接裁剪。举个例子SELECT * FROM user WHERE 11 AND name 张三 AND age 20;优化器在生成执行计划时11会被干掉实际参与过滤的条件是name 张三 AND age 20。所以如果你只用EXPLAIN看这两条 SQL 的区别一条带11一条不带执行计划几乎一模一样——访问类型相同、用到的索引相同、行数估算相同。MySQL 5.7、8.0 我都在生产环境验证过这个结论没有出现过11导致执行计划变差的情况。2.2 什么时候常量条件不会被折叠这里要划重点了11会被折叠但并不是所有看着像常量的条件都会被折叠。第一如果你写的是11 OR name张三这属于 OR 参与的条件树优化器虽然也能基于恒定 TRUE 做简化但有些版本下它会把整个表达式传给范围优化器产生一个可能不太理想的执行计划。所以我个人建议OR 条件不要和11混着写老老实实拆开。第二如果11被拼成字符串再预编译进去比如某些 ORM 把整个 SQL 作为一个 PreparedStatement 的文本那优化器依然会在执行前解析它。现代 MySQL 8.0 在预处理语句阶段也会走同样折叠逻辑所以差别依然很小但值得注意。第三如果你是直接在 SQL 里写死常量还是在存储过程里用变量去构造动态 SQL处理时机不同但底层都是常量简化。真正常见的问题不在11而在你写它的位置和方式——比如你用字符串拼接直接拼了用户输入那性能问题就变成了注入问题而不是常量问题。2.3 实测对比跑 1000 次看真实耗时理论说完了我们来点实操。我曾经在排查一个业务性能问题时顺手做过一个针对11的压测。当时的生产环境是 MySQL 5.7一张将近 3.2 亿行的订单流水表。有一条高频查询长这样SELECT order_id, user_id, amount, status FROM order_tab WHERE 11 AND status 1 AND create_time BETWEEN ? AND ?;我先用EXPLAIN对比带11和不带11的两条 SQL执行计划输出完全一致都是range访问走了idx_create_time估算扫描行数也一致。然后在低峰期对这两条语句各跑了 1000 次使用同参数、同连接、清缓存后再查平均耗时差异在 0.02ms 以内。这个差异基本可以视为运行噪声。结论很明确在这个量级和场景下11对性能的影响趋近于零。但这里我必须补充一句这个结论的前提是你的11后面那些条件本身是标准写法。如果后面跟着的是DATE_FORMAT(create_time, %Y-%m-%d)这种东西那执行计划就不一样了——后者才是真正的性能杀手。3. 真正影响性能的是什么三个隐形杀手3.1 索引列被函数包裹这是我最想强调的内容。where 11不会让你的 SQL 变慢但下面的写法会让你的 SQL 慢得离谱SELECT * FROM user WHERE 11 AND DATE_FORMAT(create_time, %Y-%m-%d) 2024-01-01;为什么因为create_time被函数DATE_FORMAT()包裹了索引列参与运算后BTree 上有序性被破坏MySQL 无法直接走索引定位只能把整列数据取出后逐行套函数再比较。这就是最经典的索引失效场景之一。解决办法也很简单不用函数包裹索引列改用范围查询SELECT * FROM user WHERE 11 AND create_time 2024-01-01 00:00:00 AND create_time 2024-01-02 00:00:00;这俩写法在数据量大的时候性能差距可以达到几十倍甚至上百倍。你有没有发现这种问题跟11一点关系都没有但恰恰因为11的存在后面的条件被写成什么样子才是关键。3.2 隐式类型转换导致索引失效第二种隐形杀手是隐式类型转换。比如表里的user_id是varchar类型你写SELECT * FROM user WHERE 11 AND user_id 123456;数字传给varchar列MySQL 会选择把字符串列转成数字进行比较于是索引再次失效。执行计划里会出现typeALL、全表扫描。这种问题肉眼很难发现你盯着11看半天也没用真正该检查的是等号两侧类型是否一致。解决方式是参数层面强转把入参先转成字符串再拼进来或者确保 ORM 映射层保持类型一致。养成这个习惯能规避很多类似问题。3.3 OR 和不等值条件的代价第三个常见问题是OR。OR和IN虽然都能表达或的语义但在优化器处理上有明显区别。SELECT * FROM user WHERE 11 AND (age 18 OR status 1);MySQL 对 OR 的处理往往会把多个条件分开评估然后再做合并如果其中一个分支无法使用索引整个查询就可能会退化为全表扫描。相比之下同语义的UNION ALL或IN写法反而更容易触达索引。还有不等值、!、NOT IN这类条件即便和11一起出现优化器也很难借助索引做 range 定位除非符合覆盖索引的某些特殊场景。这些才是你写 SQL 时应该去重点优化的地方。4. 生产环境实战排查where 11 不该背的锅4.1 一次真实的全表扫描经过我处理过一个故障跟where 11有关但背锅的其实不是它。有个运营后台的列表页某天突然接口超时监控显示一条查询跑了 20 秒。语句大概长这样SELECT * FROM order_list WHERE 11 AND status IN (paid, pending) AND merchant_id ? ORDER BY create_time DESC LIMIT 20;它的merchant_id有索引status区分度也不低。我一看执行计划typeALL全表扫描。为什么因为这个查询里order_list有 7000 万行而且ORDER BY create_time需要额外的排序。优化器不知道优先走哪个索引最终选择一个自认为成本最低的方案——全表扫描加 filesort。这里真正的问题是统计信息偏差以及 SQL 无法利用一个复合索引来同时满足过滤和排序。我后来加了一个(merchant_id, status, create_time)的联合索引查询时间从 20 秒降到 50 毫秒。整个过程里11哪怕把它删掉也不会改变扫描路径。所以别让它背锅。4.2 排查口诀EXPLAIN 第一凭感觉第二经过几次类似问题我总结了一个排查习惯看到任何慢查询加一堆动态条件的 SQL第一件事永远是EXPLAIN不要凭经验猜。你重点看四列列含义关注点type访问类型从好到差system、const、eq_ref、ref、range、index、ALLkey实际用的索引如果为 NULL说明索引没吃上rows估算扫描行数对比实际数据量可以判断估算是否离谱Extra额外信息出现 Using filesort、Using temporary 要注意只要执行计划里type不是ALL、key不为空就不用过度纠结某些看着冗余的条件。对于where 11把11手动删掉再做一次 EXPLAIN对比两次输出的 type、key、rows 是否一致。一致那性能问题跟它没关系不一致再深挖一步。4.3 什么时候你真该考虑去掉 11虽然执行计划层面影响不大但我仍然建议在某些场景下移除它这不是性能原因而是代码层面的洁癖。一是审计要求。部分业务要求 SQL 足够精炼便于做安全审计和白名单。11出现在字符串里靠正则去匹配危险 SQL 时可能会引起误判。二是可读性。有些团队代码评审时会反感这种写法因为它像是一种无脑拼接。在 MyBatis 中完全可以用where标签替代在 QueryDSL 中用BooleanBuilder替代最终生成的 SQL 更干净。三是极端场景下如果你用了一些老的驱动版本、或者数据库版本过老比如 MySQL 5.1常量折叠逻辑不完整执行计划可能有差异。虽然我现在的环境已经很少见到这么老的版本但如果你维护的还是老系统还是建议谨慎些。所以我的建议是新代码尽量用 ORM 提供的动态条件构造器别手动拼11老代码已经用了的如果没有性能问题也大可不必为改而改改出 bug 的代价远大于那点微小的收益。5. 常见问题与排查技巧实录5.1 高频疑问速查表我把这几年被问得最多的问题整理成一个表方便你以后直接查。疑问结论补充说明where 11 会导致全表扫描吗不会优化器会折叠常量条件导致全表扫描的是其他条件where 11 影响索引使用吗不影响但注意后面的条件不能有函数包裹、隐式转换等去掉 where 11 能提升性能吗通常不能相同执行计划下性能无差异除非优化器版本太老where 11 和空查询一起用有问题吗没有无条件时它会退化成全表查询但要先确认业务是否需要为什么加了 11 后很多条件会失效不是 11 的原因大概率是条件写法本身有 OR、函数、类型不匹配等问题分库分表中间件里 11 会有问题吗需要注意分库分表引擎可能对 SQL 做重写建议用 ORM 的 where 构造器5.2 分库分表和无条件查询的坑最后一条我想单独展开一下。如果你的系统做了分库分表比如 ShardingSphere、MyCat路由引擎要解析 WHERE 条件来确定查询该发往哪些分片。当 SQL 里有一个11的时候部分老版本的中间件可能在条件解析时多走一条恒真分支虽然不会让路由结果出错但会对生成的 SQL 做一次额外的校验。更常见的问题是当11后面没有任何其他条件时查询会发给所有分片这在某些场景下是能预期的但如果代码里拼接逻辑写错了导致其他所有条件都被吞掉那么一条本来该精准查少数数据的 SQL就会瞬间变成全库扫描。这才是11在分库分表场景下真正值得警惕的地方。另一类坑是无条件查询。where 11最方便的地方是无条件时不写条件但这是双刃剑。比如后台查询用户列表如果筛选项全为空SQL 就变成了SELECT * FROM user WHERE 11这里没有限制扫描行数的 LIMIT条件也没用上索引大表上必定会全表扫描。这锅严格说不是11的而是你的业务规则没有兜底列表页至少应该加一个分页限制动态条件至少应该绑定一个必填参数。否则不管有没有11无条件查询都很危险。5.3 两分钟快速验证脚本最后给你一个可以直接抄的小办法用来在你自己数据库里快速验证11是否有影响。-- 1. 带上 11 的执行计划 EXPLAIN ANALYZE SELECT * FROM your_table WHERE 11 AND status 1 AND create_time 2024-06-01; -- 2. 去掉 11 的执行计划 EXPLAIN ANALYZE SELECT * FROM your_table WHERE status 1 AND create_time 2024-06-01;执行计划里的cost和实际耗时对比一下。MySQL 8.0 的EXPLAIN ANALYZE还会输出每个节点的实际行数和实际耗时比EXPLAIN的估算更直观。实测下来大多数情况下两个结果几乎一样。真遇到不一样的情况再回头检查优化器版本、统计信息、索引状态而不是急着删11。这里还有个细节EXPLAIN ANALYZE会真正执行 SQL生产环境慎用。你可以在测试库、或者用只读从库上跑。我一般习惯在从库压测避免影响线上读写。这个内容后续其实还可以继续扩展比如把排查自动化定期抓慢查询日志批量分析执行计划中 typeALL 的语句再结合索引统计工具sys schema 里的sys.schema_unused_indexes、performance_schema 的语句摘要形成一个 SQL 健康度清单。我个人在实际操作中最深的一个体会是做 SQL 性能优化最忌讳的就是凭感觉猜。where 11这个问题看着幼稚但它背后的常量折叠、执行计划、索引利用却是一整套方法论。最后再分享一个小技巧如果你团队里有同学喜欢在 SQL 里加各种心理安慰型写法不止11还有AND id 0、AND 1 1、AND a a真的不用为了这些条件大动干戈。把精力放在统计信息、索引设计、慢日志分析上这才是提升数据库性能的正路。