Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可能正在数据库队列中处理相同的消息(SQL Server)_Sql_Sql Server_Database - Fatal编程技术网

可能正在数据库队列中处理相同的消息(SQL Server)

可能正在数据库队列中处理相同的消息(SQL Server),sql,sql-server,database,Sql,Sql Server,Database,我们有时会遇到作业队列中的消息被处理两次。在我们的系统环境中,有两个服务可以直接处理来自队列表T_JOB_的消息 下面您可以看到服务调用的存储过程,该服务假定从队列中拾取消息 您能看到是否有一些明显的错误使得两个服务可以从T_JOB_DIRECT表中选择相同的消息吗 CREATE PROCEDURE [dbo].[usp_GetOneBatchJobForExec] @sMachineName NVARCHAR(50) AS BEGIN DECLARE @JOB_DIRECT_

我们有时会遇到作业队列中的消息被处理两次。在我们的系统环境中,有两个服务可以直接处理来自队列表T_JOB_的消息

下面您可以看到服务调用的存储过程,该服务假定从队列中拾取消息

您能看到是否有一些明显的错误使得两个服务可以从T_JOB_DIRECT表中选择相同的消息吗

CREATE       PROCEDURE [dbo].[usp_GetOneBatchJobForExec]
@sMachineName NVARCHAR(50)
AS

BEGIN  
DECLARE @JOB_DIRECT_ID INTEGER

  SET NOCOUNT ON
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED

  IF RTRIM(ISNULL(@sMachineName, '')) = '' BEGIN
    RAISERROR('@sMachineName parameter must be supplied!',16,1)
  END
  ELSE
  BEGIN
    SET @JOB_DIRECT_ID = 0
    WHILE @JOB_DIRECT_ID IS NOT NULL
    BEGIN
      -- If there is an job that has been marked for this service then return that.
      SET @JOB_DIRECT_ID = NULL
      SELECT TOP 1 @JOB_DIRECT_ID = JOB_DIRECT_ID FROM dbo.T_JOB_DIRECT WHERE IN_PROCESS_BY = @sMachineName
      IF @JOB_DIRECT_ID IS NULL
      BEGIN

        -- else take highest prioritized job that is free
        SELECT TOP 1 @JOB_DIRECT_ID = JOB_DIRECT_ID
        FROM dbo.T_JOB_TYPE AS JY 
          INNER JOIN dbo.T_JOB_TYPE_ON_INST AS JTI 
            ON JY.JOB_TYPE_ID = JTI.JOB_TYPE_ID
          INNER JOIN dbo.T_JOB_DIRECT AS JD 
            ON JD.JOB_TYPE_ID = JTI.JOB_TYPE_ID
          AND JD.INST_ID = JTI.INST_ID
        WHERE JD.IN_PROCESS_BY IS NULL AND JD.START_DATETIME < GETDATE()
        ORDER BY JD.START_DATETIME DESC, JTI.PRIORITY DESC

        IF @JOB_DIRECT_ID IS NOT NULL
        BEGIN
          --Mark the job as taken and pick it up the next loop OR if update misses (someone else got it before) then get anotehr i next loop.
          UPDATE dbo.T_JOB_DIRECT SET IN_PROCESS_BY = @sMachineName WHERE JOB_DIRECT_ID = @JOB_DIRECT_ID AND IN_PROCESS_BY IS NULL
        END
      END
      ELSE
      BEGIN
        -- Return job information for the selected job.
        SELECT TOP 1 JD.*, JY.JOB_NAME
        FROM dbo.T_JOB_TYPE AS JY INNER JOIN dbo.T_JOB_TYPE_ON_INST AS JTI 
            ON   JY.JOB_TYPE_ID = JTI.JOB_TYPE_ID
          INNER JOIN dbo.T_JOB_DIRECT AS JD 
            ON   JD.JOB_TYPE_ID = JTI.JOB_TYPE_ID
          AND JD.INST_ID = JTI.INST_ID
        WHERE JOB_DIRECT_ID = @JOB_DIRECT_ID

        --Set ID NULL to exit the loop
        SET @JOB_DIRECT_ID = NULL
      END
    END  --LOOP

  END  --IF RAISE

END
它应该只选择一个具有相同ID的作业,如果在\u进程中,\u BY=NULL。只有当且仅当没有其他人在选择和更新之间选择作业时,才能选择作业

程序应该能够处理一场比赛,而他认为你没有一个有效的分数。
如果没有和IN_PROCESS_BY为空,我们肯定会遇到问题

看起来你有比赛条件。具体地说,从选择行到将其更新为“正在处理”之间有一段时间。您可以通过一些查询提示和在事务中包装select和update来解决这个问题。您的选择可以类似于:

