☰
37%准则:最优停止理论如何指导SQL优化与性能调优决策
2026/9/26 14:23:45 网站建设 项目流程

"37%准则"这个词我第一次真正体会到分量,是在处理一个棘手的慢SQL优化任务时。当时面对一张千万级订单表,索引加了好几个,SQL改写三版,性能还是卡在2秒以上。组里一位老前辈路过看了眼说:"你调研的样本量够了,别等了,动手改吧。"后来我才知道,他说的"够了"背后藏着一个数学模型——最优停止理论里的37%准则。这个准则讲的是:如果你要在N个候选里选一个最好的,又必须按照顺序逐个考察、且无法回头重选,那么最优策略是——先观察前37%的候选,只看不选,只记住其中最好的是什么水平;从第37%之后开始,一旦遇到任何一个超过这个"参考基准"的候选,立刻就地选定,不再犹豫。这样操作,你选到全局最优的概率最大,而这个最大概率也恰好是37%。这篇文章我想把它掰开揉碎,讲清楚它为什么成立,以及真正把它用进SQL优化、性能调优、产品选型和团队决策时,它到底能帮我们省下多少时间。

1. 为什么优化工作里最该先学的,是"怎么停止优化"

先聊一个听起来反直觉的问题:大部分优化工作越做越久,真不是能力问题,而是"没有决策截止点"。

我自己早期做优化也有这个毛病。拿到一个性能问题,先看执行计划,再翻慢查询日志,接着去查索引统计信息,然后改配置参数,改完测一轮,不理想又回到第一步……循环往复。最夸张的一次,我优化一个接口花了整整三天,最后收益和第一天加索引的收益几乎一样。那一刻我意识到:在优化这个动作里,真正让人消耗殆尽的不是技术难度,而是"什么时候停止探索、开始行动"这件事没有标准答案。

37%准则就是给这个"停止点"一个数学答案。

1.1 秘书问题:一个被低估的决策模型

37%准则来自一个经典数学模型,叫"秘书问题"(Secretary Problem),也叫最优停止问题。它描述的场景是这样的:假设你是招聘经理,有N个候选人按随机顺序依次来面试,你要当场决定录不录用。规则有两条——第一,每个候选人面试完必须立即做决定,不可以等看完后面的人再回头选前面的;第二,你的目标不是"找个差不多能干的",而是"必须选到所有人里最优的那个"。

太早下定决心,大概率错过后面更好的人;等得太晚,真正的人才又被前面的人挤掉了。N越大,凭感觉决策越不靠谱。

数学家给出的结论很干净:最优策略是先拒绝前N/e个候选人(e是自然对数底数,约2.718),数量上就是前37%左右。这37%的人纯粹用来建立"什么水平算好"的判断基准,谁都不选。之后从第37%加一个开始,一旦遇到比基准里最好的那人更好的,立刻录用。这个策略能把选到最优候选人的概率提升到约37%,相比之下,随缘选或者"看一半再选"的成功率都低得多。

我第一眼看到这个结论时觉得很神奇:一个如此简单的规则,居然是从几百个N值下通用推出来的数学结果。更让我惊讶的是,它不只在招聘里有用,凡是要从一堆顺序出现的选项里挑一个、而且没法回头的场景,几乎都能套用。

1.2 37%为什么是37%:直觉与推导

先给不习惯数学的读者一个直观版本。你决策时面临一个“探索—利用”的拉扯:探索太少,你根本不知道好选项长什么样,后面遇到好机会也认不出来;探索太多,你又被规则限制"过了这村没这店",眼睁睁看着好机会从手边溜走。37%恰好是探索和利用之间的平衡点。

往深一点说,这个结论背后用到的是概率的连续化近似。假设N个候选者的水平做一个排序,最好的排第1名。你选择的时刻发生在第k个候选人之后,那么你成功的概率大致可以写成:

$$P(\text{成功}) \approx \frac{k}{N}\int_{k/N}^{1}\frac{1}{x},dx$$

