梁敬彬梁敬弘兄弟出品
往期回顾
技术人生------第1 集: 机械出身,我不机械
技术人生------第2 集: 膨胀自信,午夜惊魂
技术人生------第3 集: 穿越救赎,因祸得福
技术人生------第4 集: 便捷惨案,泣血蜕变
技术人生------第5 集: 剑破冰山,畅行无阻
技术人生------第6 集: 收获惊喜, 不止O记
技术人生------第7 集: 一本书籍,一所小学
技术人生------第8 集: 打破枷锁,火箭跃升
技术人生------第9 集: 拓荒立制,创研究院
技术人生------第10集:幸运敲门,新曲线成
技术人生------第11集: 收获不止,SQL优化
技术人生------第12集:思想破晓,功能追随
1. 这些SQL,真有运行的必要吗?
上一集,我通过回忆三个"思想跑在功能前面"的案例,更加坚信了"优化的尽头是思想"这一道理。但这,依然只是"术"的层面。在为我的新书《收获,不止SQL优化》搜寻更深层次的"道"时,我意识到,所有优化的前提,都源于一种更宝贵的能力------怀疑。敢于对一切"理所当然",提出一个"为什么",这,才是优化的最高境界。
在我构思新书的那个下午,我复盘的第一个案例,就充满了这种"怀疑"的味道。
那是一个被性能问题拖垮的核心系统,当时团队所有专家都围着一条"功勋卓著"的老SQL一筹莫展。这条SQL负责校验用户资格,逻辑复杂,稳定运行了两年,但随着数据量暴增,它成了系统最大的瓶颈。
当时所有人的思路,都是"如何让它跑得更快"。但在我花了一天时间,和业务人员反复沟通,并把新旧两版业务流程图都画在白板上之后,我提出了一个让整个会议室都瞬间安静的问题:"我们,为什么一定要执行它?"
起初,所有人都觉得我疯了。但在我的坚持下,经过层层溯源,一个让所有人大跌眼镜的事实浮出水面:这条SQL的校验逻辑,在一次业务改版后,已经被一个新功能完全覆盖了!但当时没人敢动老代码,就让它一直"沉睡"在系统里,日复一日地空跑。
当我最终在代码里,将这几行"功勋卓著"的SQL,亲手砍掉时,整个世界,都清凉了。嗯,不优化,直接砍了!【参考链接:不优化是最好的优化】。

2. 这些SQL,真要执行这么多次?
尝到了"快刀斩乱麻"的甜头,我开始用这种"批判性思维",去审视系统里所有看似"天经地义"的逻辑。我的第二个案例,来自系统的"推荐"功能。
这个功能,会根据用户的行为,为他推荐可能感兴趣的内容。其中,一条刷新推荐列表的SQL,被设置成每分钟执行一次。它本身不慢,但巨大的执行频率,给数据库带来了持续的、不必要的压力。
在评审会上,我问了产品经理李哥:"一个用户的兴趣,真的会每分钟都发生一次变化吗?我们这种'实时'的价值,和它带来的服务器成本,成正比吗?"
这个问题,让产品经理陷入了沉思。最终,我们一致认为,将这个刷新频率,从"每分钟一次",改成"每小时一次",对用户体验,毫无影响。但是这个执行次数骤降后,系统整体的运行性能得以大幅提升。

3. 这些SQL,真要一次性全跑完?
第三个案例,是关于一个很多系统都有的通病------"过度加载"。
那是一个功能非常复杂的主界面,用户一登录,系统就会"热情"地、一次性执行十几条SQL,去查询并加载出该用户拥有的所有子菜单、所有权限、所有待办事项......这导致所有用户的首页加载速度,都慢得令人发指。
当时团队的优化思路,还是如何去加速这十几条SQL。
而我想搞明白的问题是:"有哪个用户,会在一次登录里,把所有菜单都点一遍呢?我们真的有必要,在用户打开主页的那一刻,就把所有他'可能'会用到的数据,都一次性准备好吗?"
答案是否定的。我们立刻对页面加载逻辑进行了优化,改成了"按需加载"(Lazy Loading)------只有当用户真正点击某个父菜单时,才去异步执行那条查询其子菜单权限的SQL。
这个小小的思想转变,让所有用户的首页加载速度,快得飞起。

4. 这些SQL,真需要这么实现吗?
最后一个案例,聊聊最为常见的count(*) 语句【参考链接:小count大智慧】,请看这条SQL。
sql
begin
select count(*) into v_cnt from t1;
if v_cnt > 0 then
...A逻辑... -- 表中有记录时执行
else
...B逻辑... -- 表中没有记录时执行
end if;
end;
对此,我和开发人员说:"如果你要出差,想找一个空皮箱,你需要打开皮箱,一件一件数里面有多少件衣服,来判断这个皮箱是否为空吗?"
开发人员笑着说:"当然不需要了,我只要看一眼就知道了啊。"
"是的,你随便伸手摸衣服,能拿到一件,就表示不是空皮箱了。这其实和你的这个SQL代码的逻辑是一致的,你只要在表中找第一条记录,找到就表示不空,而找第一条记录不需要遍历全表,是很快的。"我继续和开发人员解释道。

最终研发人员恍然大悟,将SQL调整如下:
sql
begin
-- 检查t1表是否有数据,只需找到一条记录即可,比全表COUNT(*)更高效
select count(*) into v_cnt from t1 where rownum=1;
if v_cnt=1 then
...A逻辑... -- 表中有数据时执行此逻辑
else
...B逻辑... -- 表中无数据时执行此逻辑
end if;
end;
这个微小的改动,在处理海量数据时,性能的提升是指数级的。
回想完这些经历,我更加坚信,优化的尽头,是哲学。不过,这是否就是优化的全部了呢?我隐隐觉得,在"快"与"慢"、"多"与"少"的二元对立之外,或许还存在着一个更广阔的、关于"稳定"与"平衡"的全新世界。
未完待续...