IF @JOB_DIRECT_ID IS NULL
BEGIN
    BEGIN TRANSACTION;

    SELECT TOP 1 @JOB_DIRECT_ID = JOB_DIRECT_ID
    FROM dbo.T_JOB_TYPE AS JY 
    INNER JOIN dbo.T_JOB_TYPE_ON_INST AS JTI 
       ON JY.JOB_TYPE_ID = JTI.JOB_TYPE_ID
    INNER JOIN dbo.T_JOB_DIRECT AS JD with (updlock, rowlock, readpast)
       ON JD.JOB_TYPE_ID = JTI.JOB_TYPE_ID
       AND JD.INST_ID = JTI.INST_ID
    WHERE JD.IN_PROCESS_BY IS NULL 
       AND JD.START_DATETIME < GETDATE()
    ORDER BY JD.START_DATETIME DESC, JTI.PRIORITY DESC;

    IF @JOB_DIRECT_ID IS NOT NULL
    BEGIN
        -- Mark the job as taken and pick it up the next loop 
        -- OR if update misses (someone else got it before) 
        -- then get another in next loop.
        UPDATE dbo.T_JOB_DIRECT 
        SET IN_PROCESS_BY = @sMachineName 
        WHERE JOB_DIRECT_ID = @JOB_DIRECT_ID
           AND IN_PROCESS_BY IS NULL
    END
    COMMIT TRANSACTION;
END

另一方面,我将在in_process_by列的t_job_direct表中添加一个唯一的筛选索引,如果您还没有索引,则该索引的值不为null。这将防止给定机器在任何时候处理多行。

由于上面Ben Thul的评论,我更改了存储过程以利用OUTPUT子句。原始sp是在SQL Server中引入OUTPUT子句之前编写的

这是最终的新版本

CREATE       PROCEDURE [dbo].[usp_GetOneBatchJobForExec]
@sMachineName NVARCHAR(50)
AS
BEGIN

SET NOCOUNT ON; 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

with cte as (
    SELECT TOP(1) JD.*, JY.JOB_NAME
    FROM dbo.T_JOB_TYPE AS JY 
        INNER JOIN dbo.T_JOB_TYPE_ON_INST AS JTI 
            ON JY.JOB_TYPE_ID = JTI.JOB_TYPE_ID
        INNER JOIN dbo.T_JOB_DIRECT AS JD WITH (ROWLOCK, READPAST)
            ON JD.JOB_TYPE_ID = JTI.JOB_TYPE_ID
        AND JD.INST_ID = JTI.INST_ID
    WHERE (JD.IN_PROCESS_BY IS NULL OR JD.IN_PROCESS_BY = @sMachineName) AND JD.START_DATETIME < GETDATE()
    ORDER BY JD.IN_PROCESS_BY DESC, JD.START_DATETIME DESC, JTI.PRIORITY DESC
)
UPDATE cte 
SET IN_PROCESS_BY = @sMachineName
OUTPUT inserted.JOB_DIRECT_ID, inserted.JOB_TYPE_ID, inserted.INST_ID, inserted.JOB_QUEUE_ID, inserted.JOB_DATA, inserted.REG_DATETIME, 
        inserted.REG_USER_ID, inserted.JOB_TIME_ID, inserted.IN_PROCESS_BY, inserted.START_DATETIME, deleted.JOB_NAME

END

感谢您抽出时间调查此事!我从程序的原始创建者那里得到了一些反馈。请看下面的编辑在原来的职位以上!可能但问题是,您在一个单独的select中获得了@JOB\u DIRECT\u ID,因此两个或多个进程可以同时获取同一行。另一种管理方法是在更新中使用OUTPUT子句,同时跳过select all。感谢您向我介绍OUTPUT子句。帮助了我们很多。
CREATE       PROCEDURE [dbo].[usp_GetOneBatchJobForExec]
@sMachineName NVARCHAR(50)
AS
BEGIN

SET NOCOUNT ON; 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

with cte as (
    SELECT TOP(1) JD.*, JY.JOB_NAME
    FROM dbo.T_JOB_TYPE AS JY 
        INNER JOIN dbo.T_JOB_TYPE_ON_INST AS JTI 
            ON JY.JOB_TYPE_ID = JTI.JOB_TYPE_ID
        INNER JOIN dbo.T_JOB_DIRECT AS JD WITH (ROWLOCK, READPAST)
            ON JD.JOB_TYPE_ID = JTI.JOB_TYPE_ID
        AND JD.INST_ID = JTI.INST_ID
    WHERE (JD.IN_PROCESS_BY IS NULL OR JD.IN_PROCESS_BY = @sMachineName) AND JD.START_DATETIME < GETDATE()
    ORDER BY JD.IN_PROCESS_BY DESC, JD.START_DATETIME DESC, JTI.PRIORITY DESC
)
UPDATE cte 
SET IN_PROCESS_BY = @sMachineName
OUTPUT inserted.JOB_DIRECT_ID, inserted.JOB_TYPE_ID, inserted.INST_ID, inserted.JOB_QUEUE_ID, inserted.JOB_DATA, inserted.REG_DATETIME, 
        inserted.REG_USER_ID, inserted.JOB_TIME_ID, inserted.IN_PROCESS_BY, inserted.START_DATETIME, deleted.JOB_NAME

END