web-dev-qa-db-fra.com

Le service de service est-il le meilleur choix pour l'audit des modifications de données sur SQL Server Express?

Mon projet est d'audit 5 à 10 tables existantes dans notre système sans prolonger les transactions. Quelle que soit la méthode utilisée, elle doit fonctionner sur SQL Server Express 2005 à (éventuellement) 2016.

J'ai effectué des recherches sur Change Capture de données (CDC) et modifier le suivi. Modifier le suivi ne capture pas les modifications spécifiques et CDC n'est disponible que dans l'édition Enterprise.

Ensuite, j'ai trébuché sur le courtier de service. J'ai été intrigué par le courtier de service, alors j'ai commencé à créer un prototype. Le courtier de service fonctionne bien, mais les réponses reçues dans deux de mes autres postes me conduisent à croire que cela pourrait ne pas être la bonne façon d'y aller. Trop compliqué pour rien. Je suis toujours dans la phase d'analyse et j'essaie différentes choses dans le cadre de mon analyse.

À l'heure actuelle, les résultats du courtier de service ne sont pas convaincants ... Une mise à jour en vrac de 105 000 articles à une table de prix prend 38 secondes tandis que le traitement de la file d'attente (la partie d'audit) prend 17 secondes ... mais les 38 secondes incluent Le double traitement de l'insertion dans 2 #TEpp Tables qui sont ensuite utilisés pour insérer dans les TMPINS et TMPDel. Donc, je pense que je peux couper cela en deux ... Je suis maintenant interrogé sur l'utilisation du courtier de service ... logiquement, le déclencheur prendrait probablement le même temps simplement en insérant l'info directement dans la table d'audit. .

Pour clarifier, quand je dis Insert en vrac, il n'est pas la fonction "insert en vrac". Je parle d'un gros morceau de données insérées ou mises à jour à la fois. Lors de la mise à jour de 105 000 articles dans la table de prix, je souhaite auditer les modifications apportées. Lorsque je dis des changements qui se produisent, j'ai décidé d'insérer les nouvelles valeurs de la table d'audit (s'il s'agit d'un insertion ou de sa mise à jour) ou d'insérer la touche principale avec tous les autres champs NULL (pour les enregistrements qui ont été supprimés) ... alors oui! Il peut être après que les données sont chargées, mais je ne veux pas perdre d'audit (je ne veux pas de transmettre de transactions à l'emploi)

Les deux autres postes aideront à obtenir le contexte de ce que j'essaie de faire et de ce que j'ai essayé:

J'apprécie chaque idée.

4
JohnG

Je suppose que le message suivant est la base de ce que vous utilisez actuellement: Audit asynchrone centralisé avec courtier de service .

Bien que j'aime vraiment le courtier de service, je ne pense pas que c'est le meilleur ajustement pour répondre à cette situation particulière. Les principales préoccupations que j'ai avec le courtier de service, du moins dans ce scénario particulier, sont les suivantes:

  • Il est probablement trop compliqué s'il est utilisé principalement pour séparer l'événement DML de l'événement d'audit. Si vous ne déplacez pas les données sur un autre système, il ne semble pas offrir beaucoup d'avantages car vous aurez toujours besoin de persister les tables INSERTED et DELETED dans le déclencheur DML.
  • Il est basé sur l'événement et les événements varient considérablement dans leur taille et leur fréquence. Vous pourriez avoir un million d'opérations à 1 enregistrement, ou une ou deux 1 million d'opérations record. Et puisque chaque événement est chaque opération DML individuelle, vous n'avez aucune occasion de supprimer des entrées en double et/ou négativement dans plusieurs opérations DML.

Ma préférence est de jeter les changements dans une table de file d'attente, puis dans un processus distinct programmé pour exécuter toutes les minutes de X, lisez-y le nombre de lignes et les traiter.

  1. Créez une table d'attente pour contenir les données d'audit pour une table particulière (non pour chaque opération DML individuelle). La table devrait avoir:

    • l'option LOCK_ESCALATION définie sur DISABLE (via ALTER TABLE {name} SET (LOCK_ESCALATION = DISABLE)) Pour éviter tout conflit entre la journalisation des nouvelles données par la gâchette et la suppression des données du traitement de l'audit. Cette option a été introduite dans SQL Server 2008, il ne peut donc pas être utilisé sur les cas de 2005, mais aucune raison de ne pas l'utiliser dans les instances de 2008 et plus récentes, car elle ne modifie pas la fonctionnalité dans les deux cas.
    • un AuditID pk qui est l'une des suivantes:
      • un INT IDENTITY à partir de -2147483648
      • un BIGINT IDENTITY
      • un INT avec sa valeur provenant d'un SEQUENCE qui est défini sur CYCLE
    • seuls les champs que vous suivez changements, sinon tous
    • champs pour "Old" et "Nouveau" Si vous devez garder une trace des valeurs avant et après chaque fois que vous traitez. En fonction de la manière dont vous avez des changements d'audit, si vous avez une archive de valeurs antérieures, vous ne devez avoir besoin que des "nouvelles" valeurs de la table INSERTED puisque les valeurs de la table DELETED devraient déjà être dans vos données d'audit préalables.
  2. Créez un déclencheur qui insère simplement dans la table d'attente. Si vous avez besoin de transmettre les "anciennes" et "nouvelles" valeurs, rejoignez les tables INSERTED et DELETED tables ici plutôt que d'essayer de conserver deux tables de file d'attente distinctes, une pour chacune de ces pseudo-tables . En y rejoignant à ce stade et insérer à la fois les "anciennes" et "nouvelles" valeurs en une seule ligne est une légère performance frappée, mais garantira que chaque opération reste ensemble et ​​dans l'ordre chronologique (via l'incrémentation PAQUET).

    Si vous ne suivez pas de changements sur tous les champs, utilisez Soit mise à jour () ou colonnes_updated () Pour déterminer si les colonnes étant auditées ont bien été mises à jour ( Pour UPDATE Opérations; ces fonctions renvoient true pour toutes les colonnes de INSERT opérations). N'oubliez pas que la fonction UPDATE() et COLUMNS_UPDATED() fonctions font non Déterminez si un changement a été effectué dans la valeur de la colonne (s)! Ils ne rapportent que si les colonnes étaient présentes dans la clause SET de la déclaration UPDATE. Le seul moyen de déterminer si la valeur de la valeur a été modifiée consiste à rejoindre les tables INSERTED et DELETED tables. Mais si vous ne suivez pas toutes les colonnes, ces fonctions sont excellentes pour la sortie de la gâchette sans faire de travail si aucune colonne de suivi n'a changé. Signification, au début de la gâchette, vous feriez:

    IF (NOT UPDATE(TrackedColumn1) AND NOT UPDATE(TrackedColumn2))
    BEGIN
      RETURN; -- no changes so just exit
    END;
    

    Si vous ne capturez pas les "anciennes" et "nouvelles" valeurs dans la table de file d'attente, c'est la seule occasion d'éliminer les enregistrements "mis à jour" où aucune colonne n'a été modifiée. Lorsque vous insérez dans la table de file d'attente, vous venez de filtrer sur WHERE IntColOld <> IntColNew OR StringFieldOld <> StringFieldNew COLLATE Latin1_General_BIN2 OR ISNULL(DateFieldOld, '1900-01-01') <> ISNULL(DateFieldNew, '1900-01-01') OR .... Il est important d'utiliser une collation de _BIN2 sur les champs de chaîne pour vous assurer que les deux champs sont en fait identiques. Et vous devez utiliser INSULL pour les champs nullables, de sorte qu'ils puissent assimiler si les deux sont NULL.

  3. Créez une procédure stockée qui fonctionnera pendant x secondes et, à cette époque, traitera des ensembles de TOP(@BatchSize) lignes, à l'aide de ORDER BY AuditID ASC. Vous faites cela par une boucle WHILE qui vérifie GETDATE() contre @StartTime qui a été définie au début de la procédure stockée. Selon le traitement que vous devez faire, il est parfois plus facile de créer une table temporaire locale à INSERT INTO #TempTable SELECT... l'ensemble de travail (alors vous aurez à DELETE ces lignes à la fin de chaque boucle) ou DELETE FROM à l'aide de OUTPUT INTO #TempTable .

    Ici vous pouvez supprimer des modifications de modifications. Si vous suivez des valeurs "anciennes" et "nouvelles" par chaque ligne, vous devez également éliminer les lignes où aucune des colonnes n'a été modifiée. Vous pouvez le faire en testant WHERE IntColOld = IntColNew AND StringFieldOld = StringFieldNew COLLATE Latin1_General_BIN2 AND .... Il est important d'utiliser une collation de _BIN2 sur les champs de chaîne pour vous assurer que les deux champs sont en fait identiques. Collations non binaires, même si sensibles à la casse et sensible à l'accent, etc., permettent toujours de comparer les normalisations linguistiques qui doivent comparer la même chose pour des comparaisons régulières, mais pas lors de l'audit. N'utilisez pas _BIN Collations car ils ont été obsolètes depuis que SQL Server 2005, qui est lorsque les collations _BIN2 sont sorties.

    Tout fait à l'intérieur de la boucle doit être dans un explicity BEGIN TRAN/COMMIT/ROLLBACK. Utilisez un bloc d'essai/attraper pour gérer cela. Cela empêchera de perdre des enregistrements ou de le traitement deux fois.

  4. Planifiez la procédure stockée pour exécuter toutes les minutes. Typiquement, cela se fait via A SQL Server Agent Job, mais SQL Server Agent n'est pas disponible pour les éditions Express. Dans ce cas, vous pouvez utiliser le fichier Windows Task Scheduler ou obtenir quelque chose comme Quartz.net .

La mise en place d'un processus de cette mode vous permet d'avoir un processus plus cohérent et plus utile et synonyme qui devrait suivre régulièrement X enregistrements toutes les minutes. Donc, peu importe si vous avez 1 million d'opérations DML à une seule rangée ou une seule opération DML de 1 million de lignes; Ce processus ne fera que garder la brutalité, faire de quoi il fait.

5
Solomon Rutzky