Je teste des insertions de journalisation minimales dans différents scénarios et à partir de ce que j'ai lu INSERT INTO SELECT dans un tas avec un index non clusterisé à l'aide de TABLOCK et SQL Server 2016+ devrait se connecter de manière minimale, mais dans mon cas, lorsque je fais cela, je reçois journalisation complète. Ma base de données est dans le modèle de récupération simple et j'ai réussi à obtenir des insertions enregistrées de manière minimale sur un tas sans index et TABLOCK.
J'utilise une ancienne sauvegarde de la base de données Stack Overflow pour tester et j'ai créé une réplique de la table Posts avec le schéma suivant ...
CREATE TABLE [dbo].[PostsDestination](
[Id] [int] NOT NULL,
[AcceptedAnswerId] [int] NULL,
[AnswerCount] [int] NULL,
[Body] [nvarchar](max) NOT NULL,
[ClosedDate] [datetime] NULL,
[CommentCount] [int] NULL,
[CommunityOwnedDate] [datetime] NULL,
[CreationDate] [datetime] NOT NULL,
[FavoriteCount] [int] NULL,
[LastActivityDate] [datetime] NOT NULL,
[LastEditDate] [datetime] NULL,
[LastEditorDisplayName] [nvarchar](40) NULL,
[LastEditorUserId] [int] NULL,
[OwnerUserId] [int] NULL,
[ParentId] [int] NULL,
[PostTypeId] [int] NOT NULL,
[Score] [int] NOT NULL,
[Tags] [nvarchar](150) NULL,
[Title] [nvarchar](250) NULL,
[ViewCount] [int] NOT NULL
)
CREATE NONCLUSTERED INDEX ndx_PostsDestination_Id ON PostsDestination(Id)
J'essaie alors de copier le tableau des posts dans ce tableau ...
INSERT INTO PostsDestination WITH(TABLOCK)
SELECT * FROM Posts ORDER BY Id
En regardant fn_dblog et l'utilisation du fichier journal, je peux voir que je n'obtiens pas de journalisation minimale à partir de cela. J'ai lu que les versions avant 2016 nécessitent l'indicateur de trace 610 pour se connecter de manière minimale aux tables indexées, j'ai également essayé de définir cela, mais toujours pas de joie.
Je suppose que je manque quelque chose ici?
EDIT - Plus d'informations
Pour ajouter plus d'informations, j'utilise la procédure suivante que j'ai écrite pour essayer de détecter une journalisation minimale, peut-être que j'ai quelque chose de mal ici ...
/*
Example Usage...
EXEC sp_GetLogUseStats
@Sql = '
INSERT INTO PostsDestination
SELECT TOP 500000 * FROM Posts ORDER BY Id ',
@Schema = 'dbo',
@Table = 'PostsDestination',
@ClearData = 1
*/
CREATE PROCEDURE [dbo].[sp_GetLogUseStats]
(
@Sql NVARCHAR(400),
@Schema NVARCHAR(20),
@Table NVARCHAR(200),
@ClearData BIT = 0
)
AS
IF @ClearData = 1
BEGIN
TRUNCATE TABLE PostsDestination
END
/*Checkpoint to clear log (Assuming Simple/Bulk Recovery Model*/
CHECKPOINT
/*Snapshot of logsize before query*/
CREATE TABLE #BeforeLogUsed(
[Db] NVARCHAR(100),
LogSize NVARCHAR(30),
Used NVARCHAR(50),
Status INT
)
INSERT INTO #BeforeLogUsed
EXEC('DBCC SQLPERF(logspace)')
/*Run Query*/
EXECUTE sp_executesql @SQL
/*Snapshot of logsize after query*/
CREATE TABLE #AfterLLogUsed(
[Db] NVARCHAR(100),
LogSize NVARCHAR(30),
Used NVARCHAR(50),
Status INT
)
INSERT INTO #AfterLLogUsed
EXEC('DBCC SQLPERF(logspace)')
/*Return before and after log size*/
SELECT
CAST(#AfterLLogUsed.Used AS DECIMAL(12,4)) - CAST(#BeforeLogUsed.Used AS DECIMAL(12,4)) AS LogSpaceUsersByInsert
FROM
#BeforeLogUsed
LEFT JOIN #AfterLLogUsed ON #AfterLLogUsed.Db = #BeforeLogUsed.Db
WHERE
#BeforeLogUsed.Db = DB_NAME()
/*Get list of affected indexes from insert query*/
SELECT
@Schema + '.' + so.name + '.' + si.name AS IndexName
INTO
#IndexNames
FROM
sys.indexes si
JOIN sys.objects so ON si.[object_id] = so.[object_id]
WHERE
si.name IS NOT NULL
AND so.name = @Table
/*Insert Record For Heap*/
INSERT INTO #IndexNames VALUES(@Schema + '.' + @Table)
/*Get log recrod sizes for heap and/or any indexes*/
SELECT
AllocUnitName,
[operation],
AVG([log record length]) AvgLogLength,
SUM([log record length]) TotalLogLength,
COUNT(*) Count
INTO #LogBreakdown
FROM
fn_dblog(null, null) fn
INNER JOIN #IndexNames ON #IndexNames.IndexName = allocunitname
GROUP BY
[Operation], AllocUnitName
ORDER BY AllocUnitName, operation
SELECT * FROM #LogBreakdown
SELECT AllocUnitName, SUM(TotalLogLength) TotalLogRecordLength
FROM #LogBreakdown
GROUP BY AllocUnitName
Insertion dans un tas sans index et TABLOCK à l'aide du code suivant ...
EXEC sp_GetLogUseStats
@Sql = '
INSERT INTO PostsDestination
SELECT * FROM Posts ORDER BY Id ',
@Schema = 'dbo',
@Table = 'PostsDestination',
@ClearData = 1
J'obtiens ces résultats
À une croissance de fichier journal de 0,0024 Mo, de très petites tailles d'enregistrement de journal et très peu d'entre eux, je suis heureux que cela utilise une journalisation minimale.
Si je crée ensuite un index non clusterisé sur id ...
CREATE INDEX ndx_PostsDestination_Id ON PostsDestination(Id)
Ensuite, exécutez à nouveau mon même insert ...
Non seulement je ne reçois pas de journalisation minimale sur l'index non cluster, mais je l'ai également perdu sur le tas. Après avoir fait plus de tests, il semble que si je fais un ID en cluster, il se connecte de manière minimale, mais d'après ce que j'ai lu 2016+, il devrait se connecter de manière minimale à un tas avec un index non cluster lorsque le tablock est utilisé.
MODIFICATION FINALE :
J'ai signalé le comportement à Microsoft sur UserVoice de SQL Server et je mettrai à jour si j'obtiens une réponse. J'ai également écrit tous les détails des scénarios de journalisation minimaux que je n'ai pas pu travailler sur https://gavindraper.com/2018/05/29/SQL-Server-Minimal-Logging-Inserts/