sql - 以原子方式标记并返回数据库中的一组行

标签 sql sql-server linq-to-sql concurrency

我正在编写一个后台服务,需要处理一系列作业,这些作业作为记录存储在 sqlserver 表中。该服务需要找到最旧的 20 个需要处理的作业(其中 status = 'new'),标记它们(set status = 'processing'),运行它们,然后更新职位。

这是我需要帮助的第一部分。可能有多个线程同时访问数据库,我想确保“标记并返回”查询以原子方式运行,或几乎如此。

此服务将花费相对较少的时间访问数据库,并且如果一个作业运行两次,也不是世界末日,因此我可能能够接受小概率的作业运行多次,以提高简单性代码。

执行此操作的最佳方法是什么?我在数据层使用 linq-to-sql,但我想我必须为此使用 t-sql。

最佳答案

您的作业表是一个队列。写入用户表备份队列是出了名的容易出错,因为它会导致死锁和并发问题。

最简单的事情是删除用户表并使用 true queue反而。这将为您提供经过系统测试和验证的代码库上的无死锁并发队列。问题是围绕队列的整个范例从 INSERT 和 DELETE/UPDATE 更改为 SEND/RECEIVE 。另一方面,通过内置队列,您可以获得一些非常强大的免费好东西,即 Activationcorrelated items locking .

如果您想继续沿着用户表支持的队列的路径前进,那么编写用户表队列的第二最重要的技巧是使用 UPDATE ... OUTPUT:

WITH cte AS (
  SELECT TOP(20) status, id, ...
  FROM table WITH (ROWLOCK, READPAST, UPDLOCK)
  WHERE status = 'new'
  ORDER BY enqueue_time)
UPDATE cte
  SET status = 'processing'
OUTPUT
  INSERTED.id, ...

CTE语法只是为了方便正确放置TOP和ORDER BY,查询可以使用派生表编写,同样简单。您不能直接使用 UPDATE ... TOP 因为 UPDATE 不支持 ORDER BY 并且您需要它来满足您的要求的“最旧”部分。需要锁提示来促进并行处理线程之间的高并发性。

我说这是第二重要的技巧。最重要的是如何组织表格。对于队列,它必须(status, enqueue_time)进行集群。如果你没有正确地组织表格,你最终会陷入死锁。先发制人的评论:碎片在这种情况下是无关紧要的。

关于sql - 以原子方式标记并返回数据库中的一组行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1821142/

相关文章:

c# - SqlBulkCopy 是否自动启动事务?

sql-server - PostgreSQL - 根据第 1 列过滤第 2 列结果

c# - 如何使用 Linq to SQL 查找一行的 ROW_NUMBER()

sql - SQL Server 中数据库范围的唯一但简单的标识符

SQL Server : +(unary) operator on non-numeric Strings

sql - 如何使用 SQL Server 对 SQL Server 查询进行基准测试

linq-to-sql - 存储库应该向服务层公开 IQueryable 还是在实现中执行过滤?

linq-to-sql - LinkDataSource 无法加载 DataContext

sql - 查找提供每个零件的供应商的sid

mysql - 连接查询和表并出现错误?