Assertion SQL 2016 SQL Server: Fichier: <pageref.cpp>, ligne = 951 Échec de l'assertion

8

Je suis actuellement en train de mettre à niveau notre entrepôt de données de SQL 2012 à SQL 2016. J'ai mon ancien et mon nouveau DW fonctionnant côte à côte en parallèle.

Mon processus ETL (un cadre développé dans SSIS par un tiers) a fonctionné avec succès pendant plus de 2 ans en 2012 mais échoue en 2016. Jusqu'à présent, les bases de données et le processus ETL sont identiques.

Les deux serveurs sont des machines virtuelles exécutées sur VMWare. Old Server est Win 2008 avec 24 Go de RAM. SQL 2012 Std. Mem max réglé sur 16 Go. Le nouveau serveur est Win 2012 avec 64 Go de RAM. SQL 2016 dev. Mem max réglé sur 50 Go. Le nouveau DW exécute la version 13.0.1601.5 RTM Developer Edition (64 bits).

Lors de l'exécution de mon processus ETL, les étapes de chargement qui utilisent une fusion SQL dans une dimension ou une table de faits échouent avec l'erreur suivante.

Texte intégral:

DESCRIPTION: Assertion SQL Server: Fichier:, ligne = 951 Échec de l'assertion = 'IS_OFF (BUF_MINLOGGED, m_buf-> bstat) || pageModifyType! = PageModifyType_Contents || GetPagePtr () -> IsTextPage () '. Cette erreur peut être liée au timing. Si l'erreur persiste après la réexécution de l'instruction, utilisez DBCC CHECKDB pour vérifier l'intégrité structurelle de la base de données ou redémarrez le serveur pour vous assurer que les structures de données en mémoire ne sont pas corrompues.

Comme recommandé, j'ai exécuté DBCC et aucune erreur n'a été trouvée. J'ai également redémarré SQL. J'ai ensuite redémarré le processus ETL et j'ai eu la même erreur.

Mes recherches pour cette erreur montrent qu'il s'agissait d'une erreur connue dans SQL 2008, 2012 et 2014 et corrigée dans les correctifs suivants et les mises à jour cumulatives. donc je suis un peu surpris de le voir réapparaître en 2016.

Les liens que j'ai trouvés indiquent que cela affecte SSIS lorsque vous essayez de faire des insertions si la base de données est en modèle de récupération simple ou en bloc. (Je cours dans le modèle de récupération simple)

Une solution de contournement suggérée consiste à remplacer le modèle de récupération Db par FULL. J'ai essayé cela et cela fonctionne, mais ce n'est pas vraiment une solution pour un entrepôt de données.

Est-ce que quelqu'un d'autre a rencontré cela avec 2016?

Quelqu'un peut-il suggérer des solutions alternatives?

Mises à jour:

26/7/2016: J'ai appliqué la mise à jour critique KB3164398 (v13.0.1708.0) et le problème persiste.

27/7/2016: J'ai appliqué la mise à jour cumulative CU1 KB3164674 (v13.0.2149.0).

08/03/2016: Une erreur s'est produite pendant la nuit sur notre plus petit cube. CU1 n'a pas résolu le problème. Aujourd'hui, j'ai signalé le bogue sur MS Connect et j'ai également enregistré un appel de support avec Microsoft.

08/12/2016: MS-Support a répondu initialement, mais les réponses étaient "Nous n'avons pas de correctif pour cela". Le gars du support allait en discuter avec ses collègues et revenir vers moi. 8 jours plus tard, je n'ai pas eu de ses nouvelles.

Bien que je n'aie pas de «solution», nous avons trouvé une solution de contournement qui nous convenait. Voir ma réponse publiée.

29/9/2016. J'ai appliqué CU2 la semaine dernière. Le jeudi, nous avons accidentellement exécuté une ancienne version de la fusion qui a échoué à nouveau avec la même erreur. Donc .. CU2 ne l'a pas corrigé non plus.

23/1/2017 : J'ai appliqué 2016 SP1 CU1 et je crois que cela a résolu le problème. Plus précisément KB3205964

Sir jure beaucoup
la source

Réponses:

2

