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 ストアド プロシージャを書き留めようとしていUPDATEますが、SELECT または変数テーブルの結果を使用して実装する方法に問題があります。

これまでのところ、私は試しました:

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」を宣言する必要があります。

@result( がonを使用している場合のみ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開発の観点からは完全に理にかなっていると思います-行ごとのアプローチではなく、セットで考える必要があります。しかし、Microsoft は、ネイティブ コンパイル プロシージャには別のアプローチが必要であると考えているようです。より命令的で、実際には行単位です ( FROM またはサブクエリを使用した UPDATE の実装またはネイティブ コンパイル ストアド プロシージャでの MERGE 機能の実装を参照してください。したがって、プロシージャは次のようになります。これ:

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  )

2 つの列と - おそらく - 数行で返されます。これを「=」と比較することはできません。

お手並みをみせてもらおう:

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 ..CROSS APPLY や CROSS APPLY を試すことができます。

編集: CTE を使用したもう 1 つのアプローチ:

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 に答える