额外的数据库查询有多糟糕?

5

我来自于Web开发中的前端领域,我们会尽力减少HTTP请求的数量(通过合并CSS、JS文件、图片等方式)。

在使用数据库连接(MySQL)时,显然你不希望有不必要的连接,但通常情况下,拥有多个小查询有多糟糕呢?(它们执行得很快)

我之所以问这个问题是因为我正在将我的应用程序移植到一个集群环境中,在此之前,我将一些内容缓存到服务器内存中(因为我只运行在单个服务器上),现在我正在尝试使我的应用程序“无状态”,在我的当前实现中,这意味着更多的小型数据库调用。这将帮助我进行负载平衡(避免粘性会话)并且保持服务器内存使用率低。

我们不会谈论大量的查询,可能是6-8个数据库调用而不是2-4个,返回数十条记录到几千条记录。它们每个都执行得很快,少于30ms(有些更少),但我不知道是否应该担心某些“连接延迟”。

感谢您的见解。


布赖恩,我很乐意稍微发表一些见解,但现在没有机会。 - Drew
谢谢Drew,期待您的见解。 - Brian FitzGerald
所有规则都有例外。要根据实际情况做出最好的决策。 - Dan Bracuk
1
丹所说的。在某些情况下,一个长时间运行的查询可能比两个或三个小而快速的查询更糟糕。无论哪种方式,您都应该尽可能使用“CachedWithin”。 - TRose
1
你也可以考虑将查询分组到一个存储过程中,然后使用cfstoredproc标签调用它。 - Dan Bracuk
2个回答

9
简短回答:(1)确保您保持相同的大O级别,重用连接,测量性能;(2)考虑您对数据一致性的关注程度。
详细回答:
性能
从性能角度严格讲,通常情况下,除非您已经接近数据库资源的最大限制,例如最大连接数,否则这不太可能产生重大影响。但是有些事情你应该记住:
- 替换“2-4”个查询的“6-8”个查询是否保持相同的执行时间?例如,如果当前数据库交互处于O(1),它是否会更改为O(n)?或者当前的O(n)是否会更改为O(n ^ 2)?如果是,则应考虑这对您的应用程序意味着什么。 - 大多数应用程序服务器可以重用现有的数据库连接,或具有持久的数据库连接池;确保您的应用程序不会为每个查询建立新连接;否则这将使其更加低效。 - 在许多常见情况下,主要是在具有复杂索引和连接的较大表上,通过主键进行少量查询可能比在单个查询中连接这些表更有效;如果在执行此类连接时,服务器不仅需要更长时间执行复杂查询,而且还会阻止针对受影响表的其他查询。
总的来说,对于性能而言,经验法则是-始终进行测量。
一致性
但是,性能并不是唯一要考虑的方面。还要考虑您在应用程序中对数据一致性的关注程度。
例如,考虑一个简单的情况-表A和B具有一对一关系,并且您正在使用主键查询单个记录。如果您连接这些表并使用单个查询检索结果,则会从A和B中获取记录,或者从两者中都没有获取记录,这也是您的应用程序所期望的。现在考虑将其拆分为2个查询(并且您没有使用首选隔离级别的事务)-您会从表A中获取一条记录,但在您可以获取匹配的表B记录之前,它已被另一个进程删除/更新。现在您的应用程序具有来自A但没有来自B的记录。
一般问题是-您是否关心与您正在拆分的查询相关的关系数据的ACID合规性?如果答案是肯定的,则必须考虑您的应用程序逻辑将如何在这些特定情况下做出反应。

非常好的回答,感谢您的见解!关于您对“始终进行测量”的评论,是否有任何特定的工具用于测量数据库性能? - Brian FitzGerald
当 ColdFusion 处于调试模式时,可以设置它显示查询运行所需的时间。它还可以显示查询是否被缓存。 - James A Mohler
@BrianFitzGerald 我建议在监控数据库(CPU、RAM、连接、慢查询等)的同时测量应用程序性能。如果您有一个可用于负载测试的环境,或者可以为此目的创建一个单独的堆栈,那么使用siege、apachebench或类似工具应该非常简单。 - Unix One

5

一个网页6-8个查询?通常这样做没有问题,我经常这样做。

返回成千上万行数据?会出毛病!客户端要怎么处理这么多数据?SQL能否进行更多的处理,然后返回较少的行数呢?

除非特殊情况,每个网页只有1个连接。

每个查询都有很多开销。例如,向表中INSERT100行数据——使用100个单行INSERT语句将花费约10倍于一个100行INSERT语句。因此,当实际可行时,尽量减少与服务器之间的往返次数。如果网络是WAN,这一点变得尤为重要。地球的另一端需要250毫秒才能传输过来,仅仅是延迟时间而已。同一数据中心内的服务器可能如此接近,以至于延迟可以忽略不计。在WAN中,使用存储过程最小化往返次数。

我喜欢在代码中主动计时每个查询。然后,如果我遇到了性能问题,我就会查看哪个查询应该首先处理。或者使用SlowLog。


1
谢谢Rick!里面有一些很好的技巧。而且你提到了成千上万行的问题,这基本上是在尝试预填充用户对象,以便我可以执行类似user.getFavorites()(例如),并且所有用户的收藏都可以用于使用。我知道它们可以进行惰性加载等操作,但在变为“无状态”之前,用户是按会话缓存的,因此在会话初始化时预填充一次是没有问题的。无论如何,您已经说服我对我的应用程序进行一些架构更改,以防止在每个请求上加载太多记录 :) - Brian FitzGerald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接