这里积分算完是 $-\frac{k}{N}\ln\frac{k}{N}$。你只要对这个函数求最大值,就能发现它取最大值时的 $k/N$ 正好等于 $1/e$,也就是约0.3679。所以"前37%观察、后63%行动"是最优的。

为了让自己心服口服,我写过一个小脚本做过蒙特卡洛模拟,在不同阈值比例下分别跑了10万次,结果和理论高度吻合:

import random import math def simulate(n, stop_ratio): k = max(1, int(n * stop_ratio)) best_so_far = -float("inf") # 观察阶段:只看不选 for i in range(k): best_so_far = max(best_so_far, random.random()) # 行动阶段:超过基准就选 for i in range(k, n): val = random.random() if val > best_so_far: return val == max_val return False def run_experiments(n, stop_ratio, trials=100000): max_val = None # 这里略去预生成最大值,仅示意结构 win = 0 for _ in range(trials): arr = [random.random() for _ in range(n)] max_val = max(arr) k = max(1, int(n * stop_ratio)) best_so_far = max(arr[:k]) picked = False for val in arr[k:]: if val > best_so_far: picked = val == max_val break if picked: win += 1 return win / trials for ratio in [0.2, 0.367, 0.5, 0.8]: print(ratio, run_experiments(100, ratio))

跑出来的结果很有意思:0.37附近成功率最高,而且30%到40%这个区间差距不大,都在35%上下。这给我一个额外启发——你不需要把37%卡得那么精确,落在一个合理区间就行。真正致命的错误是只观察10%就急着行动,或者观察到60%还不做决定。

2. 把37%准则翻译成工程师能用的语言

聊完数学,我想回到大家更熟悉的领域:技术优化。不管是SQL优化、服务端性能调优,还是移动端卡顿治理,本质上都是"从一堆可选操作里,按顺序尝试,找到最有效的那一个"。但是,多数人做的事和37%准则背道而驰。

最常见的错误是什么?是把所有能想到的优化手段全做一遍再说。明天先加索引,后天改分页,大后天调缓存,最后发现前面两步白干了,回滚还费半天劲儿。我见过有人为了优化一个接口,一遍遍开着慢查询日志、profile、performance_schema,收集了满满十几页数据,但问题核心早就在前几页里暴露了。收集数据这件事,做着做着就容易变成拖延的理由。

2.1 优化流程里的"观察期"和"行动期"

用37%准则重新设计一次性能优化的流程,可以拆成两个阶段。

先说观察期。假设你面对一个未知系统,列出的潜在优化点有N个(比如索引设计、SQL改写、连接池参数、缓存引入、数据归档、硬件配置……),你不可能在动手前把每个点的收益都精确预判出来。合理的做法是:先快速尝试前37%的措施——不是"做完并长期观测",而是"做个最小验证,量化出效果趋势"。这段观察期要忍住优化冲动,即使某个手段看起来收益不错,也不要立刻深挖。因为你的任务是建立基准,而不是锁定方案。

举例说,慢SQL排查里你通过explain和慢日志锁定5类候选问题:缺索引、join过多、select了不需要的列、order by导致filesort、隐式类型转换。5的37%约等于2次,也就是前两个问题你只管看方案、估算回表和扫描行数,不动手。走完这两步,你心里对"这表什么量级、什么访问模式、瓶颈在哪"已经有了清晰轮廓。从第3个措施开始,一旦它明显优于前面观察到的基线,立刻全力投入。

我在实际操作中的体会是,这套流程的价值不在于精确的百分比,而在于它强制你先把"观察"和"行动"分离开。没有这个分离,人的本能是哪个方法听着省事就先试哪个,最后优化变成堆砌。

2.2 实战案例:慢SQL优化中的37%决策

拿一个我处理过的真实case来对照。某订单查询接口响应1200ms,查询频率极高。我用慢日志抓出主查询后,列出可动手的方向:A加复合索引、B改子查询为join、C减少返回字段、D把排序字段加入索引避免filesort、E调整MySQL的buffer pool、F拆分历史数据表。

按37%规则,N=6,观察期做的是前2项(约2.2取整),也就是A和B。我先不深改代码,只做explain分析加索引推演。看执行计划发现,lines字段是text类型且必查,回表严重;join的驱动表选反了。这时我已经知道大方向不是内存参数,而是减少回表和调整驱动表。

