【问题标题】:Where to do joins - in the database server or in the application server?在哪里进行连接 - 在数据库服务器或应用程序服务器中?
【发布时间】:2010-10-12 14:29:44
【问题描述】:

我目前正面临一个性能问题(它可能会导致稍后出现扩展问题)。我正在处理的应用程序非常复杂,它在 SQL Server 2005 上运行。我需要连接 6 - 7 个表来获取所需的数据。到目前为止,每个表都包含超过 100,000 行数据。无法更改数据库架构(必须保持原样)。所以我只能尽量优化。我想到了两件事:

  • 尽量不要加入数据库,让应用服务器使用LINQ进行过滤:

    • 优点:可以通过添加更多应用服务器轻松扩展。
    • 缺点:更加努力;我不确定响应能力是否会降低。
  • 应用服务器保持原样并尽可能优化 SQL 查询(更多索引、频繁重建索引等):

    • 优点:省力
    • 缺点:当表记录变大时,问题会再次出现

目前缓存基本上不是我的解决方案(硬件问题、托管问题等),这就是我最初没有提出它的原因。但我确实知道缓存会给我带来什么好处,并且已经使用过很多次了。

【问题讨论】:

  • 6 - 100,000 行上的 7 个连接并不大...

标签: database performance join scaling


【解决方案1】:

不要忽视在服务器之间传输数据的开销。以太网在负载下会迅速降级(我认为持续传输速率大约是单数据包速率的 30%;即,您的 100Mb/秒链路实际上只能处理 30Mb 的大流量)。一旦您在数据库服务器上的链接饱和,添加更多应用服务器就不再重要了,因为您将无法更快地获取数据。

应用服务器上的连接也会让您受制于最慢的服务器。我们在客户端站点看到性能下降,发现主应用服务器已经崩溃,客户端的恢复策略是让机器故障转移到运行在其他服务器之一上的虚拟机。一种巧妙的解决方案,但肯定不如性能。当路由器出现故障时,我还看到速度变慢,突然间所有对等服务器都在三到四跳之外,而不是在同一个子网中。

