5

我正在将磁盘中的队列迁移到内存 SQL Server 2016 以实现队列。

这是我的队列格式:

CREATE TABLE dbo.SimpleQueue
(
   MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED IDENTITY(1, 1),
   Payload VARCHAR(7500) NOT NULL,
   IsDeleted BIT NOT NULL
) WITH (MEMORY_OPTIMIZED=ON)
GO

这是我的Enqueue原生 SQL Server 存储过程:

CREATE PROCEDURE dbo.Enqueue(@Payload VARCHAR(7500), @IsDeleted BIT)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
(TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')

  INSERT INTO dbo.SimpleQueue (Payload, IsDeleted) VALUES (@Payload, @IsDeleted); 

END
GO

我正在尝试写下本Dequeue机 SQL Server 存储过程,但在如何实现UPDATESELECT 或变量表的使用结果方面遇到了一些困难。

到目前为止,我尝试过:

CREATE PROCEDURE dbo.Dequeue(@BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT,LANGUAGE = 'english' )
   UPDATE dbo.SimpleQueue
        SET IsDeleted=1
        WHERE MsgId = (
            SELECT TOP(@BatchSize) MsgId, Payload
                FROM dbo.SimpleQueue
                WHERE IsDeleted = 0)
END
GO

但我得到这个错误:

子查询(嵌套在另一个查询中的查询)仅在具有本机编译模块的 SELECT 语句中受支持。

所以我尝试了一种不同的方法,使用变量来存储结果。

首先我创建了一个表格类型:

CREATE TYPE dbo.SimpleDequeue
  AS TABLE 
   (
    MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED, 
    Payload INT NOT NULL
   )
   WITH (MEMORY_OPTIMIZED=ON)
GO

到目前为止一切顺利,然后我尝试使用它:

CREATE PROCEDURE dbo.Dequeue(@BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')

    DECLARE @result dbo.SimpleDequeue;

    INSERT @result 
        SELECT TOP(@BatchSize) MsgId, Payload FROM dbo.SimpleQueue
        WHERE IsDeleted = 0

    UPDATE dbo.SimpleQueue 
        SET IsDeleted = 1 
        WHERE 
            @result.MsgId = dbo.SimpleQueue.MsgId

    SELECT MsgId, Payload FROM @result
END
GO

我收到此错误:

必须声明标量变量“@result”。

(仅当使用@resulton时WHERE @result.MsgId = dbo.SimpleQueue.MsgId

这是在磁盘 SQL Server 表中使用的旧出队过程:

CREATE PROCEDURE dbo.DequeueInDisk
    @BatchSize INT = 1
AS
BEGIN
    SET NOCOUNT ON;
    WITH 
    cte AS (
        SELECT TOP(@BatchSize) Payload
        FROM dbo.SimpleQueue WITH (ROWLOCK, READPAST)
        ORDER BY MsgId
    )
    DELETE FROM cte OUTPUT deleted.Payload;
END

如何使 UPDATE 和 OUTPUT 更新值(具有高性能,因为这很关键)?

4

2 回答 2

2

我认为从 SQL 开发的角​​度来看,您的方法非常有意义——您必须以集合而不是逐行的方式思考。但看起来微软认为您需要对本机编译过程使用不同的方法,更加命令式并且真正逐行(请参阅Implementing UPDATE with FROM or Subqueries或Implementing MERGE Functionality in a Native Compiled Stored Procedure。所以你的过程看起来像这个:

create or alter procedure [dbo].[Dequeue](@BatchSize int = 1)
with native_compilation, schemabinding, execute as owner
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
    declare
        @result dbo.SimpleDequeue;

    declare
        @MsgId int,
        @Payload varchar(7500),
        @i int = 0;

    while @i < @BatchSize
    begin
        select top (1)
            @MsgId = s.MsgId,
            @Payload = s.Payload
        from dbo.SimpleQueue as s
        where
            s.IsDeleted = 0
        order by
            s.MsgId;

        if @@rowcount = 0
        begin
            set @i = @BatchSize;
        end
        else
        begin
            update dbo.SimpleQueue set IsDeleted = 1 where MsgId = @MsgId;

            insert into @result (MsgId, Payload)
            select @MsgId, @Payload;

            set @i += 1;
        end;
    end;

    select MsgId, Payload from @result;
END

我还没有测试过它的工作速度,但我肯定会用一些实数来测试它,因为我们已经实现了几个这样的表队列,我想知道我们是否可以通过 Hekaton 获得一些性能提升。

于 2018-07-08T11:34:30.123 回答
-1

在您的旧例程中,您TOP(@BatchSize)使用ORDER BY MsgId. 新方法似乎没有这个ORDER BY......你会得到随机结果......

您的

WHERE MsgId = (
            SELECT TOP(@BatchSize) MsgId, Payload
                FROM dbo.SimpleQueue
                WHERE IsDeleted = 0
                /*added this!*/ ORDER BY MsgId  )

将返回两列 - 可能 - 几行。您不能将其与“=”进行比较。

你可以尝试什么:

WHERE MsgId IN (
            SELECT TOP(@BatchSize) MsgId
                FROM dbo.SimpleQueue
                WHERE IsDeleted = 0
                ORDER BY MsgId)

或者您可以尝试使用 INNER JOIN,如下所示:

 UPDATE dbo.SimpleQueue
        SET IsDeleted=1
   FROM dbo.SimpleQeueu
   INNER JOIN dbo.SimpleQueue AS sq ON dbo.SimpleQeueu.MsgId=sq.MsgId
                                       AND sq.IsDeleted=0
                                       --this is missing the TOP-clause

还有什么:你可以尝试一个INNER JOIN (SELECT TOP ... ) AS InnerSimpleQueue ON ..或者可能是一个交叉应用。

编辑:使用 CTE 的另一种方法:

WITH myCTE AS
(
    SELECT TOP(@BatchSize) MsgId
    FROM dbo.SimpleQueue
    WHERE IsDeleted = 0
    ORDER BY MsgId
)   
UPDATE dbo.SimpleQueue
        SET IsDeleted=1
FROM dbo.SimpleQeueu
INNER JOIN myCTE ON myCTE.MsgId=dbo.SimpleQueue.MsgId
于 2015-09-10T12:00:03.387 回答