奇怪的 MS SQL Server 行为/性能问题

Posted

技术标签:

【中文标题】奇怪的 MS SQL Server 行为/性能问题【英文标题】:Strange MS SQL Server behavior / Performance issue 【发布时间】:2020-08-04 09:00:27 【问题描述】:

对于一个非常简单的查询,我有一个奇怪的响应时间行为。到目前为止的所有研究都没有显示出任何帮助甚至暗示,这里可能出了什么问题。

我的数据库中有一个表,用于存储签名对象链。我有不同的链,除了要签名的数据之外,还有一些元数据字段。简化的 CREATE 语句如下所示:

CREATE TABLE [dbo].[SomeTable](
    [id] [bigint] NOT NULL,
    [user] [nvarchar](255) NULL,
    [someType] [nvarchar](255) NULL,
    [someId] [bigint] NULL,
    [someDescription] [nvarchar](255) NULL,
    [processName] [nvarchar](255) NULL,
    [taskId] [nvarchar](255) NULL,
    [data] [varbinary](max) NULL,
    [signature] [varbinary](max) NULL,
    [signedFormat] [nvarchar](255) NULL,
    [keyVersion] [int] NULL,
    [predecessorId] [bigint] NULL,
    [chainName] [nvarchar](255) NULL,
    [date] [bigint] NULL,
 CONSTRAINT [PK_SomeTable] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

当然有索引:

主键是“id” 一个 ASC 和一个 DESC 在“日期” 关于“链名”的一个 关于“someDescription”的一个 “someId”上的一个

所有都是非唯一的、非集群的,当然,除了主要的。

与此同时,这张桌子变得相当大 - 嗯,至少相对而言。完整的数据库大约有 100 GB 大小,这个表占用了其中的一半。索引空间已经增长到使用 7.5 GB。索引统计信息是最新的。

我正在使用具有 (Hikari-)JDBC-ConnectionPool 的多线程 Java 应用程序写入此表。

所以,让我头疼的是,尝试创建一个新链需要很长时间,尽管它不应该。 我正在使用 javax.persistency 类来封装数据访问。我的应用程序的每个线程都试图在应用程序启动时创建一个新链,并且在应用程序运行期间的两个明确定义的步骤中再次尝试创建一个新链。

封装对该表的访问的类的标准行为是,当尝试向其添加更多条目时,它会尝试确定具有给定名称的链是否已经存在。如果是这样,则加载最后一个条目以继续该链,否则创建一个新条目。此对象用于向此链添加更多条目。

但是,由于我的用例有点特殊,我通过使用唯一名称(包括时间戳和一些随机信息)来确保我尝试创建的链不存在的链名。所以我在启动时为每个线程启动一个新的链,使用它直到输入两个定义明确的步骤之一,这会再次导致新的链。因此,每个线程在其生命周期中恰好创建了三个链。

无论如何,相同的标准行为适用于我对类的使用,因此,应用程序会尝试查找不存在链的最新条目。这应该相当快,因为​​相关列上有索引。这是执行的语句:

select top(1) * from SomeTable s where s.chainName = '<someNotExistingName>' order by s.date, s.id desc

在管理控制台中执行此语句时,速度非常快,不到一秒就返回空结果。 在启动时,每个线程(10-15,视情况而定)几乎同时请求一个新的链。尝试使用控制台模拟这种“并发访问”仍然显示快速响应时间。

但是,在应用程序启动时,这种行为就像有人在询问现有链的头部,结果证明,即使在控制台上执行也需要很长时间。我在控制台上等待了 48 分钟来等待一个这样的查询,这令人难以置信,tbh。 看看这段等待时间的内存消耗情况,好像整个表都加载到 RAM 中,而不仅仅是扫描索引!

我使用 SQL Profiler 监控应用程序的启动,但找不到“错误”或有罪的陈述。也许我使用了错误的分析设置?我正在使用默认模板录制一个会话,另一个使用“持续时间”模板录制。

我已经没有什么想法了,我应该看看什么,是什么导致了这个问题。但我注意到随着桌子变大,情况变得更糟。同时,它需要长达 3 小时(!!)等待线程返回并使其链可用:(

拼命寻找一些提示!

最好的问候, 安德烈亚斯

【问题讨论】:

会不会是参数嗅探?会不会被锁定?或者分享一下执行计划brentozar.com/pastetheplan? “参数嗅探”是什么意思?我不认为锁定是一个问题。我没有在 Analyzer 中看到可能会导致问题的锁定资源。共享计划是一种选择——但是,共享哪个计划?我在控制台中启动的所有语句都非常快。如何从我的应用执行的语句中获取和共享计划? sp_whoisactive 可以选择查看执行计划以及blocked_by_session_id。 parameter sniffing 见网址。 【参考方案1】:

您能否尝试以下查询,因为您的

查询不能使用任何索引查找运算符,索引扫描运算符将读取所有索引页。

select top(1) * from SomeTable s where s.chainName = '<someNotExistingName>' order by s.date, s.id desc

但是,以下查询有更多机会使用您的索引。

SELECT *
FROM(SELECT *, 
            ROW_NUMBER() OVER(
            ORDER BY s.date, 
                     s.id DESC) AS RowNr
     FROM SomeTable s
     WHERE s.chainName = '<someNotExistingName>') AS TMP_TBL
WHERE RowNr = 1;

【讨论】:

以上是关于奇怪的 MS SQL Server 行为/性能问题的主要内容,如果未能解决你的问题,请参考以下文章

MS Access 直通选择查询导致 SQL Server 中的页面锁定

SQL Server 中全文搜索的奇怪行为

使用奇怪的查询优化器行为加入 SQL Server 中的视图

MS SQL SERVER中的SELECT * 的I/O性能

C++对MS SQL Server的操作

mssql-jdbc MS SQL Server JDBC 驱动程序准备好的语句缓存性能问题与 Hikari CP