当磁盘使用率高时,使用 SqlCommand.ExecuteScalar() 从序列中选择返回 NULL

Posted

技术标签:

【中文标题】当磁盘使用率高时,使用 SqlCommand.ExecuteScalar() 从序列中选择返回 NULL【英文标题】:Select from sequence with SqlCommand.ExecuteScalar() returns NULL when high disk usage 【发布时间】:2018-03-16 16:07:01 【问题描述】:

我在生产环境中遇到SqlCommand.ExecuteScalar() 有时会返回NULL

我在这里遇到过很多类似的问题,最接近的一个是:SqlCommand.ExecuteScalar returns null but raw SQL does not。但给出的建议与我的情况无关。

代码示例在这里:

using (var connection = new SqlConnection(connectionString))

    connection.Open();
    using (var command = connection.CreateCommand())
    
        command.CommandText = "SELECT NEXT VALUE FOR Seq_Revision";
        command.CommandType = CommandType.Text;

        return (long)command.ExecuteScalar(); //<---ExecuteScalar() here returns NULL sometimes
    

Seq_Revision 这是简单的 MSSQL 序列,如下所示:

CREATE SEQUENCE [dbo].[Seq_Revision] 
 AS [bigint]
 START WITH 0
 INCREMENT BY 1
 MINVALUE -9223372036854775808
 MAXVALUE 9223372036854775807
 CACHE  10 
GO

而且我很确定它永远不会真正返回 NULL。


当在此代码示例中返回 NULL 时,我也观察到类似的奇怪(不可重复的行为),而我确定 存在具有此 ID 的实体:

NHibernate.ISession.Get<FooEntity>(entityId)

有趣的是,通过这种方法返回 NULL 与 SQL 节点上存在大量磁盘活动(磁盘队列长度 > ~50)的时间范围非常相关。

这可能很重要:我们使用带 2 个节点的 AlwaysON 集群,其中一个节点用于读取模式(连接字符串中的ApplicationIntent=READONLY)。

MSSQL 版本是:

Microsoft SQL Server 2014 (SP2-CU5) (KB4013098) - 12.0.5546.0 (X64) 
    Apr  3 2017 14:55:37 
    Copyright (c) Microsoft Corporation
    Enterprise Edition: Core-based Licensing (64-bit) on Windows NT 6.3 <X64> (Build 9600: )

【问题讨论】:

ExecuteScalar 返回null 表示:没有返回行。如果返回的行为空值,则为DBNull.Value。这没有回答“为什么?”,但它可能是有用的上下文 @MarcGravell,我知道。但是你相信 SELECT NEXT VALUE FOR Seq_Revision 返回 0 行吗?听起来不可能。 我不知道;这是个好问题 NEXT VALUE FOR 不支持只读数据库。请参阅docs.microsoft.com/en-us/sql/t-sql/functions/… 的限制和限制 => 从连接字符串中删除 ApplicationIntent=READONLY 您能否测试这种情况,例如if (command.GetType() == typeof(DBNull)) 或者它用InvalidCastException 捕获它,然后检查值或在Thread.Sleep(1000); 之后重试,以排除磁盘的计时问题.也是添加一些日志记录以帮助形成minimal reproducible example 的好地方,我猜它是间歇性的,并且只能在 IO 压力下重现。 【参考方案1】:

我认为问题可能与序列缓存有关。

也许有一些未处理的东西导致缓存中剩余的序列号丢失。

尝试禁用序列中的缓存:

ALTER SEQUENCE [dbo].[Seq_Revision] 
 NO CACHE
GO

或尝试使用更高的缓存值:

ALTER SEQUENCE [dbo].[Seq_Revision] 
 CACHE 100
GO

【讨论】:

谢谢,我们会试试这个理论。但这无法解释为什么我有时会从 ISession.Get(id) 获得 NULL,而我确信具有此 ID 的实体确实存在:NHibernate.ISession.Get(entityId) ISession.Get 访问数据库或会话缓存以检索实体数据。如果实体存在则返回,否则返回 NULL。这意味着如果会话缓存存在未处理的问题.. 你可以得到 NULLs

以上是关于当磁盘使用率高时,使用 SqlCommand.ExecuteScalar() 从序列中选择返回 NULL的主要内容,如果未能解决你的问题,请参考以下文章

Mysql - 当Linux服务器上的IO等待很高时查询很慢

卷扩容业务失败了,在线等…

当构建目标较高时,AVD 不显示

致命错误:当内存使用率非常高时,调用非对象上的成员函数 write()

卷扩容业务失败了,在线等…

卷扩容业务失败了,在线等…