たくさんの活動をすることを意図したこのテーブルを持っています。その中にあるものは、ユーザーアクションがまだ保留中であることを示します。削除すると、アクションが保留中でなくなったことを示します。システムの他の領域はこれに依存しているため、表自体は実際にはシナリオを説明していません。しかし、私が持っている問題は、保留中のアクションが完了したときにテーブルをそのままにして削除する必要があるか、フラグ列を追加して更新する必要があるかです。
挿入された同じ秒にレコードが削除される場合があることに注意してください。 1秒あたり最大100をサポートしたいと思っていますが、それを制限として使いたくありません。
SQL Server 2014 Enterprise Editionを使用しています。
これがテーブルの定義です(すべてのインデックスは、このテーブルを使用する選択クエリに基づいています)。
CREATE TABLE [dbo].[OpenRounds](
[OpenRoundId] [bigint] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[GameActivityId] [bigint] NOT NULL,
[VendorId] [int] NOT NULL,
[Date] [datetime] NOT NULL,
[UserBonusId] [bigint] NULL,
[VendorRoundId] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_GamesOpenRounds] PRIMARY KEY CLUSTERED
(
[OpenRoundId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_GameOpenRoundsUserIdUserBonusId] ON [dbo].[OpenRounds]
(
[UserId] ASC,
[UserBonusId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
CREATE NONCLUSTERED INDEX [IX_GameOpenRoundsUserIdVendorIdVendorRoundId] ON [dbo].[OpenRounds]
(
[UserId] ASC,
[VendorId] ASC,
[VendorRoundId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
CREATE NONCLUSTERED INDEX [IX_GameOpenRoundsVendorIdVendorRoundId] ON [dbo].[OpenRounds]
(
[VendorId] ASC,
[VendorRoundId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
1秒あたり数千のトランザクションをサポートする目的でシステムを設計している場合は、挿入に依存しないキューシステムを設計することをお勧めします。挿入にはコストがかかり、キューテーブルの設計によっては、キューテーブルの最後のページがシステムのスループットレートを制限する「ホットスポット」になる場合があります。
次の設計では、Roundsテーブルを介して最大100,000の同時トランザクションが可能です。
CREATE TABLE dbo.OpenRounds
(
OpenRoundID BIGINT NOT NULL
CONSTRAINT PK_GamesOpenRounds
PRIMARY KEY CLUSTERED
, UserID INT NULL
, GameActivityID BIGINT NULL
, VendorID INT NULL
, RoundDate DATETIME NULL
, UserBonusID BIGINT NULL
, VendorRoundID NVARCHAR(50) NULL
, ReferenceCount INT NOT NULL
CONSTRAINT DF_OpenRounds_ReferenceCount
DEFAULT ((0))
);
INSERT INTO dbo.OpenRounds (OpenRoundID)
SELECT TOP(100000) /* top 100,000 -> we're creating 100,000 slots */
rn = ROW_NUMBER() OVER (ORDER BY o1.object_id)
FROM sys.objects o1
, sys.objects o2
, sys.objects o3;
GO
CREATE SEQUENCE dbo.RoundSlotSequence
AS INT
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 100000
CYCLE
CACHE 10000;
GO
上記で作成されたシーケンスは、100,000に達するとロールオーバーするように設計されています。これは、dbo.OpenRounds
テーブルに作成した行数と一致します。
OpenRoundsテーブルにメッセージをプッシュするには、次のプロシージャを使用できます。
CREATE PROCEDURE dbo.PushRound
(
@UserID INT
, @GameActivityID BIGINT
, @VendorID INT
, @RoundDate DATETIME
, @UserBonusID BIGINT
, @VendorRoundID NVARCHAR(50)
)
AS
BEGIN
DECLARE @SlotID INT;
DECLARE @SequenceID INT;
DECLARE @TryCount INT = 0;
SELECT @SequenceID = NEXT VALUE FOR dbo.RoundSlotSequence;
WHILE @SlotID IS NULL AND @TryCount < 50
BEGIN
UPDATE dbo.OpenRounds WITH (ROWLOCK)
SET ReferenceCount = ReferenceCount + 1
, @SlotID = OpenRoundID
, UserID = @UserID
, GameActivityID = @GameActivityID
, VendorID = @VendorID
, RoundDate = @RoundDate
, UserBonusID = @UserBonusID
, VendorRoundID = @VendorRoundID
WHERE ReferenceCount = 0
AND OpenRoundID = @SequenceID;
/* If @SlotID IS NULL the slot was not available
- wait 5 milliseconds before checking again
to see if the slot is open
*/
IF @SlotID IS NULL WAITFOR DELAY '00:00:00.005';
SET @TryCount += 1;
END
IF @SlotID IS NULL
RETURN 1
ELSE
RETURN @SlotID
END;
GO
このプロシージャは、OpenRoundsテーブルから次のメッセージを取得するために使用できます。通常、これは、処理する行を継続的に検索するループ内で実行されます。
CREATE PROCEDURE dbo.PopRound
(
@UserID INT OUTPUT
, @GameActivityID BIGINT OUTPUT
, @VendorID INT OUTPUT
, @RoundDate DATETIME OUTPUT
, @UserBonusID BIGINT OUTPUT
, @VendorRoundID NVARCHAR(50) OUTPUT
, @MaxRetries INT = 2000 /* 10 seconds
default maximum wait time */
)
AS
BEGIN
DECLARE @SlotID INT;
DECLARE @TryCount INT = 0;
WHILE @SlotID IS NULL AND @TryCount < @MaxRetries
BEGIN
UPDATE dbo.OpenRounds WITH (ROWLOCK)
SET ReferenceCount = ReferenceCount - 1
, @SlotID = OpenRoundID
, @UserID = UserID
, @GameActivityID = GameActivityID
, @VendorID = VendorID
, @RoundDate = RoundDate
, @UserBonusID = UserBonusID
, @VendorRoundID = VendorRoundID
WHERE ReferenceCount > 0;
IF @SlotID IS NULL WAITFOR DELAY '00:00:00.005';
SET @TryCount += 1;
END
END;
GO
この設計は、Chris Adkinによる Exadat.co.uk Super-scaling SQL Serverサイトで見た概念に大まかに基づいています。彼のサイトは、SQL Serverを限界まで押し上げることに関する優れた資料とガイダンスを提供しています。これには、ハードウェアとSQL Serverとの相互作用に関する非常に詳細な詳細が含まれます。私はクリスや彼のウェブサイトとは一切関係ありません。