进入行动期后,第三个方向C(减少返回字段)一试——行数从扫描6万行直接降到1.8万,耗时降到280ms。按规矩我应该停止继续尝试D和E了。但那天我没忍住,顺手把D也做了,最后降到了180ms。收益确实有,但回头算笔账:C方案的性价比大约是"一行SQL改动换900ms收益",D方案是"重建一个索引、验证半小时换100ms收益"。如果在后来的运维里D成了负担(索引写放大),那更是得不偿失。

这个case给我的教训是:37%准则说到底是教你管理"决策成本"的,不是让你追求单次收益最大化。当收益曲线已经进入平台期,再投入的边际回报就极为有限。

2.3 为什么不能"把问题全查清楚再动手"

有人会杠:做优化不得先把所有情况摸清楚吗?不全查清楚,万一漏掉了那个最优解怎么办?

这个担忧看似合理,但在真实世界里它有一个被忽略的成本——时间。每多排查一个点,你就多花一段时间。真正的问题在于,排查本身也有机会成本,而且大部分系统的性能问题服从"长尾分布",前两三个关键手段通常能解决80%的问题。这意味着你把第4个、第5个潜在原因查清楚能获得的边际信息非常少。

37%准则还有一个隐藏前提:候选出现的顺序是随机的,你不能预测下一个候选是否一定更好。这和性能优化的实际情况高度吻合——你列出的优化方案清单,获益水平基本是随机的,你无法事前预判哪个收益最大。既然无法预判,最优策略就是"少预测、多对比、设门槛"。把时间花在设计更好的门槛上,比花在无限排查上划算得多。

我自己的经验是:优化任务开始前先定一个"行动触发线",效果达到多少就算达标。如果做了前37%的观察还是没头绪,也别急着全查,停下来重新定义问题,往往比继续钻牛角尖好。

3. 产品与运营优化:用门槛规则终结选择困难

把37%准则从技术性能挪到产品优化和运营决策里,它依然能用,因为这些场景有一个共性:方案池是顺序出现的,你必须在有限时间内选一个重点投入,而且选了之后反悔成本很高。

做功能迭代尤其如此。我记得有次参与一个支付转化率优化项目,产品组一口气整理了9个优化点:首屏优惠信息前置、按钮颜色、登录方式简化、支付方式排序、文案修改、优惠倒计时、渲染性能优化、客服入口后移、提交按钮防抖。按9的37%计算,大约3.3个,也就是前3个方案用来观察,后面遇到超越前3个平均表现的方案就定下来。

现实操作里,产品经理往往不是这么干的。他们会做一个"全量实验矩阵":9个点全测试一轮,每个跑两周,最后看数据再决定。时间成本是2-3个月,竞品早就迭代了两轮。更常见的是老板拍板先做他觉得最靠谱的按钮颜色,做完了没效果,再试下一个,凭感觉排序导致节奏完全混乱。

3.1 "再优化一版"背后的模型缺失

产品优化里最消耗团队士气的三个字是"再想想"。功能上线数据不理想,第一反应不是看数据,也不是继续做下一个候选,而是把当前功能推翻重来。而据我观察,推翻重来的大概率又是一个没有对标基准的拍脑袋版本,效果和之前半斤八两。

用37%准则做一套简单的决策框架,可以这样操作:把优化候选按启动顺序排列,前37%作为"对照观察组",不许投入深开发,只做轻量上线或灰度,测算出基础转化率作为阈值。从第37%之后的候选方案里,凡是预估收益能超过那个阈值、且实现成本不高的,就可以立即定为"重点方案"投入深攻。

这里面的关键点是:产品优化里我们真正缺的,很多时候不是想法,而是一个"什么时候可以停止生成新想法,开始在已生成的想法里做选择"的标准。37%门槛就充当了这个标准。

3.2 用门槛规则取代"再想想":A/B测试与候选方案筛选

