Hangfire在SQL Server中引起了锁定问题

12
我们在使用ASP.NET Web项目和SQL Server 2016期间,使用Hangfire 1.7.2。我们的服务器上有大约150个站点,每个站点都使用Hangfire 1.7.2。当我们升级这些网站以使用Hangfire时,DB服务器崩溃了。检查DB日志,我们发现存在多个锁定查询。我们已经识别出一个RPC事件“sys.sp_getapplock;1”在所有阻塞会话中。看起来Hangfire正在锁定我们的DB,使整个DB无法使用。由于Hangfire,我们注意到几乎有670个以上的锁定查询。
这可能是由于我们设置的以下属性导致的:
   SlidingInvisibilityTimeout = TimeSpan.FromMinutes(30),
   QueuePollInterval = TimeSpan.FromHours(5)
每个站点大约有20个后台任务,其中一些每分钟运行几次,而另一些每小时、每6小时和某些只运行一次每天。
我搜索了文档,但找不到任何可以解释这两个属性或如何设置它们以避免数据库锁定的内容。
希望能得到一些帮助。
编辑:以下查询每秒执行一次:
exec sp_executesql N'select count(*) from [HangFire].[Set] with (readcommittedlock, forceseek) where [Key] = @key',N'@key nvarchar(4000)',@key=N'retries'

select distinct(Queue) from [HangFire].JobQueue with (nolock)

exec sp_executesql N'select count(*) from [HangFire].[Set] with (readcommittedlock, forceseek) where [Key] = @key',N'@key nvarchar(4000)',@key=N'retries'

无论我们设置何种时间跨度值,都会产生影响。以下是我们正在使用的 GetHangfireServers 代码:

  public static IEnumerable<IDisposable> GetHangfireServers()
    {
        // Reference for GlobalConfiguration.Configuration: http://docs.hangfire.io/en/latest/getting-started/index.html
        // Reference for UseSqlServerStorage: http://docs.hangfire.io/en/latest/configuration/using-sql-server.html#configuring-the-polling-interval
        GlobalConfiguration.Configuration
            .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)

            .UseSimpleAssemblyNameTypeSerializer()
            .UseRecommendedSerializerSettings()
            .UseSqlServerStorage(ConfigurationManager.ConnectionStrings["abc"]
                .ConnectionString, new SqlServerStorageOptions
            {
                CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                SlidingInvisibilityTimeout = TimeSpan.FromMinutes(30),
                QueuePollInterval = TimeSpan.FromHours(5), // Hangfire will poll after 5 hrs to check failed jobs.
                UseRecommendedIsolationLevel = true,
                UsePageLocksOnDequeue = true,
                DisableGlobalLocks = true
            });

        // Reference: https://docs.hangfire.io/en/latest/background-processing/configuring-degree-of-parallelism.html
        var options = new BackgroundJobServerOptions
        {
            WorkerCount = 5
        };

        var server = new BackgroundJobServer(options);

        yield return server;
    }

工人数量仅设置为5。

只有4个任务,而且即使这些任务都已完成(SELECT * FROM [HangFire].[State]): enter image description here

您是否知道为什么 Hangfire 每秒钟会执行如此多的查询?


1
这不是Hangfire作业的问题,很可能是您的数据库结构在查询时被锁定了,请尝试在没有Hangfire作业的情况下执行操作,您会发现锁定仍然存在。请尝试解决数据库结构中的锁定问题,这不是Hangfire的问题。 - Hammad Sajid
还要检查一下你的 Hangfire 作业仪表板,作业是否成功执行了? - Hammad Sajid
1
你的应用程序查询或Hangfire发出了“sp_getapplock”查询吗? - Dan Guzman
1
sp_getapplock的问题是由于HangFire引起的。 - Raghav
QueuePollInterval是Hnagfire命中数据库的时间间隔。但是"SlidingInvisibilityTimeout"是什么? - Vivek
3个回答

10

我们在一个项目中遇到了这个问题。 Hangfire仪表板相当读取大量数据,会非常频繁地轮询Hangfire数据库以刷新作业状态。

对于我们最有效的解决方案是拥有一个专用的Hangfire数据库。 这样,您将隔离应用程序查询和Hangfire查询,并且您的应用程序查询不会受到Hangfire服务器和仪表板查询的影响。


3
另一个好主意是将DashboardOptions.StatsPollingInterval更改为类似于600000(10分钟)的值。这会减缓Hangfire仪表板自动刷新的频率(您始终可以手动刷新)。 - tgharold
1
我们有一个专用的Hangfire数据库,但今天我在我们的生产环境中遇到了这个问题;除了所有作业都失败并显示“DistributedLockTimeoutException”之外,它还影响了我们不引用Hangfire数据库的其他服务的行为。我的猜测是,除了锁定Hangfire数据库中的资源外,Hangfire还锁定了我们使用的主数据库中的资源(我们的Hangfire作业读取和写入该数据库)。 - Tagc

2

在配置SqlServerStorage时,有一个名为SlidingInvisibilityTimeout的新配置选项,它会导致这些数据库锁定作为较新的非事务消息获取算法的一部分。它适用于可能导致事务日志备份出错(因为长时间运行的作业仍处于活动状态作为长时间运行作业的一部分)的长时间运行的作业。

最初的回答:

在配置SqlServerStorage时,有一个新的配置选项SlidingInvisibilityTimeout,它会导致这些数据库锁定,这是较新的非事务消息获取算法的一部分。它适用于那些可能导致事务日志备份出错的长时间运行的作业,因为该作业仍处于活动状态。

.UseSqlServerStorage(
    "connection_string", 
    new SqlServerStorageOptions { SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5) });

我们的数据库管理员不喜欢数据库锁定,所以我删除了SlidingInvisibilityTimeout选项,改用旧的基于事务的消息获取算法,因为我的队列中没有长时间运行的作业。
是否启用此选项取决于您的情况。如果您的队列数据库不在应用程序数据库中,则可以考虑将其移动到外部,并启用SlidingInvisibilityTimeout选项。如果您的DBA即使队列是单独的数据库也无法接受锁定,则可以将任务重构为更多、更短的任务。只是一些想法。
最初的回答: https://www.hangfire.io/blog/2017/06/16/hangfire-1.6.14.html

0

SqlServerStorage 运行 Install.sql,该文件对 Hangfire-schema 进行独占模式的架构锁定。

DECLARE @SchemaLockResult INT;
EXEC @SchemaLockResult = sp_getapplock @Resource = '$(HangFireSchema):SchemaLock', 
@LockMode = 'Exclusive'

来自Hangfire文档:

“SQL Server对象是通过执行Install.sql文件中描述的语句(该文件位于NuGet包中的tools文件夹下)从SqlServerStorage构造函数自动安装的。其中包含迁移脚本,因此可以无缝安装具有模式更改的新版本的Hangfire,而无需您的干预。”

如果您不想每次运行此脚本,可以将SqlServerStorageOptions.PrepareSchemaIfNecessary设置为false。

var options = new SqlServerStorageOptions
{
    PrepareSchemaIfNecessary = false
};

var sqlServerStorage = new SqlServerStorage(connectionstring, options);

相反,通过使用以下命令手动运行Install.sql:

SqlServerObjectsInstaller.Install(connection);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接