【讨论】:

    【解决方案2】:

    您提到每个表都有“超过 100,000 行”,但您没有提及您选择的数据量以及连接的复杂程度。对于正确设置和索引的 SQLServer,100K 行不会很大。我们有 17 路连接,它们在几毫秒内返回结果,但它的索引很好并且选择了几行。在开始重新设计您的应用程序之前,我会查看 SQLServer 上的分析信息。

    【讨论】:

      【解决方案3】:

      一般来说,在 DBMS 中进行连接。如果您在应用程序服务器中执行此操作,那么您打赌您可以比编写 DBMS 的人更好地优化连接,并且(进一步)您可以通过足以抵消成本的方式超越他们的最佳努力通过网络传输未连接的数据。

      现在,如果您要对两个宽表(假设它们是 T1,N1 行宽度为 W1 和 T2,N2 行宽度为 W2)进行交叉乘积而不进行过滤,那么 DBMS 是必须通过网络创建和发送 N1 * N2 * (W1 + W2) 字节的数据,而您可以将表格分别作为 N1 * W1 + N2 * W2 字节的数据来提取。如果 N1 = N2 = 1M 和 W1 = W2 = 100,那么这是 200 TB 与 200 MB 的数据传输,有利于在应用服务器中进行交叉产品。但这对 DBMS 来说并不完全公平。大多数查询并不是那么愚蠢——它们加入列并应用条件,DBMS 优化器将竭力(并且自动地)努力最小化完成的工作。此外,它只会将相关数据发回给您;它不必发送所有与您的条件不匹配的行。

      为了展示另一种情况(有利于 DBMS),请考虑以下情况:T1 有 N1 = 1M 行宽度 W1 = 100,但 T2 有 N2 = 100K 行宽度 W2 = 50。一个整数列上有两个表,因此,T1 中有 10 行,T2 中的每一个。假设您将所有 T1 和 T2 吸入应用服务器:这需要 N1 * W1 + N2 * W2 = 105 MB 的数据。但是过滤条件将数据限制为 T2 中行的 1/10,对于 T1 中与 T2 中的一行匹配的每一行,实际上只有 2 行匹配过滤条件。现在 DBMS 只会转移 N2 * (W1 + W2) / 5 = 3 MB,DBMS 节省了超过 100 MB 的数据传输。现在,如果您设法聪明并仅下载与 T2 中的值相对应的 N2 * W2 / 10 = 500 KB 数据,您仍然必须让 DBMS 对值执行 T1 的“半连接”您想从 T1 获取正确的行到应用服务器。如果您只需要列的一个子集,则可以节省另一组。 DBMS 往往有相当聪明的排序包;你需要在你的应用服务器中有一个好的排序包来以正确的顺序呈现数据。

      对于 DBMS 中的联接,通常应该是一个不折不扣的胜利。如果不是,那是因为您要求服务器做的工作超出了它的处理能力。在这种情况下,您需要查看复制数据库服务器是否有意义,或者添加更多内核、更多网络带宽或更多主内存是否可以完成这项工作。

      【讨论】:

        【解决方案4】:

        通过在“不加入”场景中添加更多服务器,您将获得更多性能提升,无论是尝试优化连接。你是对的 - 当你有更多数据时,问题就会出现。

        最好的解决方案是使用内存缓存。您可以缓存大多数尺寸较小的表-表关系,而不是一直获取它们。

        最佳方法是最小化连接,最小化选择,然后将很少更改的数据缓存到内存中。这将起到推动作用。

        根据 Microsoft(以及其他数据库制造商)关于联接的建议 - 尽可能优化使用它们。根据我的经验 - 超过 2-3 个加入复杂选择的人数最多。

        【讨论】:

          【解决方案5】:

          总的来说,我在谈论规模时会考虑以下几点:

          1. 多久执行一次?对于访问频率较低的查询,您可能会接受一些性能下降。

          2. 增长率/变化率是多少?如果其中一些表中的记录相对静态,您可能需要考虑在外部将内容缓存在 dbm 类型的文件(或任何 Windows 等效文件)中。还有像 memcache 这样的东西可能值得一看。不过,这可能会也可能不会。这是基于在应用程序代码中执行“连接”。

          3. 个人资料。如果你加入索引列(你是,不是吗?),你不一定会随着行数的增长而降级。这将在很大程度上取决于您是在处理 1:1 还是 1:N 关系、N 的平均大小是多少、数据库服务器上有多少可用内存、如何处理通常会计算您的表统计信息,以及列和索引的类型。如果您正在处理 1:1 关系并且它是唯一的,那么数据库将能够进行简单的哈希和查找。

          确保将获取的列限制为绝对不超过您的需要,尤其是在连接许多表时,因为如果连接两个表所需的只是被索引的列,数据库甚至可能不会考虑该表一点也不;可以仅使用索引来执行连接。这减少了争用并提高了需要处理表的实际内容的次优查询的性能,因为拉动表的查询更少。

          所有关系数据库都有一个工具或功能来查看给定查询的查询执行计划。用它。如果输出对您没有意义,请学习它。这是您了解数据库将如何处理给定查询、将使用哪些索引、在每个执行步骤中将遇到的估计(或实际)行数以及其他有趣内容的主要窗口。

          一旦您了解了查询优化器对查询的实际操作,并且所有索引/统计信息/列选择都一目了然,您就会更好地了解从那里去哪里。如果你在数据库中尽你所能,你将不得不考虑使用数据缓存,并使用更具体/更好的 where 子句来处理更少的表。

          免责声明:我没有直接使用 SQL Server 的经验,但我在其他 RDBMS(Oracle、MySQL、PostgreSQL 等)和一般架构方面有很多经验。

          【讨论】:

            【解决方案6】:

            您需要检查哪些索引已经到位,它们(和统计信息)是否是最新的,以及新索引是否有利于您的查询工作量。

            【讨论】:

              猜你喜欢
              • 2013-07-02
              • 2015-11-12
              • 2015-09-25
              • 2017-09-14
              • 2017-09-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多