A/B测试和37%准则可以结合得很好。很多人做A/B测试的问题是实验数量无限膨胀,每个小变化都开一个实验,最后每组的样本量都不够,结论全是噪声。

引入37%思维以后,流程变成这样:假设有N个实验想法,先不做N个实验,而是把前37%想法合并成"探测包",用最低成本快速抢跑一遍,得到一组基准数据。然后从剩余63%里,挑选那些在方向上与"探测包最优结果"明显不同的高优先级想法做单点实验。这样做实验总量变少,但每个实验的样本充足、结论可信。

我在某个内容产品的推荐页优化里就是这么干的。7个候选优化点,我强制要求团队在前3个只做埋点和轻量开关验证,不做复杂开发。结果发现"推荐解释理由外露"这个方向带来的点击提升远超其他方向,于是把后续精力和开发资源全压到这条线上。最后复盘发现,如果我们按传统思路平均用力,效果最好的那个方向反而会被平庸的方案分流掉一部分时间。

3.3 从37%到帕累托:两种"二八"如何合作

帕累托法则说80%的结果来自20%的投入,37%准则说的是用37%的样本建立基准。两者并不矛盾,而是配合使用。

37%准则是"决策前"的策略,它帮我们确定在哪里停止寻找更多选项;帕累托法则是"决策后"的资源分配策略,它告诉我们在既定方案内部,把80%的资源倾注在那20%的关键杠杆点上。

拿优化案例说:先用37%准则选出应该投入的那个方案,然后用帕累托法则找到方案内部的关键模块重点打磨。我在数据库优化的一个项目里,正是用37%准则锁定了"I/O瓶颈"这个方向,然后顺着这个方向做了I/O合并、预读、冷热分离三步优化,每一步明显迭代。如果顺序反过来,先盲目应用二八定律随便挑一个模块优化,我大概率会浪费两三天。

4. 技术选型与团队决策中的37%应用

除了具体的代码优化和产品迭代,37%准则在更高维度的决策里同样有很好的指导意义,比如技术选型、团队招聘、项目排期。这些场景的共同特征是:选项像流水一样来到你面前,而你的决策窗口是有限的。

4.1 技术调研期限:调研本身也要设停

做技术选型是最容易陷入"调研无底洞"的事。尤其这几年中间件、框架、数据库新东西层出不穷,每个方案都有对应的博客、源码解读、踩坑帖,越看越觉得自己还没研究透,不敢拍板。

我在做一次数据库选型时,给自己定了一个硬性规则:候选方案有5个,调研时间只有前2个方案的周期(5的37%约1.85,取2)。也就是说,前2个方案我只看架构和核心场景测试,不做深度压测;从第3个方案开始,如果它在前2个方案建立的评估标准上超越任何一个,就马上组织深测。最终选定的那个方案并不是全项第一,但在核心读写场景上是明显领先的,而且它的优先定下来让团队提前两周进入开发。

另一种常见做法是等到调研100%完成再开工,表面稳健,实际上损失的是市场窗口和团队热情。所谓的"完美选型"根本不存在,你需要的是"够好的选型"。37%准则给的就是这个"够好"的停止线。

4.2 面试、换方向与跳槽决策的启发

37%准则在招聘里的含义很容易误解,不是说"必须淘汰前37%的候选人",而是说面试官前期的37%面试主要用于校准自己的评价尺度。举个例子,如果你想招一名资深后端,前面来的5个人你心里都没底,没关系,这5个人就是你的"参照系",帮你理解当前市场行情到底什么水平、简历上的"精通"和实际能力之间差距多大。从第6个人开始,一旦遇到你觉得明显优于前5个平均水平的候选人,就要果断推进offer。

跳槽场景也一样。你手握N个机会,不可能等所有机会都面完再选,每个offer又有有效期。比较好的策略是:前面收到的offer别急着签,用它们建立"市场价基准线";当后续机会出现时,只要在总包、方向、成长性三项里有明显突破基准线的,就可以重点接触和决策。我见过太多人选offer时因为"后面可能还有更好的"而把前面的好机会全部放走,最后落得两手空空。

