降低 SQL 查询的 CPU 成本

Posted

技术标签:

【中文标题】降低 SQL 查询的 CPU 成本【英文标题】:Lowering CPU cost on an SQL Query 【发布时间】:2013-03-25 14:47:23 【问题描述】:

我有一个如下查询,其中 table150k 有 150k 条记录,而 table3m 有 3m 条记录。在我们的生产服务器上,我们必须非常频繁地一次针对一条记录运行此查询。这会消耗大量 CPU 功率。

select t.id, t1.field1 as f1, t2.field1 as f2, t3.field1 as f3, ..., t12.field1 as f12
from table150k t
inner join table3m t1 on t1.fk = t.id and t1.[type] = 1
inner join table3m t2 on t2.fk = t.id and t2.[type] = 2
inner join table3m t3 on t3.fk = t.id and t3.[type] = 3
...
inner join table3m t12 on t12.fk = t.id and t12.[type] = 12
where t.id = @id

当我从此查询中删除内部联接时,它工作正常。当它们被包含在内时,我们的服务器会受到 cpu 的影响。

我应该如何优化这个查询、数据结构或场景,以使频繁获取数据的 CPU 成本不会那么高?

【问题讨论】:

当您删除连接时,您只能从一个表中进行选择。添加连接会增加复杂性,因此 CPU 会增加。 患者:“医生,我这样做会很痛”。医生:“那就别这样了”…… 为什么需要多次加入同一张表? 所涉及的表上有哪些索引,执行计划是什么样的? 这些是1-1连接吗? t.id 有多少个重复项?而且,您真的是从表中选择不同的字段,还是:t1.field as field1, t2.field as field2, . . . 【参考方案1】:

你有table3m(fk)的索引吗?

这应该可以解决您的问题。

另一种表述是:

select t.id,
       max(case when m.[type] = 1 then field end) as field1,
       max(case when m.[type] = 2 then field end) as field2,
       . . .
       max(case when m.[type] = 12 then field end) as field12
from table150k t join
     table3m m
     on m.fk = t.id and m.[type] in (1,2,3,4,5,6,7,8,9,10,11,12)
where t.id = @id
group by t.id

此结构包含来自“3m”表中同一列的所有数据。

【讨论】:

table3m(fk + [type]) 怎么样? @JoeEnos 我们也有 :( 我将尝试替代配方并发布结果。 戈登的回答是对的。 SQL Server 将处理他的查询,对表进行单次传递。您还可以使用 PIVOT 运算符。关于索引,table150k 应该聚集在 id 上。 table3m 需要一个带有 fk 和 type 的索引,可能按照这个顺序,但也可能颠倒过来。如果只有一小部分 table3m 符合标准并且没有经常更新,请考虑仅包含这两列的物化视图。 @JamesK.Lowden - 单次遍历表不一定比具有正确索引的多次连接更好。 See plans and timings here【参考方案2】:

试试这个:

select *
from table150k t
inner join table3m t1 on t1.fk = t.id and t1.[type] in (1,2,3,4,5,6,7,8,9,10,11,12)
where t.id = @id

【讨论】:

这不会产生 12 倍的记录吗? 它是一个内连接,而不是左连接 它仍然会获取 12 倍的记录,因为对于 table3m 中的每条记录,每一行都会重复。 这个例子看起来很简单,但在实际的生产代码中,除了t1.Id和table3m.field1之外还有很多列。所有这些都将被复制 12 次。 @SerhatÖzgel - 没错,但它仍然可能表现更好。如果您尝试一下并且确实表现更好,则可能值得更改您的应用程序,以期望每个详细信息值一条记录,而不是每个详细信息一列。【参考方案3】:

如果两个表中的数据不经常更改,我会按照另一种方法创建一个缓存表(只是另一个表),它只保存上述查询的结果。

【讨论】:

业务逻辑需要数据是即时的:(【参考方案4】:
select t.id, t1.type, t1.field1
from table150k as t
inner join table3m as t1 on t1.fk = t.id 
where t.id = @id and t1.[type] in (1,2,3,4,5,6,7,8,9,10,11,12)

这将带回 12 条记录(假设它们都存在)。

这里的优点是服务器端的速度,缺点是您必须将每条记录映射到数据表中的相应列或基于类型值的对象上的值,一旦您将其导入应用程序。

【讨论】:

问题是这段代码将所有不同的 [type] 融合到一个列中,而 OP 希望它们分开在不同的列中 是的,这将为每种类型获取一个值记录。这就是为什么我通过说一旦他将数据放入他的应用程序中就必须重新组织数据来限定它。它在数据访问层需要做更多的工作,但它比同一张表上的 12 个内部连接要快得多。

以上是关于降低 SQL 查询的 CPU 成本的主要内容,如果未能解决你的问题,请参考以下文章

索引优化驱动SQL优化总结

tsql 查询分析器:如何降低“成本”?

如何降低通过链接服务器执行的查询的成本

SQL Server 中的 SQL 查询优化

Bigquery 集群不会降低查询成本

USQL如何帮爱普新媒降低80%成本,提升50%数据分析速度