我正在编写一个后台服务,需要处理一系列作业,这些作业作为记录存储在 sqlserver 表中。该服务需要找到最旧的 20 个需要工作的工作(where status = 'new'
),标记它们(set status = 'processing'
),运行它们,然后更新作业。
这是我需要帮助的第一部分。可能有多个线程同时访问数据库,我想确保“标记和返回”查询以原子方式运行,或几乎如此。
该服务将花费相对较少的时间访问数据库,并且如果一个作业运行两次也不是世界末日,因此我可能能够接受小概率的作业运行多次,以提高代码的简单性。
做这个的最好方式是什么?我在数据层使用 linq-to-sql,但我想我必须为此使用 t-sql。
你的工作表是一个队列。写入用户表备份队列是出了名的容易出错,因为它会导致死锁和并发问题。
最简单的事情是删除用户表并使用 truequeue反而。这将为您提供经过系统测试和验证的代码库上的无死锁并发队列。问题是围绕队列的整个范例从 INSERT 和 DELETE/UPDATE 更改为SEND/RECEIVE。另一方面,通过内置队列,您可以获得一些非常强大的免费好东西,即激活 and 相关项目锁定.
如果您想继续沿着用户表支持的队列的路径前进,那么second编写用户表队列最重要的技巧是使用 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 并且您需要它来满足您的要求的“最旧”部分。需要锁提示来促进并行处理线程之间的高并发性。
我说这是第二重要的技巧。最重要的是如何组织表格。对于队列来说must被聚类为(status, enqueue_time)
。如果你没有正确地组织表格,你最终会陷入死锁。先发制人的评论:碎片在这种情况下是无关紧要的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)