Operator 成本不应该至少与包含它的 I/O 或 CPU 成本一样大吗?

Ian*_*oyd 11 sql-server sql-server-2014

我在一台服务器上进行了查询,优化器估计其成本为 0.01。实际上,它最终运行得非常糟糕。

  • 它最终执行聚集索引扫描

注意:您可以在 Stackoverflow 上找到详细的 ddl、sql、表等。但是这些信息虽然有趣,但在这里并不重要——这是一个不相关的问题。而这个问题甚至不需要DDL。

如果我强制使用覆盖索引查找,它估计使用该索引的子树成本为 0.04。

  • 聚集索引扫描:0.01
  • 覆盖索引扫描:0.04

因此,服务器会选择使用以下计划也就不足为奇了:

  • 实际上会导致聚集索引的 147,000 次逻辑读取
  • 而不是覆盖索引的 16 次读取要快得多

服务器A:

| Plan                                       | Cost      | I/O Cost    | CPU Cost  |
|--------------------------------------------|-----------|-------------|-----------|
| clustered index scan (optimizer preferred) | 0.0106035 | 116.574     | 5.01949   | Actually run extraordinarily terrible (147k logical reads, 27 seconds)
| covering index seek (force hint)           | 0.048894  |   0.0305324 | 0.0183616 | actually runs very fast (16 logical reads, instant)
Run Code Online (Sandbox Code Playgroud)

这与最新的统计信息 WITH FULLSCAN 相同。

在另一台服务器上尝试

所以我在另一台服务器上尝试。我使用生产数据库的最新副本获得了相同查询的估计值,以及最新的统计数据(使用 FULLSCAN)。

  • 这台另一台服务器也是 SQL Server 2014
  • 但它正确地意识到聚集索引扫描是不好的
  • 它自然更喜欢覆盖索引查找(因为成本要低 5 个数量级!)

服务器 B :

| Plan                                      | Cost        | I/O Cost   | CPU Cost  |
|-------------------------------------------|-------------|------------|-----------|
| Clustered index scan (force hint)         | 115.661     |   110.889  | 4.77115   | Runs extraordinarily terrible as server A (147k logical reads, 27 seconds)
| Covering index seek (optimizer preferred) |   0.0032831 |   0.003125 | 0.0001581 | Runs fast (16 logical reads, near instant)
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚为什么对于这两个服务器,几乎相同的数据库副本,都有最新的统计数据,都是 SQL Server 2014:

  • 可以如此正确地运行查询
  • 另一个摔倒死了

我知道这似乎是统计过时的经典案例。或者缓存执行计划,或者参数嗅探。但是这些测试查询都是用 发出的OPTION(RECOMPILE),例如:

SELECT MIN(RowNumber) FROM Transactions
WITH (index=[IX_Transactions_TransactionDate]) WHERE TransactionDate >= '20191002 04:00:00.000' OPTION(RECOMPILE)
Run Code Online (Sandbox Code Playgroud)

仔细一看,好像“算子”估计是错的

聚集索引扫描是一件坏事。其中一台服务器知道这一点。这是一个非常昂贵的操作,扫描操作应该告诉我。

如果我强制执行聚集索引扫描,并查看两台服务器上的估计扫描操作,就会发现一些事情:

在此处输入图片说明

| Cost                | Server A    | Server B   |
|---------------------|-------------|------------|
| I/O Cost            | 116.573     | 110.889    |
| CPU Cost            |   5.01945   |   4.77155  |
| Total Operator Cost |   0.0106035 | 115.661    |
                        mistakenly  | avoids it
                          uses it   |
Run Code Online (Sandbox Code Playgroud)

服务器A上的Operator成本太低了。

  • I / O成本是合理的
  • CPU的成本是合理的
  • 但综合起来,Operator的整体成本太低了 4 个数量级。

这就解释了为什么它错误地选择了糟糕的执行计划;它只是有一个糟糕的运营商成本。服务器 B 已正确计算,并避免了聚集索引扫描。

不是operator = cpu+io吗?

几乎在您将鼠标悬停在每个执行计划节点上,以及 dba、stackoverflow 和所有博客上执行计划的每个屏幕截图上,您都会看到:

operatorCost >= max(cpuCost, ioCost)
Run Code Online (Sandbox Code Playgroud)

事实上,它通常是

operatorCost = cpuCost + ioCost
Run Code Online (Sandbox Code Playgroud)

