Linux 上的 MSSQL Server 2022:sys.* 表对于非 db_owner 用户来说非常慢

fin*_*nal 5 sql-server linux sql-server-2022

对于非sys.columns.​​sys.indexessys.tablesdb_owner

要重现,请在 Linux 上使用 SQL Server 2022(我使用的是 16.0.4035,这是撰写本文时的最新版本)并执行以下命令。它创建一个虚拟数据库,其中包含 500 个表,每个表有 10 列,这样sys.columns该数据库就会获取大约 5000 条记录。如果您手头的数据库总数超过 1000 列,则可以跳过创建虚拟数据库。

CREATE DATABASE SLOWSYSTABLES
GO

USE SLOWSYSTABLES

DECLARE @i int
DECLARE @createTable nvarchar(max)
SET @i = 0
WHILE (@i < 500)
BEGIN
    SET @createTable = 'CREATE TABLE dummy_' + CAST(@i as nvarchar) + '(Col1 int not null identity(1,1) primary key, Col2 int, Col3 int, Col4 int, Col5 int, Col6 int, Col7 int, Col8 int, Col9 int, Col10 int)'
    SET @i = @i + 1
    exec sp_executesql @createTable
END

CREATE LOGIN slowsystables_reader WITH password='Asdfasdf1', check_policy=off
CREATE USER slowsystables_reader FOR LOGIN slowsystables_reader
ALTER ROLE db_datareader ADD MEMBER slowsystables_reader
Run Code Online (Sandbox Code Playgroud)

现在,我执行以下查询:

SET STATISTICS TIME on
USE SLOWSYSTABLES
SELECT * FROM sys.columns
Run Code Online (Sandbox Code Playgroud)

sa在我的系统(Intel i7-10700k、32GB RAM、M.2 SSD 磁盘)上,具有权限的用户或任何具有权限的用户的执行时间db_owner约为 100 毫秒。但如果slowsystables_reader运行查询,执行时间为13秒。SQL Server 在整个 13 秒内使用 100% 的可用 CPU。

 (5048 rows affected)

 SQL Server Execution Times:
   CPU time = 7138 ms,  elapsed time = 13194 ms.
Run Code Online (Sandbox Code Playgroud)

slowsystables_reader如果将用户添加到数据库角色,该问题就会消失db_owner;如果再次从角色中删除该用户,该问题会立即重新出现。

Linux 上的 SQL Server 2019 或 Windows 上的 SQL Server 2019/2022 中不存在此问题。

我可以在新安装的 Ubuntu 20.04 Server 或 Debian bullseye 上新安装的 SQL Server 2022 上重现此内容。我还在两个不同的系统上进行了尝试(两个虚拟机,一个 Hypver-V,一个支持 KVM,但具有不同的底层硬件)。SQL Server 执行过的唯一查询就是上面的查询。

我尝试更新系统表统计信息,但没有雪茄。

遗憾的是,我没有 Microsoft 支持计划,因此无法向 Microsoft 提出支持案例。

我在执行计划中看到 SQL Server 几乎 100% 的时间都花在该has_access函数上,据我所知,has_access如果用户是 则“已知”会短路db_owner因此,这与针对 sys.schemas 和 sys.synonyms 的查询对于一个用户来说运行非常慢是相同的根本情况,但据我所知,我绝对没有影响has_access,而且我无法真正重写/优化SELECT * FROM sys.columns

SQL Server 2019 和 2022 之间以及 Windows 和 Linux 之间的巨大差异正是这一点的奇怪之处。我们之所以注意到这个问题,是因为SELECT * FROM sys.columns2019 年运行得非常好,但在 2022 年突然超时了。

war*_*yen -2

USE [master]
GO

-- Create a test database
IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = 'SLOWSYSTABLES')
BEGIN
    CREATE DATABASE [SLOWSYSTABLES]
END
GO

USE [SLOWSYSTABLES]
GO

-- Create a dummy user
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = 'slowsystables_reader')
BEGIN
    CREATE LOGIN [slowsystables_reader] WITH PASSWORD = 'Asdfasdf1', CHECK_POLICY = OFF
    CREATE USER [slowsystables_reader] FOR LOGIN [slowsystables_reader]
    ALTER ROLE [db_datareader] ADD MEMBER [slowsystables_reader]
END
GO

-- Create a table with identity column and 10 additional columns
IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = 'dummy')
BEGIN
    CREATE TABLE [dbo].[dummy] (
        [Col1] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
        [Col2] INT,
        [Col3] INT,
        [Col4] INT,
        [Col5] INT,
        [Col6] INT,
        [Col7] INT,
        [Col8] INT,
        [Col9] INT,
        [Col10] INT
    )
END
GO

-- Populate the table with dummy data
DECLARE @i INT = 1
WHILE (@i <= 500)
BEGIN
    INSERT INTO [dbo].[dummy] ([Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Col10])
    VALUES (@i, @i, @i, @i, @i, @i, @i, @i, @i)
    SET @i = @i + 1
END
GO

-- Update statistics
EXEC sp_updatestats
GO

-- Query sys.columns
USE [SLOWSYSTABLES]
GO

SET STATISTICS TIME ON

-- Run the query as slowsystables_reader
EXECUTE AS USER = 'slowsystables_reader'

SELECT * FROM sys.columns

REVERT

SET STATISTICS TIME OFF
Run Code Online (Sandbox Code Playgroud)

该脚本包含命令 EXEC sp_updatestats,用于更新统计信息以提高查询性能。仅当测试数据库“SLOWSYSTABLES”尚不存在时才创建它。