代码之家  ›  专栏  ›  技术社区  ›  John Sheehan

您将如何在.NET/SQL Server中构建此消息处理系统?

  •  5
  • John Sheehan  · 技术社区  · 14 年前

    假设我有一个SQL Server数据库表,表中有X(>1000000)条记录,需要逐个处理(获取数据、执行外部操作、更新数据库中的状态)(控制台应用程序、windows服务、Azure工作者角色等)。我需要保证每行只处理一次。理想情况下,无论有多少台机器/进程被转起来处理消息,都可以保证独占性。我最担心的是两个选择同时抓住同一行。

    2 回复  |  直到 14 年前
        1
  •  7
  •   egrunin    14 年前

    我经历过这种情况。

    添加 InProcess 列,默认值为0。在消费过程中:

    UPDATE tbl SET Inprocess = @myMachineID WHERE rowID = 
        (SELECT MIN(rowID) WHERE InProcess = 0)
    

    SELECT * FROM tbl WHERE rowID = 
        (SELECT MAX(rowID) FROM tbl WHERE ProcessID = @myMachineID)
    

    您还必须添加 Done 将某种类型的标志添加到行中,这样您就可以判断该行是否已声明,但处理未完成。

    编辑

    这个 UPDATE 获取独占锁(请参见 MSDN ). 我不确定 SELECT 在子查询中允许从 更新 ;如果是这样的话,你就得把它们放到一个交易中。

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    

    ……但我没试过。

    @马丁史密斯的链接也提出了一些好的观点,看看 OUTPUT

    最后一次编辑

    在交流中很有意思的点评,我在这里肯定学到了一些东西。这就是为什么,对吗?

    只是为了颜色:当我在2004年使用这种方法时,我让一群网络爬虫将URL转储到一个表中进行搜索,然后从同一个表中提取下一个URL进行爬网。由于爬虫试图吸引恶意软件,它们随时可能崩溃。

        2
  •  0
  •   Nick DeVore    14 年前

    我会考虑让进程将“processed”标志为零的前N个记录提取到本地集合中。事实上,对于processed标志,我有三个值:NotProcessed(0)、Processing(2)和processed(1)。然后,在集合中循环并发出以下sql:

    update table_of_records_to_process
    set processed = 2
    where record_id = 123456
    and processed = 0
    

    …这样,如果其他进程已经获取了该记录ID,则不会将“已处理”字段设置为2。您需要验证记录ID 123456是否真的设置为2:

    select count(*)
    from table_of_records_to_process
    where record_id = 123456
    and processed = 2