我注意到,当 tempdb 事件溢出(导致查询缓慢)时,对于特定连接,行估计通常会偏离。我已经看到溢出事件发生在合并和散列连接中,它们通常将运行时间增加 3 到 10 倍。这个问题涉及如何在减少溢出事件机会的假设下改进行估计。
实际行数 40k。
对于此查询,计划显示错误的行估计(11.3 行):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Run Code Online (Sandbox Code Playgroud)
对于此查询,计划显示了良好的行估计(56k 行):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile); …
Run Code Online (Sandbox Code Playgroud) 在使用 try catch 和动态 sql 的存储过程中发出备份命令时,与直接运行备份命令相比,错误消息非常普遍。
在 SP 中尝试/捕获:
begin try
execute sp_executesql @sql; -- a backup command
end try
begin catch
print ERROR_MESSAGE(); -- save to log, etc.
end catch
Run Code Online (Sandbox Code Playgroud)
结果是
50000:usp_Backup:117: BACKUP DATABASE 异常终止。
whearea 发出原始命令:
backup DATABASE someDb to disk...
Run Code Online (Sandbox Code Playgroud)
结果更好的细节:
查找错误 - SQL Server 数据库错误:文件“H:\FolderName\Filename.bak:”112(磁盘空间不足。)发生不可恢复的 I/O 错误。
有没有办法将这些细节捕获到存储过程中的变量中(记录,传回调用者,重试逻辑)?似乎细节正在通过消息通道传递,但我希望它们在 SP 中可用。
标量 UDF 强制执行整体串行计划,这是有据可查的。
给定大量行进入管道中必须计算 UDF 的点,为什么引擎不能将它们分配给处理器?如果 UDF 中没有状态,那么顺序应该无关紧要。
有人声称 UDF 是一个必须使用光标的黑匣子。我可以看到,对于在迭代之间维护某些状态但似乎应该可以并行化的情况,用户游标无法在 SP 中并行化。
解释为什么引擎强制整个计划是串行的,而不仅仅是 UDF 计算阶段的额外要点。
对并行 UDF 的支持是一个合理的要求吗?
每隔一段时间(例如~几个月),SQL Server 代理每小时作业将开始报告错误 15404 并继续这样做直到被干预。
[298] SQLServer 错误:15404,无法获取有关 Windows NT 组/用户“DOMAIN_NAME\SomeDomainAccount”的信息,错误代码 0x6e。[SQLSTATE 42000] (ConnIsLoginSysAdmin)
有时,在手动重新启动 SQL Server 引擎和 SQL Server 代理服务后会立即发生第一次失败。可以通过重新启动机器来清除问题。
作业所有者是错误消息中列出的名称,并且是 SQL Server 管理员。
SQL Server 引擎服务帐户看起来是一个服务帐户(我相信它是默认安装帐户(比通用 NetworkService 好一个档次,以防止引擎/代理实例之间的干扰):
NT Service\MSSQL$INSTNAME
Run Code Online (Sandbox Code Playgroud)
如果作业总是失败,那将是一回事,但由于作业在重新启动后成功,这让我认为像这样的服务帐户应该可以正常工作,并且存在一些 A/D 计时问题或可能存在错误。当 IT 被问及 A/D 配置时,回答通常是“没有任何变化”。
使用 Microsoft SQL Server 2012 (SP3) (KB3072779) - 11.0.6020.0 (X64)。
给定一个表和索引:
create table [User].[Session]
(
SessionId int identity(1, 1) not null primary key
CreatedUtc datetime2(7) not null default sysutcdatetime())
)
create nonclustered index [IX_User_Session_CreatedUtc]
on [User].[Session]([CreatedUtc]) include (SessionId)
Run Code Online (Sandbox Code Playgroud)
以下每个查询的实际行数为 310 万,估计行数显示为注释。
当这些查询在 View 中提供另一个查询时,由于 1 行估计,优化器选择循环连接。 如何在此基础级别改进估计以避免覆盖父查询连接提示或求助于 SP?
使用硬编码日期效果很好:
select distinct SessionId from [User].Session -- 2.9M (great)
where CreatedUtc > '04/08/2015' -- but hardcoded
Run Code Online (Sandbox Code Playgroud)
这些等效查询与视图兼容,但都估计为 1 行:
select distinct SessionId from [User].Session -- 1
where CreatedUtc …
Run Code Online (Sandbox Code Playgroud) 这个问题的一个例子表明,SQL Server 将选择全索引扫描来解决这样的查询:
select distinct [typeName] from [types]
Run Code Online (Sandbox Code Playgroud)
其中 [typeName] 有一个非聚集的、非唯一的升序索引。他的示例有 200M 行,但只有 76 个唯一值。在这种密度下,搜索计划似乎是更好的选择(约 76 次二进制搜索)?
他的情况可以正常化,但问题的原因是我真的想解决这样的问题:
select TransactionId, max(CreatedUtc)
from TxLog
group by TransactionId
Run Code Online (Sandbox Code Playgroud)
上有一个索引(TransactionId, MaxCreatedUtc)
。
使用标准化源 (dt) 重写不会改变计划。
select dt.TransactionId, MaxCreatedUtc
from [Transaction] dt -- distinct transactions
cross apply
(
select Max(CreatedUtc) as MaxCreatedUtc
from TxLog tl
where tl.TransactionId = dt.TransactionId
) ca
Run Code Online (Sandbox Code Playgroud)
仅将 CA 子查询作为标量 UDF 运行确实显示了 1 次搜索的计划。
select max(CreatedUtc) as MaxCreatedUtc
from Pub.TransactionLog
where TransactionID = @TxId;
Run Code Online (Sandbox Code Playgroud)
在原始查询中使用该标量 UDF 似乎可行,但会失去并行性(UDF 的已知问题): …
从SQL Server 2008 开始的From 子句文档简要提到了 3 个连接提示及其基本机制:
然而,似乎没有太多关于何时可能需要使用它们的信息。
似乎它们可以与散列、循环和合并结合使用,这些已经被理解为这个问题的目的。
文档中的相关部分:
对于 SQL 数据仓库和并行数据仓库,这些联接提示适用于两个分布不兼容的列上的 INNER 联接。它们可以通过限制查询处理期间发生的数据移动量来提高查询性能。SQL 数据仓库和并行数据仓库的允许连接提示如下:
REDUCE
减少要为联接右侧的表移动的行数,以使两个分布不兼容的表兼容。REDUCE 提示也称为半连接提示。REPLICATE
将连接左侧表的连接列中的值复制到所有节点。右侧的表连接到这些列的复制版本。REDISTRIBUTE
强制将两个数据源分布在 JOIN 子句中指定的列上。对于分布式表,Parallel Data Warehouse 会执行shuffle move。对于复制表,并行数据仓库将执行修剪移动。要了解这些移动类型,请参阅并行数据仓库产品文档中“了解查询计划”主题中的“DMS 查询计划操作”部分。当查询计划使用广播移动来解决分发不兼容的连接时,此提示可以提高性能。
join sql-server hints sql-server-pdw azure-sql-data-warehouse
我们有一个针对单项查询优化的视图(200 毫秒无并行性):
select *
from OptimizedForSingleObjectIdView e2i
where ObjectId = 3374700
Run Code Online (Sandbox Code Playgroud)
它也适用于一小组静态 ID(~5)。
select *
from OptimizedForSingleObjectIdView e2i
where ObjectId in (3374700, 3374710, 3374720, 3374730, 3374740);
Run Code Online (Sandbox Code Playgroud)
但是,如果对象来自外部源,那么它会生成一个缓慢的计划。执行计划显示视图部分的执行分支忽略了 ObjectId 上的谓词,而在原始情况下,它使用它们来执行索引查找。
select v.*
from
(
select top 1 ObjectId from Objects
where ObjectId % 10 = 0
order by ObjectId
) o
join OptimizedForSingleObjectIdView v -- (also tried inner loop join)
on v.ObjectId = o.ObjectId;
Run Code Online (Sandbox Code Playgroud)
我们不希望投资于“双重”优化非奇异情况的视图。相反,我们“寻求”的解决方案是对每个对象重复调用一次视图,而无需求助于 SP。
大多数情况下,以下解决方案会逐行调用视图。但是这次不是,甚至不是只有 1 个对象:
select v.*
from
(
select top 1 ObjectId
from Objects …
Run Code Online (Sandbox Code Playgroud) 我可以理解,如果我加入“单独快速”的单个查询,组合可能会变慢,因为默认执行计划可能不是最佳的。但是,当我知道一个查询的行数非常少时,我想我应该能够使用提示来控制连接。
select cj.a, cv.b
from (select distinct a from complexJoin) cj -- 2 rows
inner loop join complexView cv
on cj.a = cv.a
order by cj.a, cv.b
Run Code Online (Sandbox Code Playgroud)
如果 cj <1s 和 cv <1s 期望这是 <~2s 但使用任何提示(合并/散列/循环)通常> 1分钟。
我还尝试使用 CROSS APPLY,因为文档声称内部选择对于每个外部行只执行一次。该查询比手动运行两次内部查询需要大约 100 倍的时间,所以也许我不理解文档。
select cj.a, cv.b
from (select distinct a from complexJoin) cj -- 2 rows
cross apply (select * from complexView
where a = cj.a) cv
order by cj.a, cv.b
Run Code Online (Sandbox Code Playgroud)
如果我用“cj”的结果填充临时表,然后加入 _with_no_hint_ 或使用交叉应用,它会很快,但我真的必须求助于它吗?如果我使用临时表并尝试“任何”连接提示(循环/合并/散列),它会很慢,所以这可能是一个关键点。
我不相信深入查询计划的深度(两者都是复杂的开始)是解决一般问题所必需的:我只想保证隔离而不诉诸临时表 - 这真的不可能?
我正在尝试改进此子查询(较大查询的)的行估计。估计显示 1266 行。实际是 117k 行。这个特定的属性(EAV 模式)只定义了两个值(2 和 3):
declare @pPropVal smallint = 2;
select Value, ObjectId
from Oav.ValueArray PropName
where PropName.PropertyId = 897
and PropName.Value = @pPropVal
option (recompile)
Run Code Online (Sandbox Code Playgroud)
查询计划按预期显示对 PropertyId 和 Value 索引 IX_ValueArray_PropValObj 的正确搜索谓词。
( A ) 作为改进行估计的尝试,添加了一个额外的统计数据,使行估计略微增加到 3041:
create statistics [ST_SomePropertyName] ON [Oav].[ValueArray](PropertyId, Value, ObjectId)
where
(
PropertyId = 897
and [Value] is not null
)
with fullscan
Run Code Online (Sandbox Code Playgroud)
直方图显示单行。HI 键只是 PropertyId(第一列),根据我的理解,它不是那么有用,它使用的是密度信息。
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
897 0 196026 0 1
All density Average Length Columns
1 …
Run Code Online (Sandbox Code Playgroud) sql-server ×10
optimization ×4
join ×3
hints ×2
view ×2
backup ×1
cross-apply ×1
distinct ×1
eav ×1
functions ×1
group-by ×1
index ×1
jobs ×1
merge ×1
parallelism ×1
statistics ×1