在高吞吐量数据库环境中,“勉强够用”的SQL所带来的代价
在许多环境中,每天都在悄然进行着一种妥协。这种妥协很少出现在项目计划或架构图中,也没有人正式批准它,但它确实存在。“勉强够用”的SQL被部署到了生产环境。
起初,这似乎无伤大雅。查询返回了正确的结果,测试期间的性能也尚可。应用程序达到了其服务水平协议(SLA)的要求。大家便继续推进工作。但在高吞吐量环境中,“勉强够用”很少能长期维持下去。
规模的倍增效应
高使用率数据库应用程序的一个显著特征就是规模。我们谈论的不是每天几百次执行,而是同一条SQL语句被执行数千次,有时甚至是数百万次。而在这种规模下,微小的低效问题绝不会是微不足道的。
这里多一次访问,那里多一次不必要的排序,或者使用了不匹配的索引。每次单独的执行效率可能仅略有下降。但乘以数百万次,其代价便变得相当可观。
CPU消耗上升,执行时间逐渐延长,缓冲池承受的压力也随之增大。于是,原本“足够好”的查询突然成为了资源争用的真正肇因。这是一个根本性问题,因为SQL并非孤立执行:它是大规模执行的。
当“可接受”变得代价高昂
在开发和测试阶段,SQL通常是在有限的条件下进行评估的:数据量较小、并发用户较少、工作负载可控。在这种情况下,许多访问路径看起来都还算可以接受。使用全表扫描而非索引访问可能不会引起警报。排序可能很快完成。次优的连接顺序可能并不明显。
但生产环境却是另一个世界。数据量增长、并发性增加、工作负载相互重叠,曾经“可以接受”的做法如今变得代价高昂。
考虑一个简单的例子。对中等规模表执行全表扫描的查询,在测试环境中可能只需几分之一秒即可完成。但在生产环境中,面对显著更大的数据量和相互竞争的工作负载,相同的扫描操作会推高I/O负载,消耗用于数据缓冲的内存,并影响其他应用程序。SQL语句本身并未改变,改变的是运行环境。
隐形的CPU开销
“足够好”的SQL语句最隐蔽的危害之一,就是它带来的隐形CPU开销。有时,CPU消耗会产生巨大的成本。例如,IBM Z的CPU消耗不仅是一个技术指标,更是一项业务成本。CPU消耗的增加会直接导致软件许可成本上升,并给容量规划带来更大压力。
我曾见过这样的环境:寥寥几个低效的SQL语句就占用了总CPU使用率中不成比例的大部分。这并非因为它们结构复杂,也并非因为它们存在明显的编写缺陷,而是因为它们被频繁执行。
每次执行都会带来微小的性能开销。系统最终要承担这些开销的总和。正因如此,“勉强够用”便不再仅仅是技术问题,更演变为财务问题。
访问路径的重要性超乎想象
SQL性能的核心在于查询优化器。基于一组统计信息和可用的访问路径,DBMS中的优化器会选择其认为执行查询最高效的方式。但优化器只能基于给定的信息进行工作。如果索引设计不佳、统计信息过时,或者SQL语句本身的编写方式限制了优化,最终生成的访问路径可能并不理想。
“足够好”的SQL通常依赖于DBMS当时恰好选择的访问路径。人们很少去验证该访问路径是否真正高效,或者随着数据的变化它是否仍能保持稳定。随着时间的推移,这会导致不可预测性。
更新数据库统计信息、DBMS版本升级、数据分布的变化——任何一种情况都可能导致访问路径发生改变。如果原始SQL语句构建不佳,新的访问路径可能会显著变差。这意味着曾经可以接受的方案,如今已不再适用。
对系统的累积影响
人们往往倾向于逐条评估SQL语句的性能。但在高负载环境中,实际影响是累积的。多个“勉强合格”的查询同时执行,可能会引发系统性问题。
由于不必要的I/O操作,缓冲区的效率会降低。随着查询运行时间超过必要时长,锁定和竞争可能会加剧。批处理窗口可能会扩大。在线响应时间可能会变差。
单个查询或许能通过性能测试,但集合起来却会给系统带来压力。
这就是为什么仅关注单个SQL语句会忽略全局。目标不仅仅是让每个查询达到可接受的标准,而是要确保整体工作负载的效率。
为何这种情况屡屡发生
既然代价如此明显,为何“勉强过得去”的SQL依然屡见不鲜?部分原因在于时间压力。开发团队的考核标准是交付进度。如果SQL能正常运行并满足当前需求,就很少有动力去进一步优化它。
另一个因素是工具。现代工具可以生成SQL、建议索引,甚至推荐访问路径。这些功能固然宝贵,但也可能带来一种虚假的安全感。
仅仅因为工具认为SQL代码可以接受,并不意味着它就是最优的。
还有就是深厚的数据库专业知识正逐渐流失。理解优化器的工作原理、访问路径的选择机制以及如何设计高效的SQL代码,都需要丰富的经验。随着这类经验变得越来越少,识别次优SQL代码的能力也就随之减弱。于是,“勉强够用”便成了默认标准。
提高标准
解决这个问题并不需要追求完美,而是需要有意识地去关注。首先要认识到,SQL性能优化并非一次性活动——而是一项持续的纪律。
审查SQL时,不仅要检查其正确性,更要关注其效率。应仔细检查并理解访问路径。索引设计应有计划,而非被动应对。
同样重要的是,要随着时间的推移重新审视SQL。去年高效的查询,今天可能已不再高效。数据在变化,工作负载也在演变。定期审查至关重要。
此外还涉及文化因素。团队需要像重视功能一样重视性能。这并不意味着为了追求完美的查询而延误每个项目,而是要避免在高吞吐量环境中认为“够用就好”就足够了。
核心要点
在低吞吐量系统中,“够用”的SQL可能确实足够好。但在高吞吐量环境中,这种情况很少见。其代价并不总是立刻显现,而是会悄然累积:额外的CPU周期、增加的I/O操作、更长的执行时间、更严重的资源争用。
直到某一天,这种累积不再“悄然”。届时,系统将为成千上万(甚至数百万)个从未得到解决的小问题付出代价。道理很简单:SQL并非孤立存在,而是存在于大规模环境中。而在大规模环境中,“够用”的代价可能会高得惊人。
作者:Craig S. Mullins