Yan*_*ton 10 sql-server statistics azure-sql-database cardinality-estimates
我偶然发现了 SQL Azure 中的一个问题,其中Sort-operator 由于行估计不佳而溢出到 TempDB 中。
TenantId-column 对每个租户的表进行分区FILTER PREDICATE上述所有查询添加一个TenantIdIndex Seeks检索由于租户之间的行数差异很大,基数估计器产生的估计值非常低。这与两个内部连接相结合,进一步减少了估计,使得实际产生 3600 行的查询预计只产生 3。这是 3 个数量级的下降。
Filtered Statistics为那些产生大量行的键值定义,作为对 CE 的额外提示。OPTION ( RECOMPILE )适用于某些谓词,但不适用于TenantId通过上述安全策略注入的谓词。INNER JOINs更改为s 可以LEFT OUTER JOIN改善错误连接估计,但由于我们使用实体框架,我更喜欢不需要更改查询的解决方案。注意:显然,如果唯一的方法是重写查询,那么这就是我们将要走的路线。我欢迎您的任何想法,谢谢!
排序运算符用于分页。我实际上不想检索所有行。所以简而言之,排序需要发生在数据库中(而不是在应用程序中)。
另外,要明确的是,这里的问题不是 EF 生成的查询。这是一个简单的查询,带有许多INNER/LEFT OUTER连接和一些过滤谓词。它不是典型的 5000 行 SQL 语句,没有人能看懂。
似乎细节查找是通过hash match它丢失索引的排序顺序完成的。此外,订购的列是用户定义的,并根据用例进行更改,因此为了避免排序,我需要每列的索引,它们可能会排序。
行级安全策略
CREATE FUNCTION Security.myAccessPredicate(@TenantId nvarchar(128))
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN SELECT 1 AS accessResult
WHERE @TenantId = CAST(SESSION_CONTEXT(N'TenantId') AS nvarchar(128))
OR CAST(SESSION_CONTEXT(N'TenantId') AS nvarchar(128)) LIKE 'ReservedTenantIdForCrossTenantOperations'");
CREATE SECURITY POLICY Security.mySecurityPolicy
ADD FILTER PREDICATE Security.myAccessPredicate(TenantId) ON Schema1.Object10,
ADD BLOCK PREDICATE Security.myAccessPredicate(TenantId) ON Schema1.Object10,
... And all the other tables
Run Code Online (Sandbox Code Playgroud)
主表索引 DDL
CREATE NONCLUSTERED INDEX [IX_Object10] ON Schema1.Object10
(
[TenantId] ASC,
[Discriminator] ASC,
[IsDeleted] ASC,
[CreatedAt] ASC
)
INCLUDE (
[Id],
[Column3],
...
[Column37])
WITH (
PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, SORT_IN_TEMPDB = OFF
, DROP_EXISTING = OFF
, ONLINE = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
)
Run Code Online (Sandbox Code Playgroud)
查询(匿名)
EXEC sp_set_session_context @key=N'TenantId', @value=N'TenantId123'
SELECT *
FROM ( SELECT
Object2.Column2 AS Column2,
Object2.Column3 AS CreatedAt,
-- Removed some extra columns from Object2
Object3.Column4 AS Column3,
Object4.Column4 AS Column5,
Object5.Column4 AS Column6,
Object6.Column7 AS Column7,
Object7.Column10 AS Column10,
Object8.Column10 AS Column11,
Object9.Column4 AS Column20,
CASE WHEN (Variable1 = ?) THEN Object2.Column26 ELSE cast(? as decimal(18)) END AS Column21,
CASE WHEN (Variable2 = ?) THEN Object2.Column27 ELSE ? END AS Column22,
CASE WHEN (Variable3 = ?) THEN Object2.Column28 ELSE ? END AS Column23,
CASE WHEN (Variable4 = ?) THEN Object2.Column29 END AS Column24
FROM Schema1.Object10 AS Object2
INNER JOIN Schema1.Object11 AS Object3 ON Object2.Column30 = Object3.Column2
INNER JOIN Schema1.Object12 AS Object4 ON Object2.Column31 = Object4.Column2
LEFT OUTER JOIN Schema1.Object10 AS Object5 ON (Object2.Column32 = Object5.Column2) AND (Object5.Column33 = ?)
LEFT OUTER JOIN Schema1.Object10 AS Object6 ON (Object2.Column34 = Object6.Column2) AND (Object6.Column33 = ?)
LEFT OUTER JOIN Schema1.Object13 AS Object7 ON Object2.Column35 = Object7.Column2
LEFT OUTER JOIN Schema1.Object13 AS Object8 ON Object2.Column36 = Object8.Column2
LEFT OUTER JOIN Schema1.Object14 AS Object9 ON Object2.Column37 = Object9.Column2
WHERE (Object2.Discriminator = N'SomeCategory') AND (0 = Object2.IsDeleted)
) AS Object1
ORDER BY Object1.CreatedAt DESC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY
Run Code Online (Sandbox Code Playgroud)
查询计划(匿名):https : //pastebin.com/msjPQ6Vs
估计的
实际情况
主表 (Schema1.Object10 AS Object2) 从索引查找返回 3600 多行实际行,但 CE 估计为 381 行。然后到 Schema1.Object11 和 Schema1.Object12 的两个内部联接进一步将估计值减少到 2.9 个估计行数。这对我来说毫无意义。内连接发生在具有外键约束的非空 id 列上,因此应该不可能找不到匹配项。
排序运算符工具提示:
实际时间,而排序运算符之前的累积时间为 16 毫秒
| 归档时间: |
|
| 查看次数: |
540 次 |
| 最近记录: |