En regardant la base de connaissances, vous avez quelques options / solutions de contournement:

  1. Passez au modèle de récupération complète. Vous dites que «ce n'est pas vraiment une option pour un entrepôt», mais il s'agit simplement de configurer régulièrement les sauvegardes du journal des transactions, par exemple 15 minutes, puis de les éliminer. SSIS / Plans de maintenance ont des tâches de stock pour ce faire . Vous perdrez des transactions enregistrées en masse, mais je n'ai jamais trouvé que celles-ci ont fait une grande différence dans les temps d'exécution, juste la taille du journal. Vous pouvez même sauvegarder le journal sur nul que je ne décrirai pas ici. Si vous ne savez pas quoi faire, demandez à votre DBA local. L'espace disque et la conservation de la sauvegarde du journal des transactions sont des problèmes plus faciles à résoudre que des erreurs fatales. Lorsque ce problème est finalement résolu, vous pouvez revenir en arrière.
  2. Le KB mentionne "plusieurs instructions BULK INSERT dans une seule transaction distribuée". Votre question ne précise pas comment vos inserts en vrac sont configurés. Utilisez-vous SSIS pour exécuter des tâches «Exécuter SQL» qui utilisent leMERGEcommander? Que signifie «plusieurs inserts en vrac» ici? Existe-t-il un moyen de convertir votre approche en un seul BULK INSERT, un par un par exemple? Dans SSIS, vous pouvez définir les «MaxConcurrentExecutables» sur 1 temporairement par exemple, voir si cela aide. Associez-le à une variable de configuration afin de pouvoir le modifier plus tard. Évidemment, cela ralentira les choses, mais vous préférez que votre ETL se termine plutôt que d'échouer rapidement. Faire des choses en parallèle est un bon schéma et une véritable force de SSIS, mais vous ne pouvez aller aussi vite que votre composant le plus lent; disons que vous avez 10 dimensions qui prennent une minute et un fait qui prend une heure, votre ETL se termine en une heure en parallèle ou en 1 heure 10 en série.
  3. MERGEest agréable mais a quelques problèmes. Vous pourriez envisager de reconvertir en INSERT/ UPDATE. Vous devez également utiliser HOLDLOCKavec MERGEselon ici . Utilisez-vous cet indice? Cela fait-il une différence dans ce cas si vous le faites? Nous avons eu un problème dans une version SQL 2014 précoce où l'utilisation MERGEavec la OUTPUTclause DML ( clause) composable dans columnstore a provoqué ce type d'affirmation - je leur ai fait retirer les index columnstore des dimensions, qu'ils avaient ajoutées sans me le dire.
  4. Quel type de traitement des transactions faites-vous? Parfois, avec ETL, la position est reproductible en relançant simplement. Parfois, vous en avez besoin pour échouer et revenir en arrière. Comment avez-vous mis cela en œuvre? Peut-il être modifié de sorte qu'il ne s'agit pas d'une "transaction distribuée unique"?

Bonne chance.

wBob
la source
Salut @wBob merci pour votre réponse. Pour répondre à tes questions. 1. Nous parlons d'extraire des tables 20-30Gb en une seule étape. déplacer plus de 100 Go de données. Je suis préoccupé par la suppression des journaux TX et également du stockage. Peut-être que je pourrais faire de la sauvegarde du journal une étape dans l'ETL après chaque extrait?. 2. Oui, SSIS appelle une tâche SQL pour effectuer une fusion. Chaque étape est exécutée séquentiellement. Au moment où la fusion est en cours d'exécution, c'est la seule tâche à exécuter. 3. Notre outil ETL est un framework fourni par un fournisseur. Voilà comment cela fonctionne. Pas sûr des indices. vérifierai. 4. Aucune manipulation TX. SQL simple.
Sir Swears-a-lot
Donc, si les exécutions réussies ne se poursuivent pas, essayez un journal de 100 Go avec des sauvegardes de journal après chaque déplacement de table, cela devrait le trier. De toute évidence, vous devez tester. D'autres fonctionnalités que nous devrions connaître sur la cible? Columnstore, partitionnement, indexation excessive?
wBob
Actuellement, aucune fonctionnalité qui n'est pas déjà sur notre DW existant sur 2012 std. Les nouveaux DW / Datamarts sont une copie exacte des originaux, pas de magasin de colonnes ni de partitionnement. Index minimaux (mais efficaces). Nous nous concentrons actuellement sur un changement de plate-forme similaire, mais notre nouveau produit DW sera finalement 2016 Enterprise. Mais nous n'ajoutons pas de fonctionnalités de niveau Entreprise tant que les bases ne fonctionnent pas.
Sir Swears-a-lot
Salut @ wBob. Nous avons en quelque sorte mis en œuvre votre suggestion en supprimant l'insertion de la fusion. Mais étant donné la façon dont j'ai posé la question, je ne sais pas ce qui constitue une bonne réponse. mais je pense que vous m'avez conduit à une solution de contournement valide et réalisable.
Sir Swears-a-lot
2

