J'ai cette table destinée à avoir beaucoup d'activité. Tout ce qui est dedans indique qu'une action de l'utilisateur est toujours en attente. Une fois supprimé, cela indiquerait que l'action n'est plus en instance. D'autres domaines du système en dépendent, de sorte que la table seule n'explique pas vraiment le scénario. Cependant, la question que j'ai est de savoir si je devrais garder la table comme ça et supprimer, lorsque l'action en attente est effectuée ou si je devrais ajouter une colonne de drapeau dans et mettre à jour.
Remarque, un enregistrement peut être supprimé dans la même seconde, il est inséré. J'espère soutenir jusqu'à 100 par seconde, mais je ne voudrais pas avoir cela comme la limite.
J'utilise SQL Server 2014 Enterprise Edition.
Voici la définition de la table (tous les index sont basés sur certaines requêtes à l'aide de ce tableau):
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
Si vous concevez votre système dans l'intention de soutenir des milliers de transactions par seconde, vous souhaitez probablement concevoir un système de file d'attente qui ne repose pas sur des inserts. Les inserts sont coûteux et en fonction de la conception de la table de file d'attente peuvent entraîner la dernière page de la table de file d'attente étant un "point chaud" qui limite la vitesse de débit pour le système.
La conception suivante permet un maximum de 100 000 transactions simultanées à travers la table des tours.
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
La séquence créée ci-dessus est conçue pour retirer une fois qu'elle atteint 100 000. Cela coïncide avec le nombre de lignes que nous avons créées dans le dbo.OpenRounds
tableau.
Pour pousser les messages dans la table OpenRounds, vous pouvez utiliser la procuration suivante:
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
Cette procédure peut être utilisée pour obtenir le prochain message de la table OpenRounds. Généralement, cela serait couru à l'intérieur d'une boucle qui recherche en permanence des lignes pour traiter:
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
Cette conception repose vague sur les concepts que j'ai vue à la exada.co.uk site SQL Server SQL, de Chris Adkin. Son site fournit des matériaux et des conseils exceptionnels autour de la poussée SQL Server à ses limites, notamment des détails très détaillés sur le matériel et la manière dont SQL Server interagit avec elle. Je ne suis pas associé de quelque manière que ce soit avec Chris ou son site Web.