Luk*_*til 7 performance sql-server parallelism azure-sql-database row-level-security
我发现本地计算机上的查询计划和 Azure SQL 上的查询计划之间存在奇怪的差异。我正在尝试实现行级安全性,其中我从 SESSION_CONTEXT 读取用户标识符,然后在 TVF 中检查用户是否具有访问权限。
在我的本地计算机上 - SQL Server 2019 Developer Edition,兼容级别 150 的数据库,查询计划符合预期。但是当我在兼容级别为 150 的 Azure DB 上运行它时,我只能获得带有NonParallelPlanReason="NonParallelizableIntrinsicFunction"
. 我尝试了超大规模数据库以及弹性池中的数据库,两个数据库的结果相同。
您可以使用以下代码重现该内容:
CREATE TABLE Users (
UserIdentifier nvarchar(100) PRIMARY KEY CLUSTERED
)
INSERT INTO Users (UserIdentifier) VALUES ('MyUserIdentifier')
CREATE TABLE TableWithRLS (
Id int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
DataColumn nvarchar(100) NULL
)
INSERT INTO TableWithRLS (DataColumn)
SELECT TOP 10000000 A.[name] FROM sys.all_columns AS A
CROSS JOIN sys.all_columns AS B
CROSS JOIN sys.all_columns AS C
CREATE OR ALTER FUNCTION CheckAccess (@userIdentifier varchar(100))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT TOP 1 1 AS HasAccess FROM dbo.Users WHERE UserIdentifier = @userIdentifier
EXEC sp_set_session_context N'UserIdentifier', N'MyUserIdentifier', 1
-- This query gets always non-parallel query plan on Azure
SELECT MAX(DataColumn) FROM TableWithRLS AS X
CROSS APPLY CheckAccess(CAST(SESSION_CONTEXT(N'UserIdentifier') AS nvarchar(100)))
Run Code Online (Sandbox Code Playgroud)
当我首先从会话上下文中选择值到变量中时,即使在 Azure 中,它也会生成可并行的查询计划。
DECLARE @userIdentifier AS nvarchar(100) = CAST(SESSION_CONTEXT(N'UserIdentifier') AS nvarchar(100))
SELECT MAX(DataColumn) FROM TableWithRLS AS X
CROSS APPLY CheckAccess(@userIdentifier)
Run Code Online (Sandbox Code Playgroud)
不幸的是我不能这样做(或者至少我不知道如何做到这一点),因为我需要一个内联 TVF。
来自 Azure 的查询计划:https://www.brentozar.com/pastetheplan/?id =ByxZm45e9
本地查询计划:https://www.brentozar.com/pastetheplan/? id=BylHXV9lc
Azure 中的 SESSION_CONTEXT 实现是否存在任何可能导致此问题的差异?或者有人有任何其他想法可能是什么问题吗?
Azure 中的 SESSION_CONTEXT 实现是否存在任何可能导致此问题的差异?
是的。尽管 Azure SQL 数据库和 SQL Server 是基于通用代码库构建的,但其中一个领先于另一个的情况并不罕见。尽管微软在过去几年进行了营销,但并不总是 Azure 版本处于领先地位。
SESSION_CONTEXT
使用时禁止并行计划是当前在 Azure 中启用的功能标志 ( DisableSessionContextParallelPlan ),没有任何方法可以将其关闭。可以在 SQL Server 上使用未记录的跟踪标志 11042 在查询、会话、全局和启动级别启用它。
使用 Stack Overflow 演示数据库(任何并行查询都可以):
EXECUTE sys.sp_set_session_context
@key = N'key',
@value = 123,
@read_only = 1;
SELECT COUNT_BIG(*)
FROM dbo.Badges AS B
WHERE B.UserId = CONVERT(integer, SESSION_CONTEXT(N'Key'))
OPTION (QUERYTRACEON 11042);
Run Code Online (Sandbox Code Playgroud)
SQL Server 上的查询是并行的,没有跟踪标志。
这些选项通常添加到预览功能中,作为对罕见错误情况或存在安全风险的响应。
SQL Server 2019 CU14 中发布了在并行计划中使用时出现错误结果的错误修复。SESSION_CONTEXT
CU16 的文档中报告了修复存在问题:
SQL Server 2019 CU14 引入了修复程序来解决内置 SESSION_CONTEXT 返回的并行计划中的错误结果。但是,此修复可能会在重置 SESSION 以供重用时导致访问冲突转储文件。为了缓解此问题,您可以禁用原始修复,并禁用内置 SESSION_CONTEXT 的并行性以避免错误结果。为此,请使用以下跟踪标志:
11042 - 此跟踪标志禁用内置 SESSION_CONTEXT 的并行性。
9432 - 此跟踪标志禁用 SQL Server 2019 CU14 中引入的修复。
Microsoft 正在致力于解决此问题,并将在未来的 CU 中提供。