New*_*DBA 0 sql-server execution-plan update sql-server-2014 query-performance
需要一些帮助来理解UPDATE
以下语句之一的缓慢:-
UPDATE TOP (100) xyz
SET xyz.flag = 1
OUTPUT inserted.Rcode, inserted.EDR, inserted.id, abc.EID,abc.CID,abc.ENID,abc.Cdate
FROM dbo.table1 xyz WITH (UPDLOCK, READPAST)
INNER JOIN dbo.table2 abc WITH (NOLOCK)
on xyz.id=abc.id
WHERE xyz.flag = 0
Run Code Online (Sandbox Code Playgroud)
表1有大约。50 万行,表 2 大约有。500 万行
哈希匹配不同流运算符显示黄色警报,消息为:
操作员使用 Tempdb 溢出数据,以溢出级别 4 和 1 个溢出线程执行”
构建残差:
database.dbo.table2.id as abc.id = database.dbo.table2.id as abc.id
我截了屏。不幸的是,由于安全原因,我不能提供更多,甚至不能提供匿名计划。从我的工作站我无法访问互联网,所以我无法让计划浏览器在那里运行。
通常,对于较小的行子集,它在几秒钟内,就像我们刚刚匹配 10K 行或其他东西一样。但是随着数据量的增加,这似乎是一个临界点,应用程序无法承受 1 分钟的运行时间。从 SSMS 我得到 30 秒,但从应用程序我们有平均。约 50 秒 RCSI 处于测试阶段。
我的好计划没有显示 Hash Match Flow Distinct 运算符,如我的屏幕截图所示,而其余计划保持不变。好的一个在 3 秒左右完成。正如所见,该操作员花费了近 16 秒。我们可以通过适当的索引或查询重写来消除它吗?
CREATE TABLE dbo.table1
(
Recid VARCHAR(128) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
Cdate DATETIME NULL,
flag BIT NULL DEFAULT (0),
Rcode INT NULL,
EDR VARCHAR(255) COLLATE SQL_Latin1_general_CP1_CI_AS NULL,
id BIGINT NULL
);
CREATE TABLE dbo.table2
(
ENID BIGINT IDENTITY(1,1) NOT NULL,
EID VARCHAR(50) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
CID VARCHAR(350) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
CDate DATETIME NOT NULL DEFAULT(getdate()),
id BIGINT NOT NULL,
CONSTRAINT PK_ENID PRIMARY KEY (ENID ASC, EID ASC),
);
Run Code Online (Sandbox Code Playgroud)
-- table1
CREATE INDEX ix_Cdate on dbo.table1 (Cdate) WITH (FILLFACTOR=100);
CREATE CLUSTERED INDEX ix_Recid on dbo.table1 (Recid) WITH (FILLFACTOR=80);
-- table2
CREATE INDEX ix_ENID_id on dbo.table2 (ENID,id) WITH (FILLFACTOR=100);
Run Code Online (Sandbox Code Playgroud)
我所做的更改和一些数字:
添加提示OPTION (QUERYTRACEON 4138)
- 平均。执行时间比原来的 50 秒减少了 7 秒,但应用程序团队似乎无权在代码中执行此操作。需要进一步检查这一点。
OPTION (ORDER GROUP)
给出了相同的结果。50秒所以没有改善。
按照建议添加索引:
CREATE INDEX i ON dbo.table2 (id) INCLUDE (CID, CDate);
那里没有太多改进。平均 45 秒和计划类似于此问题中所附的计划(顶级计划)。
在每次测试之前和之后,我确保计划不是从以前的缓存计划中生成的。
对于两个表中相同数量的行,附加更快且没有任何数据或查询更改的计划仍然很快。应用团队全天不断提交上述查询,以通过完成 TOP 100 来完成批次。根据一些小费数字有一个计划更改,以下是好的计划的外观:
编辑:- 一切都没有改变,没有代码更改或添加任何索引,正如我在尝试添加提示 (FORCESEEK) 时所建议的那样,它给了我以下错误
由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。
你有三个主要问题:
table2 中的多行可以匹配 on id
,因此不清楚 table2 中的哪个匹配行应该用于为OUTPUT
子句提供值。聚合用于在 table2 上分组id
并为其他列选择ANY
匹配值。由于行目标,聚合是Flow Distinct。
ANY
在非确定性UPDATE
语句中使用聚合需要非常小心,因为您可能会得到不正确的结果。
问题中没有足够的细节来提出高质量的建议,但是:
CREATE INDEX i ON dbo.table2 (id) INCLUDE (CID, CDate);
OPTION (QUERYTRACEON 4138)
禁用该行的目标,或OPTION (ORDER GROUP)
使用流聚合,而不是哈希的。UPDATE
取决于数据关系。关键点是从源中最多识别出一行与每个目标行匹配。通常,这将涉及唯一索引或约束,或使用ROW_NUMBER
或TOP (1)
。第 2 步可能是必要的,也可能不是。我添加它是为了完整性。
您可能会发现通过以这种形式编写查询更容易可视化问题并调整查询:
UPDATE TOP (100)
xyz WITH (UPDLOCK, READPAST)
SET xyz.flag = 1
OUTPUT
inserted.Rcode, inserted.EDR, inserted.id,
abc.EID, abc.CID, abc.ENID, abc.Cdate
FROM dbo.table1 AS xyz
CROSS APPLY
(
-- At most one source row per target row
SELECT TOP (1)
abc.*
FROM dbo.table2 AS abc
WHERE
abc.id = xyz.id
-- ORDER BY something to choose the one row
) AS abc
WHERE
xyz.flag = 0;
Run Code Online (Sandbox Code Playgroud)
执行计划:
我可能不会打扰 table1 上的过滤索引,但是如果您确实想尝试它,这似乎很合适:
CREATE INDEX i
ON dbo.table1 (Recid)
INCLUDE (id, flag)
WHERE flag = 0;
Run Code Online (Sandbox Code Playgroud)
如果您想继续使用问题中给出的更新语法而不正确解决所有潜在问题,您可能会发现这更快:
UPDATE TOP (100) xyz
SET xyz.flag = 1
OUTPUT inserted.Rcode, inserted.EDR, inserted.id, abc.EID,abc.CID,abc.ENID,abc.Cdate
FROM dbo.table1 xyz WITH (UPDLOCK, READPAST)
INNER JOIN dbo.table2 abc WITH (NOLOCK, FORCESEEK)
on xyz.id=abc.id
WHERE xyz.flag = 0;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
162 次 |
最近记录: |