Umgang mit Identitätsbereichen für die Transaktionsreplikation

9

Ich habe festgestellt, dass SQL Server beim Einrichten einer Transaktionsreplikation die Verwaltung des Identitätsbereichs auf manuell setzt. Dies bedeutet, dass in meiner Abonnementdatenbank beim Versuch, einen neuen Datensatz in eine Tabelle einzufügen, deren PK eine Identitätsspalte ist, eine Fehlermeldung angezeigt wird und angegeben wird, dass versucht wurde, eine PK von "1", "2" einzufügen "," 3 "usw. Dies liegt daran, dass der aktuelle Identitätswert für alle Identitätsspalten auf dem Abonnenten auf den Startwert (normalerweise 1) zurückgesetzt wird, anstatt auf dem Stand des Herausgebers zu bleiben.

Ich verstehe, warum SQL Server dies tut - Sie sollten die Abonnententabelle schreibgeschützt lassen. Mein Szenario ist jedoch etwas unorthodox - ich aktualisiere meinen Abonnenten von Zeit zu Zeit durch Replikation, mache eine sofortige Sicherung dieser Datenbank und möchte dann einige Aktualisierungen für den Abonnenten vornehmen, die dann NICHT an den Herausgeber zurückgeschickt werden Wenn ich den Abonnenten erneut aktualisiere, stelle ich seine Datenbank aus der früheren Sicherung wieder her und rufe die neuesten Updates ab. Da ich zwischen diesen Aktualisierungen Aktualisierungen für den Abonnenten durchführen möchte (wenn Sie so wollen, "temporäre Deltas"), muss die Identitätsspalte funktionieren und beim Replizieren nicht auf 1 zurückgesetzt werden.

Ich habe versucht, die automatische Verwaltung des Identitätsbereichs beim Einrichten meiner Publikation zu aktivieren. Dabei wird jedoch nur der folgende Fehler angezeigt, wenn ich versuche, der Publikation eine Tabelle hinzuzufügen:

Meldung 21231, Ebene 16, Status 1, Prozedur sp_MSrepl_addarticle, Zeile 2243 Die
automatische Unterstützung des Identitätsbereichs ist nur für Veröffentlichungen nützlich, in denen Abonnenten aktualisiert werden können.

Gibt es eine Möglichkeit, dieses Problem zu umgehen? Ich möchte diese Replikation SQL Server so präsentieren, als wäre sie auf Abonnentenseite schreibgeschützt, da ich nicht vorhabe, Aktualisierungen vorzunehmen , die an den Herausgeber zurückgesendet werden , aber ich möchte vorübergehende Aktualisierungen vornehmen wird vor der nächsten Replikation gelöscht.

Ich habe auch in Betracht gezogen, dass die Snapshot-Replikation für mein Verwendungsmuster eine geeignetere Methode als die Transaktionsreplikation sein könnte, aber das Problem ist, dass bei der Snapshot-Replikation bei jedem einzelnen Update die gesamte verdammte Datenbank gesendet werden muss. Da ich vorhabe, nach der letzten Replikation sofort eine Sicherungskopie der Datenbank zu erstellen, sollte ich nicht jedes Mal die gesamte Übertragung durchführen müssen. nur die Änderungen seit dem letzten Mal.

Jez
quelle
Welche Version von SQL Server verwenden Sie? Können Sie die Tabelle neu definieren?
2008 r2. Ich sehe nicht, wie eine Neudefinition der Tabelle dieses Problem lösen würde ...
Jez
Ich habe über eine Lösung mit SEQUENCE nachgedacht, aber das ist nur für SQL 2012.
2
Is there any way I can get round this problem?Sie müssen die Identitätsspalte mit sys.sp_identitycolumnforreplication für SQL Server 2005 und höher als NOT FOR REPLICATION festlegen . Sie müssen Ihre Artikel nicht einmal erneut aufnehmen, wenn Sie die Identitätsspalte als nicht für die Replikation ändern. Mach es einfach nicht mit der GUI.
Kin Shah
Es ist bereits als nicht für die Replikation markiert. Das ist im Grunde das Problem - SQL Server kopiert die Identitätsinformationen nicht über den Abonnenten, sondern beginnt am 1.
Jez

Antworten:

3

Angenommen, Ihr Publisher verwendet eine int-Identität, die bei 1 beginnt, können Sie dies DBCC CHECKIDENT('dbo.mytable', RESEED, -2147483648) beim Abonnenten ausstellen . Sie können dann den Bereich von -2147483648 bis 0 verwenden, um Ihre "temporären Deltas" zu speichern.

Liam Confrey
quelle
Dies ist die Lösung, die ich mir ausgedacht habe, aber es bedeutet immer noch, dass mein Code eine Verbindung zum Herausgeber und Abonnenten herstellt und die Identitäten manuell synchronisiert. Ich hatte gehofft, dass es einen automatischeren Weg gibt, dies zu tun.
Jez
Warum sollten Sie die Identitäten manuell synchronisieren müssen? Schreiben Sie einfach eine gespeicherte Prozedur auf den Abonnenten, die checkident für jede Tabelle ausführt, in der Sie temporäre Delta speichern, und führen Sie sie aus, nachdem der Snapshot die Anwendung abgeschlossen hat. Der Verteilungsagent fügt Änderungen ein, sobald sie im "echten" Identitätsbereich auftreten, und die Änderungen, die direkt am Abonnenten vorgenommen werden, liegen im negativen Bereich.
Liam Confrey
1