那么这里发生了什么?

什么可以解释服务器决定 115 + 5 的成本几乎为零,而是决定成本的 1/10000?

我知道 SQL Server 可以选择调整应用于 CPU 和 I/O 操作的内部权重:

DBCC    TRACEON (3604);     -- Show DBCC output
DBCC    SETCPUWEIGHT(1E0);  -- Default CPU weight
DBCC    SETIOWEIGHT(0.6E0); -- I/O multiplier = 0.6
DBCC    SHOWWEIGHTS;        -- Show the settings
Run Code Online (Sandbox Code Playgroud)

当您这样做时,操作员成本最终可能低于 CPU+I/O 成本:

在此处输入图片说明

但没有人玩过这些。有没有可能SQL Server有一些根据环境自动调整权重,或者是基于一些与磁盘子系统的通信?

如果服务器是虚拟机,使用虚拟 SCSI 磁盘,通过光纤链路连接到存储区域网络 (SAN),它是否可能决定可以忽略 CPU 和 I/O 成本?

除了它不能是该服务器中的某些永久环境之外,因为我发现的所有其他查询都正常运行:

在此处输入图片说明

 I/O:       0.0112613
 CPU:      +0.0001
           =0.0113613 (theoretical)
 Operator:  0.0113613 (actual)
Run Code Online (Sandbox Code Playgroud)

什么可以解释服务器不采取:

I/O Cost + Cpu Cost = Operator Cost
Run Code Online (Sandbox Code Playgroud)

在这种情况下正确吗?

SQL Server 2014 SP2。

Sha*_*tor 3

操作员成本不应该至少与构成它的 I/O 或 CPU 成本一样大吗?

这取决于。

很遗憾其他人删除了他们的帖子,因为我提出了类似的想法。

行目标

这不是您根据屏幕截图所经历的情况,但这是计算操作员成本的一个因素。I/O 和 CPU 成本不会缩放,如果行目标未生效,它们将显示每次执行成本。操作员成本确实会缩放以显示行目标。这是一个 I/O 和 CPU 并不完全包含 Operator 成本的实例,需要考虑估计的执行次数。您如何查看这些统计数据取决于您查看的是内部输入还是外部输入。

来源优化器内部:深入了解行目标 Paul White - 2010 年 8 月 18 日 存档

缓冲池使用情况

这可能是影响您的一个因素。

操作的全部成本应该是执行次数乘以 CPU 成本,加上所需 IO 数量的更复杂的公式。IO 的公式表示在访问多个页面后 IO 已经位于内存中的概率。对于大型表,它还对先前访问的页面在再次需要时可能已被逐出的可能性进行建模。子树成本表示当前操作加上馈送到当前操作的所有操作的成本。

资料来源Joe Chang 的执行计划成本模型 - 2009 年 7 月 存档

解决你的问题

我们可以在您的屏幕截图中看到,您在服务器上有一个非常有趣的子树成本,表现不佳。有趣的是,它有更多的内存可供使用,而 CPU 却更少。

上面的信息向我表明,您可能对子树成本有问题,而运算符成本只是一个症状。

...估计子树成本是每个单独操作员的累积成本(按 NodeID 顺序相加)。

资料来源实际执行计划成本,作者:Grant Fritchey - 2018 年 8 月 20 日 存档

我想答案就在这几句话中:

IO 的公式表示在访问多个页面后 IO 已经位于内存中的概率。对于大型表,它还对先前访问的页面在再次需要时可能已被逐出的可能性进行建模。

我认为你正在发生什么:

  1. 硬件设置不同。Ram / CPU / Disk,它不一样,它会影响估计。
  2. 物理数据文件。你是怎么复印的?我建议真正复制这一点的唯一方法是使用数据文件进行备份/恢复。
  3. 您是否尝试清除缓存然后强制重新编译?我想知道这会导致什么结果。

否则,我希望看到估计的和实际的查询计划,以更深入地了解正在发生的情况。

重要的是,如果您在生产中运行此程序而不了解会发生什么并且没有对此进行规划,这将会造成伤害(您可能会被解雇)。这就是我清除缓存以通过重新编译再次测试的方法。


刷新或清除 SQL Server 缓存的不同方法作者:Bhavesh Patel - 2017 年 3 月 31 日 存档

  • DBCC FREESYSTEMCACHE
  • DBCC FREESESSIONCACHE
  • DBCC FREEPROCCACHE