这个模型的本质是帮你抵抗FOMO(错失恐惧)。你不可能在所有信息都齐备后才做决定,你唯一能控制的是"在什么时候停止收集信息"。

4.3 迭代排期里的"停止信号":Unity/移动端优化的启发

移动端和游戏性能优化是另一个典型场景。通常一个Unity项目要优化的话,候选动作非常多:合批、减面、贴图压缩、代码GC优化、资源异步加载、Shader简化、遮挡剔除、LOD、包体裁剪、预加载策略……每一件扔进排期里都能吃下一两周。

这时候团队最需要的就是"停止信号",而37%准则恰好能提供一个客观的停止线。比如有10个优化方向,前4个(37%)用来快速采样,找到当前最突出的性能瓶颈类型;一旦确定瓶颈方向,后面的优化动作只要持续贡献收益,就继续沿着这个方向深挖;直到连续几个动作的收益都跌到初始峰值的1/4以下,就判断"优化收益平台期到了",可以停下来做性能回归和发版准备。

我在一个Unity小游戏的帧率优化中切身体会到:最深的一次性能提升发生在第3个动作(合批调整)上,但如果当时没有用观察期先跑前2个动作,我很容易直接陷在"改代码GC"这种听着高级、实际收效甚微的深坑里。这让我更确信,优化排期前先定"停止条件",比排期表本身更重要。

5. 实操指南:把37%准则落地到自己的项目

聊了这么多场景,我来汇总一套可以直接落地的操作流程。这套流程我反复用过,从性能优化到功能选型都吃得住,核心是五步。

5.1 一个五步落地流程

第一步:列出候选清单。凡是你觉得可能有效的优化动作,先写下来,编号,不评价。注意这里不需要做可行性排序,因为排序会引入主观判断,反而破坏随机性。

第二步:设定观察期的长度。计算N × 0.37,四舍五入得到一个整数k,这就是你"只看不做"的候选个数。N小于等于3的时候,这个规则就不太适用了,我后面会专门说边界条件。

第三步:执行观察期。对前k个候选方案,做最小成本的验证或数据收集,不深究、不优化、不投入重资源。目标很单纯:建立一个可靠的"基准线"。这个基准线可以是性能指标、转化数据、成本数据或用户反馈,只要能量化就行。

第四步:在行动期执行门槛规则。从第k+1个候选开始,每评估一个就把它和基准线比较——如果它显著优于基准线,果断选择它并终止评估;如果不优于,继续看下一个。这个过程最忌讳的是"再等等看下一个",这违背了整个模型。

第五步:做止损保护。选定方案后给自己设定一个验证周期和关闭条件:如果一段时间后收益不达预期,或者后续出现了新的明显更优的证据,就立即重复一轮观察—更新基准—再决策。37%准则不是一锤子买卖,它是可以滚动使用的。

五步看着简单,真正执行起来容易在第三步和第四步翻车。第三部的坑是忍不住手痒,总想顺手把第k个方案的优化也做了;第四步的坑是"这个方案已经很好了但下一个万一更好怎么办"。这两条我都踩过,这里一并提醒。

5.2 边界条件:什么时候别用37%

37%准则有它的适用范围,不是万灵丹。我必须把边界条件讲清楚,免得有人拿它硬套。

第一,当你能以低成本反复比较全部选项时,不要用。比如A/B测试能在几个方案之间快速跑流量,直接做全量对比就好,用37%反而人为压缩了信息量。

第二,当失败代价极大、不可承受时,不要用。例如医疗诊断、系统核心架构的不可逆迁移,这类决策应该以完备信息为准,而不是追求"概率最大"。37%准则本质是风险管理工具,不是安全保证。

第三,当候选数量太少时(N小于5),不要机械套用。用观察期留下的候选样本太少了,门槛规则基本失效。这时候凭经验和系统分析直接做判断,远比套公式靠谱。

第四,当顺序本身有强信息时,不要用。比如候选方案有明确的从劣到优排列趋势,或者方案按优先级排序好了,那37%的随机性假设就不成立了。它的数学推导前提是候选顺序随机。

