Kra*_*ter 1 performance sql-server execution-plan sql-server-2014 query-performance
请帮我解释这个声明和计划:
https://www.brentozar.com/pastetheplan/?id=Bysy6YtEV
我们从 Oracle 迁移到 SQL Server,我们有一些非常奇怪的行为。它可能与迁移过程中的问题有关。
我发现很难解释执行计划。两种环境都应该具有相同的结构和索引。统计数据应该是最新的。SQL Server 中的设置:
DB 大小为 600 Gb,16 核,160 Gb 内存。
查询:
SELECT
COUNT ( t_01.rsecondary_objectu ) AS selectExpr
FROM
PIMANRELATION t_01 ,
PITEM t_02 ,
PITEMREVISION t_03
WHERE
( ( ( ( t_01.rprimary_objectu = t_02.puid )
OR ( t_01.rprimary_objectu = t_03.puid )
)
AND
( t_01.rrelation_typeu = 'w8INy241VJFL2B' )
)
AND t_01.rsecondary_objectu = '2yLJkWqiVJFL2B'
)
Run Code Online (Sandbox Code Playgroud)
甲骨文
我们发现问题是相关的,并且取决于数据。如果我选择一个不同的项目在 GUI 中复制(它实际上是一个复制的东西以及应用程序如何执行这些语句),它会立即工作。查询然后一旦工作正常看起来有点不同:https : //www.brentozar.com/pastetheplan/?id=SJo-h2c44
SELECT COUNT ( t_01.rsecondary_objectu ) AS selectExpr FROM PIMANRELATION t_01 , PITEM t_02 , PITEMREVISION t_03 WHERE ( ( ( ( t_01.rprimary_objectu = t_02.puid ) OR ( t_01.rprimary_objectu = t_03.puid ) ) AND ( t_01.rrelation_typeu = 'w8INy241VJFL2B' ) ) AND t_01.rsecondary_objectu IN ('wBHZpD0uVJFL2B' , 'V2PlBGlAVJFL2B' , 'lnHlBGlAVJFL2B' ) )
Run Code Online (Sandbox Code Playgroud)
SQl Server 似乎与我之前提到的(无限运行)完全斗争,同时几乎立即提供了第二个。这太疯狂太奇怪了。就像一个有缺陷的产品。
我对这些语句没有影响,因为它们是由应用程序生成的。
CE 110 / 70 产生相同的计划。即使禁用所有自定义索引并仅保留应用程序建议的索引,它的行为也是相同的。所有主键都是聚集索引。但也许在我们的迁移过程中出现了一些混乱。但这很奇怪。大多数东西运行良好,但主题查询是极端的。我让它在 SQL Server 中运行了 45 分钟。它根本没有完成。
另一个例子。一旦创建了最高评级的索引,我们的 Prod 环境将无法使用:
索引:CREATE INDEX EN_PIPRELEASESTATUS_1 ON [TCEUP01].[dbo].[PRELEASESTATUS] ([pname], [pdate_released]) INCLUDE ([puid])
此查询的结果:https : //pastebin.com/Ax3qTUjd ===> 耗时 137.142 秒
我们的测试环境,应该是一样的,但表现不同:https : //pastebin.com/0PTZTJpr ===>花了 3.884 秒
有问题的计划如下所示:https : //www.brentozar.com/pastetheplan/?id=Skvy0qRNE
Joe*_*ish 10
Oracle 优化器使用OR 扩展来提高查询效率。从文档中引用:
在 OR 扩展中,优化器将带有包含 OR 运算符的 WHERE 子句的查询转换为使用 UNION ALL 运算符的查询。
数据库可以出于各种原因执行 OR 扩展。例如,它可以启用更有效的访问路径或避免笛卡尔积的替代连接方法。
您可以将新查询视为这样编写:
SELECT
(
SELECT
COUNT ( t_01.rsecondary_objectu ) AS selectExpr
FROM
PIMANRELATION t_01
INNER JOIN PITEMREVISION t_03 ON t_01.rprimary_objectu = t_03.puid
CROSS JOIN PITEM t_02
WHERE
t_01.rrelation_typeu = 'w8INy241VJFL2B'
AND t_01.rsecondary_objectu = '2yLJkWqiVJFL2B'
)
+
(
SELECT
COUNT ( t_01.rsecondary_objectu ) AS selectExpr
FROM
PIMANRELATION t_01
INNER JOIN PITEM t_02 ON t_01.rprimary_objectu = t_02.puid
CROSS JOIN PITEMREVISION t_03
WHERE
t_01.rrelation_typeu = 'w8INy241VJFL2B'
AND t_01.rsecondary_objectu = '2yLJkWqiVJFL2B'
AND LNNVL(t_01.rprimary_objectu = t_03.puid)
)
from dual;
Run Code Online (Sandbox Code Playgroud)
现在查询的两个部分都有一个相等条件,因此 Oracle 可以使用索引为两者执行有效的嵌套循环连接。它仍然需要对查询的两个部分进行交叉联接,但与在同一查询中进行两次交叉联接相比,中间结果集的大小显着减少。例如,如果PIMANRELATION
有1个相关行和PITEMREVISION
和PITEM
均具有一百万行,然后,如果你越过它们连接在一起你一万亿行。但是,如果您将查询拆分,那么您最终只会得到一百万行。
SQL Server 查询优化器有一个规则可以转换OR
为UNION ALL
: SelToIdxStrategy
。没有这方面的文档,我能找到的唯一参考就是这个答案。但是,该规则将不适用于这种情况。相反,您会得到两个只能通过嵌套循环连接实现的交叉连接。对于 中的每个相关行PIMANRELATION
,SQL Server 将交叉联接到 中的所有行PITEM
,然后交叉联接到 中的所有行PITEMREVISION
,最后将过滤掉之后的行。您很容易最终会过滤掉数万亿行。
我有一个坏消息要告诉你。如果您确实无法更改查询文本的任何部分,并且您需要该查询执行良好,那么 SQL Server 可能不是适合您的应用程序的平台。数据库有不同的优点和缺点,您可能需要更改查询以适应这些差异。
这是仅基于查询结构的建议:
在开始对执行计划、表统计和直方图、基数估计、隔离级别、内存设置和许多其他可能的问题原因和解决方法进行追查之前,请考虑它可能是应用程序中产生询问。
我的推理很简单:如果它看起来像垃圾并且是由 ORM 生成的,那么它很可能是垃圾。
我建议你检查:
如果查询在原始 Oracle 数据库中运行完全相同,或者当目标是 SQL Server 数据库时,生成它的地方(以及 ORM/应用程序)会稍微或更多地更改它。
有哪些测试表明查询与要应用的业务逻辑/要求一致。应用程序是否有这样的测试并且它们是否在(Oracle 和 SQL Server)环境中都成功通过?
我的观点是,在确定查询没有正确性问题之前,追逐性能问题是没有意义的。
详细地说,该查询几乎没有意义。该OR
条件-这是二级(唯一的连接过滤器PITEM
和PITEMREVISION
)表-本质上引入了一个交叉连接(这幸运的是,你已经踢在甲骨文的一些优化功能,但在SQL Server中见没有做。乔Obbish的答案了发生的事情的详细解释)。
为了更清楚,请考虑以下与您的查询等效的查询:
WITH
t_01 AS
( SELECT rprimary_objectu
FROM PIMANRELATION
WHERE rrelation_typeu = 'w8INy241VJFL2B'
AND rsecondary_objectu = '2yLJkWqiVJFL2B'
),
count_items AS
( SELECT COUNT(*) AS a
FROM t_01
JOIN PITEM t_02
ON t_01.rprimary_objectu = t_02.puid
),
count_revisions AS
( SELECT COUNT(*) AS b
FROM t_01
JOIN PITEMREVISION t_03
ON t_01.rprimary_objectu = t_03.puid
),
count_all_items AS
( SELECT COUNT(*) AS aa
FROM PITEM
),
count_all_revisions AS
( SELECT COUNT(*) AS bb
FROM PITEMREVISION
)
SELECT
(a * bb) + (b * aa) - (a * b) AS selectExpr
FROM
count_items, count_all_items,
count_revisions, count_all_revisions ;
Run Code Online (Sandbox Code Playgroud)
你明白为什么上述内容毫无意义吗?请注意count_all_items
和count_all_revisions
计算如何计算这两个表中的所有行。我看不到这背后的业务逻辑。
(以上当然会更有效,因为它不做任何交叉连接,而是将表扫描分开并将计算转移到主查询中的简单乘法。无论优化器变得多么聪明,总是有限制他们可以提供的可能的转换和优化。)
可能使用这种奇怪计数的唯一情况是,是否在查询之后立即检查计数是否为0
或>= 1
- 某些 ORM 似乎比更有效的EXISTS
方法更喜欢的东西。在这种情况下,计数是 5 还是 500 万并不重要,因为目标是查找两个表中的任何一个中是否存在相关行。
归档时间: |
|
查看次数: |
763 次 |
最近记录: |