Je crois que cela a été résolu dans 2016 SP1 CU1.

Spécifiquement par KB3205964

Sir jure beaucoup
la source
1

Ce problème est résolu en appliquant la mise à jour cumulative 1 (CU1) pour MSSQL2016 voir le lien https://support.microsoft.com/en-us/kb/3164674

Steve Neshausen
la source
Steve travaillait avec moi là-dessus. nous avons pensé l'avoir résolu mais pas de cigare. j'ai donc supprimé le crédit et voté.
Sir Swears-a-lot
1

Je pense que nous avons trouvé une autre solution. Je poste ma réponse car je pense qu'elle peut être utile, et elle est suffisamment différente de la suggestion de wBob.

Nous avons modifié la partie d'insertion de l'instruction de fusion afin qu'elle soit insérée dans une table temporaire plutôt que dans la cible d'origine.

Une fois l'instruction de fusion exécutée, nous l'insérons ensuite à partir de la table # dans la cible.

Ce n'est pas l'idéal, mais au moins la fusion gère toujours la complexité de l'upsert en marquant les lignes qui ont été retirées / expirées.

Nous avons constaté qu'il s'agissait d'un compromis acceptable par rapport à une réécriture complète de la fusion sous forme d'insertions et de mises à jour distinctes.

CREATE TABLE #Activity(
[ETL_ActivitySurrogateKey] [int] IDENTITY(1,1) NOT NULL,
[Field1] [varchar](255) NULL,
)

-- Insert statements for procedure here
INSERT INTO #Activity ( [Field1],2,3,etc )

SELECT [Field1],2,3,etc
FROM 
(
    MERGE [DDS_OZ_CC].[dimActivity] AS target 
    USING (
      SELECT [Field1],2,3,etc
      FROM [STAGE_OZ_CC].[Transform_Activity]
      ) as source
    ON
    (
      target.RowIsCurrent = source.RowIsCurrent
         AND target.[Field1] = source.[Field1]
    )
    WHEN MATCHED 
        AND (        
        EXISTS (SELECT target.Level5Id EXCEPT SELECT source.Level5Id)
    )
    THEN
      UPDATE SET 
        ETL_ValidToDateTime = source.ETL_ValidFromDateTime 
       ,ETL_RowIsCurrent = 0 
       ,ETL_LastTouchedBy = source.ETL_LastTouchedBy 
       ,ETL_RowChangeReason = 'SCD2 Retired' 

    WHEN NOT MATCHED THEN 
    INSERT 
    (    
     [Field1],2,3,etc
    )
    VALUES (      
      source.[Field1],2,3,etc
    )
       WHEN NOT MATCHED BY SOURCE AND target.ETL_RowIsCurrent = 1
       THEN UPDATE SET
       ETL_RowIsCurrent = 0 
       ,ETL_RowChangeReason = 'Fact Removed' 
       ,ETL_LastTouchedBy = 'Unknown'

  OUTPUT
    $action      
      ,source.[Field1],2,3,etc    

  ) AS MergeOutput
  (
    action  
    ,[Field1],2,3,etc   
  ) 

  WHERE ACTION = 'UPDATE' AND ETL_RowIsCurrent = 1

    INSERT INTO [DDS_OZ_CC].[dimActivity]
    ( [Field1],2,3,etc  )
    SELECT [Field1],2,3,etc
    FROM #Activity

    END
Sir jure beaucoup
la source
Ok merci Peter. Juste par intérêt, avez-vous essayé le conseil HOLDLOCK?
wBob
Non, je ne l'ai pas fait. Après avoir lu un article d'Aaron Bertrands à ce sujet, j'ai compris qu'il s'agissait vraiment de problèmes de concurrence avec plusieurs utilisateurs. Dans notre cas, le processus ETL est la seule chose à exécuter.
Sir Swears-a-lot
Je pense que cela valait la peine d'essayer juste pour voir si vous obtenez un comportement différent, en plus c'est probablement la meilleure pratique, juste pour dire que vous n'êtes pas concurrente maintenant ne signifie pas que vous pourriez ne pas l'être à l'avenir. Quoi qu'il en soit, bravo pour trouver une solution et pour le problème suivant
:)
J'aurais aimé le faire, mais malheureusement, nous manquons de temps. Une fois que nous avons trouvé une solution viable, le patron a appelé pour passer à autre chose.
Sir Swears-a-lot