Am Ende blieb ich bei einer Pull-basierten Transaktionsreplikation und ließ mein Programm die Abonnentenidentitätswerte so aktualisieren, dass sie unmittelbar nach der Synchronisierung mit denen in der Veröffentlichungsdatenbank übereinstimmen (so wie ich es mir wünschte, dass der Distributionsagent von sich aus tat ). Im Pseudocode sah es ein bisschen so aus:

synchronize databases with TransSynchronizationAgent

equivalentTablesNotFound is a list of strings
for each table in publisher tables:
    try:
        check table identity value (this is via functionality provided by .NET's Microsoft.SqlServer.Management.Smo.Server class)
        parse identity value as integer to newIdentity
        if the table's identity value was NULL, skip to next loop iteration
        (HACK) increment newIdentity value by 1
        if there is no subscriber table with the same name as this one:
            record its name in equivalentTablesNotFound and skip to next loop iteration
        set subscriber table with same name's identity value to newIdentity using TSQL: DBCC CHECKIDENT ("tableName", newIdentity)
    catch:
        if exception shows that the error was because the table doesn't have an identity column, drop the exception

if equivalentTablesNotFound has more than zero entries, warn about tables on publisher without an equivalent name on subscriber

Scheint in Ordnung zu funktionieren. Das HACK-Bit ist darauf zurückzuführen, dass der Identitätswert standardmäßig und bei allen meinen Tabellen nur um eins erhöht wird. Er kann jedoch unterschiedlich konfiguriert werden. Technisch gesehen sollten Sie hier herausfinden, wie der Identitätswert in der Herausgebertabelle inkrementiert wird, und ihn erhöhen gleicher Weg.

Jez
quelle
0

Meine bevorzugte Methode, um damit umzugehen, ist Folgendes:

ein. Stoppen Sie zuerst Ihren Replikationsagenten (damit Sie keine neuen Daten in Ihre Abonnenten-Datenbank erhalten).

b. Zweitens benennen Sie Ihre vorhandene Tabelle um

exec sp_rename '[CurrentTable]', '[BackupTableName]'

c. Erstellen Sie Ihre Tabelle mit festgelegtem IDENTITY neu

CREATE TABLE [CurrentTable]
(
   ID INT NOT NULL IDENTITY(1,1), 
   OtherField VARCHAR(10) NULL,
   ....
)

d. Füllen Sie Ihre Tabelle (aus dem [BackupTableName]) mit SET IDENTITY_INSERT

SET IDENTITY_INSERT [CurrentTable] ON
INSERT INTO [CurrentTable] (ID, OtherField, ...)
SELECT ID, OtherField, ....
FROM [BackupTableName]
SET IDENTITY_INSERT [CurrentTable] OFF

Sobald Sie die IDENTITY- Einschränkung für Ihre Datenbank haben , können Sie entweder eine benutzerdefinierte Replikation durchführen (dh Ihren Insert-Repl-Prozess in SET IDENTITY_INSERT [TableName] ON ändern oder das NOT NOT REPLICATION-Flag für die Tabelle setzen (das SQL Server dies mitteilt) Wenn der verbindende Benutzer der Replikationsagent ist, erwarten Sie, dass der IDENTITY-Wert angegeben wird. ( Ich bevorzuge den benutzerdefinierten Replikationsansatz, da er mir mehr Flexibilität bietet. )

e. Ändern Sie die gespeicherte Prozedur zum Einfügen der Replikation (normalerweise sp_MSins_CurrentTable), um sie auch mit einzufügenSET IDENTITY INSERT

ALTER procedure [dbo].[sp_MSins_CurrentTable]
    @c1 int, @c2 varchar(50), ...
as
begin
    /* allow replication to insert values for IDENTITY */
    SET IDENTITY_INSERT [CurrentTable] ON
    insert into [CurrentTable]
        ([ID], [OtherField], ...)
    values
        (@c1, @c2, ...)
    /* now turn off Identity insert */
    SET IDENTITY_INSERT [CurrentTable] OFF
end

f. Jetzt können Sie Ihren Replikationsagenten neu starten.

Andrew Bickerton
quelle
1
lol, im Vergleich zur Verwendung DBCC CHECKIDENTist diese Methode eine enorme Menge an Arbeit.
Jez
@Jez Sie müssen die Tabelle (mit IDENTITY) neu erstellen, um den DBCC-CHECKIDENT auszuführen ... Ein Snapshot der Replikation erstellt die Tabelle ohne die IDENTITY-Einschränkung (basierend auf Ihrem q Ich würde sagen, der DBCC-CHECKIDENT hat gewonnen 't work)
Andrew Bickerton
Zu Ihrer Information, es hat funktioniert und die Replikation erstellt die Tabelle mit der IDENTITY-Einschränkung ...
Jez
@Jez welche Art von Replikation hast du eingerichtet? (Wenn Sie es als MERGE einrichten, wird dies normalerweise bei TRANSACTIONAL nicht der Fall sein, obwohl die Replikation in hohem Maße anpassbar ist, wenn Sie die GUI nicht verwenden.)
Andrew Bickerton
Transaktion. Wie gesagt, die IDENTITÄT ist da, aber der aktuelle Identitätswert wird auf den Startwert (1) zurückgesetzt.
Jez