边界条件不是打击使用信心,而是防止滥用。我在自己团队里常常强调一句话:没有工具是放之四海而皆准的,但理解工具的适用范围,本身就是一种专业度。

5.3 需要N吗?不知道N怎么办?

有朋友会问:我在很多真实场景里根本不知道总候选数量N是多少,比如优化一个系统的潜在手段无穷无尽,还要怎么算37%?

我的经验是分两种情况。一种情况是你可以自己定义N:把"直觉上值得试的方向"列出来,画个圈,这个圈的大小就是你定义的N。这个圈不需要覆盖所有可能的操作,因为真正值得花时间的候选,本来就不是无限多的。

另一种情况是N确实没法预先知道,比如你在探查一个陌生系统的性能问题,完全不知道可能的原因有几个。一个实用替代方案是"滚动窗口法":先拿近期查看过的原因列表当N,按顺序计算37%基准线,当前原因一旦越线就深挖。过一段时间如果没突破,就把窗口往前滚动,加入新发现的原因,重新计算。这相当于把静态的37%策略变成了动态的自适应版本。

我试过这个滚动窗口法,处理线上偶发超时问题时效率很高。那种问题本质上原因池是开放的,静态清单必然漏项,滚动窗口能兼顾信息更新和决策效率。

6. 我踩过的坑与一份自检清单

理论说得再多,不如实操教训来得深刻。这里我把在反复使用37%准则时踩过的坑、总结出的心得,一次性列出来。

6.1 常见误区(三条)

第一个误区是把37%理解成"只看前37%,然后在后63%里选"。这是理解偏差。正确的做法是前37%用来建立基准,后63%用来执行"越线即选"的门槛规则。选中的那个人大概率落在后63%,但前37%的作用是帮你知道"好在哪",而不是单纯被淘汰。

第二个误区是卡精确的37%。我做过多次模拟测试,30%到40%之间的成功率差距非常小。现实场景里你根本不需要去精算到小数点后两位。如果你非要纠结"5个候选到底是取1个还是取2个",选哪个都不会错太多,真正错的是取到5个里的第4个还在观察。

第三个误区是认为37%准则能帮你选到最优。它只能最大化"选到最优的概率",而且这个最大值只有37%左右。换句话说,用这个策略你仍然有六成概率选不到全局最优。这一点非常反直觉,但必须清醒:它是一个决策框架,用来在信息不足时定出一个理性可选的动作,不是水晶球。

6.2 一张速查表

为了方便日常使用,我把几种典型场景下的应用方式整理成了速查表:

场景N怎么定观察期做什么行动期触发条件
慢SQL优化候选项列出的优化手段数量explain、行数估算、成本基线收益显著优于基线就深挖
性能调优方案池可能的调参/改造方向数最小验证、压测快照方向收益超基准线1.5倍以上
产品迭代候选功能需求池想法数量埋点/灰度/低成本验证转化指标明显超过观察组均值
技术选型候选技术栈数量阅读架构文档、跑demo核心指标超越基准方案即可锁定
候选人面试总面试人数前37%面试只做评价校准从后63%开始越线即推进

这张表最大的作用是提醒我一句话:任何一种优化都不是"把手上资源全部砸进去",而是"花小钱买信息,再集中火力打突破点"。

6.3 最后想分享的一个小技巧

关于37%准则,我最后的补充是一个使用小技巧:它不只适用于"一次决策",也可以用在"多轮滚动优化"里。每完成一轮选型和投入,就把新收集到的信息加入基准,然后重新计算下一轮的37%观察窗口。这比一次性把所有选项铺开更贴合真实业务节奏,因为很多优化问题本身就是不断有新信息流进来的过程。

我还会把这个模型内化成一种日常思维方式:当我在一个问题上花了超过三分之一的精力却还在收集信息时,就会停下来问自己——我是不是该从探索模式切换为行动模式了?这个问题问多了,你会发现自己做决定的速度明显变快,而且不再纠结。对我而言,这就是37%准则在优化工作中最大的价值:它不只是帮你选更好的方案,还帮你在"无限追求最优"的焦虑里,找到一个可以心安理得停下来的理由。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询