AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 250474
Accepted
Ian Boyd
Ian Boyd
Asked: 2019-10-08 13:35:11 +0800 CST2019-10-08 13:35:11 +0800 CST 2019-10-08 13:35:11 +0800 CST

运算符成本不应该至少与构成它的 I/O 或 CPU 成本一样大吗?

  • 772

我在一台服务器上有一个查询,优化器估计其成本为 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)

这与使用 FULLSCAN 更新的统计数据一样。

在另一台服务器上试试

所以我尝试在另一台服务器上。我得到了相同查询的估计值,其中包含生产数据库的最新副本,以及最新的统计信息(WITH FULLSCAN)。

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

服务器乙:

| 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)

我不明白的是,为什么这两台服务器的数据库副本几乎相同,都具有最新的统计信息,而且都是 SQL Server 2014:

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

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

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

仔细一看,好像“运营商”的估计是错误的

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

如果我强制执行聚簇索引扫描,并查看两台服务器上的估计扫描操作,我会突然想到:

在此处输入图像描述

| 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   |

服务器A上的操作员成本太低了。

  • I /O成本合理
  • CPU成本合理
  • 但总的来说,运营商的总体成本低了 4 个数量级。

这就解释了为什么它错误地选择了糟糕的执行计划;它的运营成本很低。服务器 B 正确地找出了它,并避免了聚簇索引扫描。

不是operator = cpu + io吗?

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

operatorCost >= max(cpuCost, ioCost)

事实上它通常是:

operatorCost = cpuCost + ioCost

那么这是怎么回事?

什么可以解释服务器决定 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

当您这样做时,运算符成本最终可能会低于 CPU+I/O 成本:

在此处输入图像描述

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

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

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

在此处输入图像描述

 I/O:       0.0112613
 CPU:      +0.0001
           =0.0113613 (theoretical)
 Operator:  0.0113613 (actual)

什么可以解释服务器不占用:

I/O Cost + Cpu Cost = Operator Cost

在这个例子中正确吗?

SQL 服务器 2014 SP2。

sql-server sql-server-2014
  • 4 4 个回答
  • 634 Views

4 个回答

  • Voted
  1. Josh Darnell
    2019-10-08T19:35:05+08:002019-10-08T19:35:05+08:00

    行目标

    如果在查询中设置了行目标,这可能会影响行估计和成本计算。

    您可以通过在启用跟踪标志 4138 的情况下运行查询(这将消除行目标的影响)来确认这是否是导致问题的原因。

    缓冲池大小

    如果有更大的可用缓冲池(成本降低的服务器有 14 GB RAM,而另一台机器有 6 GB),一些 I/O 操作的估计成本可以降低。

    您可以通过在计划 XML 中查找“EstimatedPagesCached”来检查此行为的影响。此属性的较高值可以降低可能访问相同数据的执行计划部分的 I/O 成本。

    可用调度程序

    对于并行查询,运算符的 CPU 成本可以减少“# of schedulers / 2”。您可以通过在计划 XML 中查找“EstimatedAvailableDegreeOfParallelism”来检查它的值。

    我提到这一点是因为我注意到“慢速查询”在具有 4 个内核的服务器上运行,而更快的查询在具有 1 个内核的服务器上运行。

    成本是奇怪的和破碎的

    Forrest 在他的博客上谈到了一系列导致成本最终变得毫无意义的不同方式:Percentage Non Grata

    • 4
  2. Best Answer
    Shaulinator
    2019-11-08T11:33:28+08:002019-11-08T11:33:28+08:00

    运算符成本不应该至少与构成它的 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. 您是否尝试清除缓存然后强制重新编译?我想知道这会导致什么结果。

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

    重要的是,如果你在生产环境中运行它而不了解会发生什么并且没有计划它,这将会伤害(你可能会被解雇)。这就是我清除缓存以通过重新编译再次测试的方式。


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

    • DBCC FREESYSTEMCACHE
    • DBCC FREESESSIONCACHE
    • DBCC FREEPROCCACHE
    • 3
  3. dbilid
    2019-11-09T15:22:31+08:002019-11-09T15:22:31+08:00

    对我来说,服务器 A 选择聚集索引扫描似乎是绝对正常的。考虑到优化器的知识,这是最好的决定。奇怪的是服务器B并没有选择相同的。我想我对此有一个答案,但首先让我解释一下为什么优化器必须选择聚簇索引扫描。

    基本原因与它认为 RowNumber 和 TransactionDate 中的值是独立的这一事实有关。正如它在这里所说:

    独立性:除非相关信息可用,否则不同列上的数据分布是独立的。

    查询是

    SELECT MIN(RowNumber)   FROM Transactions WHERE TransactionDate >= '20191002 04:00:00.000'
    

    选项是:1) 开始扫描按 RowNumber 排序的聚簇索引,并在遇到第一个 TransactionDate >= '20191002 04:00:00.000' 的元组时立即停止,这将是查询的实际答案2) 在 TransactionDate 的非聚集索引中搜索值“20191002 04:00:00.000”,然后继续从该值开始扫描索引的其余部分,保持它将找到的最小 RowNumber

    我在这里假设值“20191002 04:00:00.000”是 TransactionDate 列中的最大值之一。实际上,我们假设它大于 95% 的值。鉴于独立性假设,在选项 1 中,可以合理地假设答案将在一次磁盘提取中找到,因为每个扫描的元组都有 5% 的概率是最终答案。在选项2中,搜索特定日期的索引,已经涉及更多的磁盘页面获取,然后我们还必须扫描索引的5%。但实际上,由于两列中的值直接相关,优化器认为最好的选择最终会扫描 95% 的聚簇索引。

    那么,为什么Server B不选择扫描聚集索引呢?显然,在服务器 B 中,聚集索引未按 RowNumber 排序,正如我们从原始问题中发布的计划中看到的那样: 在服务器 A 中,扫描结果是排序的,而在服务器 B 中,它不是

    那么,为什么 CPU_cost + I/O_cost >> 成本。似乎用于聚集索引扫描的 SQL Server 报告了全表 CPU 和 I/O 成本,即使它只是部分扫描,也只报告基于它找到预期值的速度的实际估计值作为总成本。您可以在此处发布的计划中看到完全相同的行为

    至于可以做什么,如果 RowNumber 和 TransactionDate 总是增加,查询可以重写如下:

    SELECT RowNumber FROM Transactions WHERE TransactionDate >= '20191002 04:00:00.000' odrer by TransactionDate LIMIT 1

    • 3
  4. simon coleman
    2019-11-08T10:12:02+08:002019-11-08T10:12:02+08:00

    我们可以假设服务器是真正相同的吗?

    • 处理器数量
    • 内存
    • SQL 服务包级别
    • 数据库兼容级别

    我注意到在修改 sql2012 服务器上的数据库兼容性级别后,为 SP 执行计划返回的查询步骤成本发生了微小变化。(空闲数据库,获得第一个计划 xml,应用选项更改,重新编译 sp,获得第二个计划 xml)计划本身看起来相同。优化器中有更多选项可用,计算方式可能略有不同。如果你在 2x 服务器上有不同的补丁/兼容性,它可能会导致实际计划更加根本不同(错误..)

    • 2

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve