Database
 sql >> Datenbank >  >> RDS >> Database

T-SQL Tuesday #33:Trick Shots:Schema Switch-A-Roo

Der T-SQL-Dienstag dieses Monats wird von Mike Fal (Blog | Twitter) moderiert, und das Thema ist Trick Shots, wo wir eingeladen sind, der Community von einer Lösung zu erzählen, die wir in SQL Server verwendet haben und die sich zumindest für uns so anfühlte. als eine Art "Trick-Shot" - ähnlich wie bei Massé, "English" oder komplizierten Bank-Shots beim Billard oder Snooker. Nachdem ich etwa 15 Jahre lang mit SQL Server gearbeitet habe, hatte ich die Gelegenheit, mir Tricks auszudenken, um einige ziemlich interessante Probleme zu lösen, aber einer, der ziemlich wiederverwendbar zu sein scheint, sich leicht an viele Situationen anpasst und einfach zu implementieren ist, ist es etwas, das ich "Schema switch-a-roo" nenne.

Nehmen wir an, Sie haben ein Szenario, in dem Sie eine große Nachschlagetabelle haben, die regelmäßig aktualisiert werden muss. Diese Nachschlagetabelle wird auf vielen Servern benötigt und kann Daten enthalten, die von einer externen Quelle oder einer Quelle eines Drittanbieters aufgefüllt werden, z. IP- oder Domänendaten oder können Daten aus Ihrer eigenen Umgebung darstellen.

Die ersten paar Szenarien, in denen ich dafür eine Lösung brauchte, waren die Bereitstellung von Metadaten und denormalisierten Daten für schreibgeschützte „Daten-Caches“ – eigentlich nur SQL Server MSDE-Instanzen (und später Express), die auf verschiedenen Webservern installiert waren, sodass die Webserver zurückgezogen wurden Dadurch werden Daten lokal zwischengespeichert, anstatt das primäre OLTP-System zu stören. Dies mag redundant erscheinen, aber das Auslagern der Leseaktivität vom primären OLTP-System und die Möglichkeit, die Netzwerkverbindung vollständig aus der Gleichung herauszunehmen, führte zu einem echten Leistungsschub und vor allem für die Endbenutzer .

Diese Server benötigten keine minutenaktuellen Kopien der Daten; Tatsächlich wurden viele der Cache-Tabellen nur täglich aktualisiert. Da die Systeme jedoch rund um die Uhr liefen und einige dieser Updates mehrere Minuten dauern konnten, standen sie echten Kunden oft im Weg, wenn sie echte Dinge auf dem System taten.

Der/die ursprüngliche(n) Ansatz(e)

Ganz am Anfang war der Code ziemlich einfach:Wir löschten Zeilen, die aus der Quelle entfernt worden waren, aktualisierten alle Zeilen, von denen wir erkennen konnten, dass sie sich geändert hatten, und fügten alle neuen Zeilen ein. Es sah etwa so aus (Fehlerbehandlung etc. der Kürze halber entfernt):

BEGIN TRANSACTION;
 
DELETE dbo.Lookup 
  WHERE [key] NOT IN 
  (SELECT [key] FROM [source]);
 
UPDATE d SET [col] = s.[col]
  FROM dbo.Lookup AS d
  INNER JOIN [source] AS s
  ON d.[key] = s.[key]
  -- AND [condition to detect change];
 
INSERT dbo.Lookup([cols]) 
  SELECT [cols] FROM [source]
  WHERE [key] NOT IN 
  (SELECT [key] FROM dbo.Lookup);
 
COMMIT TRANSACTION;

Unnötig zu erwähnen, dass diese Transaktion einige echte Leistungsprobleme verursachen kann, wenn das System verwendet wird. Sicherlich gab es andere Möglichkeiten, dies zu tun, aber jede Methode, die wir ausprobierten, war gleich langsam und teuer. Wie langsam und teuer? "Lass mich die Scans zählen..."

Da dieses MERGE vordatiert war und wir bereits „externe“ Ansätze wie DTS verworfen hatten, stellten wir durch einige Tests fest, dass es effizienter wäre, die Tabelle einfach zu löschen und neu zu füllen, anstatt zu versuchen, sie mit der Quelle zu synchronisieren :

BEGIN TRANSACTION;
 
TRUNCATE TABLE dbo.Lookup;
 
INSERT dbo.Lookup([cols]) 
  SELECT [cols] FROM [source];
 
COMMIT TRANSACTION;

Nun, wie ich bereits erklärt habe, könnte diese Abfrage von [source] ein paar Minuten dauern, insbesondere wenn alle Webserver parallel aktualisiert werden (wir haben versucht, zu staffeln, wo wir konnten). Und wenn ein Kunde auf der Website war und versuchte, eine Abfrage mit der Nachschlagetabelle auszuführen, musste er warten, bis diese Transaktion abgeschlossen war. Wenn sie diese Abfrage um Mitternacht ausführen, spielt es in den meisten Fällen keine Rolle, ob sie die Kopie der Lookup-Daten von gestern oder von heute erhalten. Sie auf die Aktualisierung warten zu lassen, schien also albern und führte tatsächlich zu einer Reihe von Supportanrufen.

Das war zwar besser, aber sicherlich alles andere als perfekt.

Meine anfängliche Lösung:sp_rename

Als SQL Server 2000 noch cool war, bestand meine ursprüngliche Lösung darin, eine „Schatten“-Tabelle zu erstellen:

CREATE TABLE dbo.Lookup_Shadow([cols]);

Auf diese Weise konnte ich die Schattentabelle füllen, ohne die Benutzer überhaupt zu unterbrechen, und dann eine Drei-Wege-Umbenennung durchführen – eine schnelle Operation, die nur Metadaten enthält – erst nachdem das Füllen abgeschlossen war. Etwa so (wieder grob vereinfacht):

TRUNCATE TABLE dbo.Lookup_Shadow;
 
INSERT dbo.Lookup_Shadow([cols]) 
  SELECT [cols] FROM [source];
 
BEGIN TRANSACTION;
 
  EXEC sp_rename N'dbo.Lookup',        N'dbo.Lookup_Fake';
  EXEC sp_rename N'dbo.Lookup_Shadow', N'dbo.Lookup';
 
COMMIT TRANSACTION;
 
-- if successful:
EXEC sp_rename N'dbo.Lookup_Fake', N'dbo.Lookup_Shadow';

Der Nachteil dieses anfänglichen Ansatzes war, dass sp_rename eine nicht unterdrückbare Ausgabemeldung hat, die Sie vor den Gefahren beim Umbenennen von Objekten warnt. In unserem Fall haben wir diese Aufgabe über SQL Server Agent-Aufträge ausgeführt und viele Metadaten und andere Cache-Tabellen verarbeitet, sodass der Auftragsverlauf mit all diesen nutzlosen Nachrichten überflutet wurde und tatsächlich dazu führte, dass echte Fehler aus den Verlaufsdetails abgeschnitten wurden. (Ich habe mich 2007 darüber beschwert, aber mein Vorschlag wurde letztendlich verworfen und als "Won't Fix" geschlossen.)

Eine bessere Lösung:Schemas

Nachdem wir auf SQL Server 2005 aktualisiert hatten, entdeckte ich diesen fantastischen Befehl namens CREATE SCHEMA. Es war trivial, denselben Lösungstyp mithilfe von Schemata zu implementieren, anstatt Tabellen umzubenennen, und jetzt würde der Verlauf des Agenten nicht mit all diesen nicht hilfreichen Nachrichten verschmutzt werden. Grundsätzlich habe ich zwei neue Schemas erstellt:

CREATE SCHEMA fake   AUTHORIZATION dbo;
CREATE SCHEMA shadow AUTHORIZATION dbo;

Dann habe ich die Lookup_Shadow-Tabelle in das Cache-Schema verschoben und umbenannt:

ALTER SCHEMA shadow TRANSFER dbo.Lookup_Shadow;
 
EXEC sp_rename N'shadow.Lookup_Shadow', N'Lookup';

(Wenn Sie diese Lösung nur implementieren, würden Sie eine neue Kopie der Tabelle im Schema erstellen und nicht die vorhandene Tabelle dorthin verschieben und umbenennen.)

Mit diesen beiden Schemas und einer Kopie der Nachschlagetabelle im Schattenschema wurde aus meiner Drei-Wege-Umbenennung eine Drei-Wege-Schemaübertragung:

TRUNCATE TABLE shadow.Lookup;
 
INSERT shadow.Lookup([cols]) 
  SELECT [cols] FROM [source];
 
-- perhaps an explicit statistics update here
 
BEGIN TRANSACTION;
 
  ALTER SCHEMA fake TRANSFER     dbo.Lookup;
  ALTER SCHEMA dbo  TRANSFER  shadow.Lookup;
 
COMMIT TRANSACTION;
 
ALTER SCHEMA shadow TRANSFER fake.Lookup;

An dieser Stelle können Sie natürlich die Schattenkopie der Tabelle leeren, aber in manchen Fällen fand ich es nützlich, die "alte" Kopie der Daten für Fehlerbehebungszwecke herumliegen zu lassen:

TRUNCATE TABLE shadow.Lookup;

Alles weitere, was Sie mit der Schattenkopie tun, sollten Sie sicherstellen, dass Sie es außerhalb der Transaktion tun – die beiden Übertragungsvorgänge sollten so prägnant und schnell wie möglich sein.

Einige Vorbehalte

  • Fremdschlüssel
    Dies funktioniert nicht standardmäßig, wenn die Nachschlagetabelle von Fremdschlüsseln referenziert wird. In unserem Fall haben wir keine Beschränkungen auf diese Cache-Tabellen hingewiesen, aber wenn Sie dies tun, müssen Sie sich möglicherweise an aufdringliche Methoden wie MERGE halten. Oder verwenden Sie Nur-Anfüge-Methoden und deaktivieren oder löschen Sie die Fremdschlüssel, bevor Sie Datenänderungen vornehmen (erstellen oder aktivieren Sie sie anschließend erneut). Wenn Sie bei MERGE / UPSERT-Techniken bleiben und dies zwischen Servern oder, noch schlimmer, von einem entfernten System aus tun, empfehle ich dringend, die Rohdaten lokal abzurufen, anstatt zu versuchen, diese Methoden zwischen Servern zu verwenden.
  • Statistiken
    Das Wechseln der Tabellen (durch Umbenennen oder Schemaübertragung) führt dazu, dass Statistiken zwischen den beiden Kopien der Tabelle hin und her springen, und dies kann offensichtlich ein Problem für Pläne sein. Sie können daher erwägen, explizite Statistikaktualisierungen als Teil dieses Prozesses hinzuzufügen.
  • Andere Ansätze
    Natürlich gibt es andere Möglichkeiten, dies zu tun, zu denen ich einfach noch keine Gelegenheit hatte, es auszuprobieren. Partitionswechsel und die Verwendung einer Ansicht + eines Synonyms sind zwei Ansätze, die ich in Zukunft untersuchen könnte, um das Thema gründlicher zu behandeln. Mich würden eure Erfahrungen interessieren und wie ihr dieses Problem in eurem Umfeld gelöst habt. Und ja, mir ist klar, dass dieses Problem größtenteils durch Verfügbarkeitsgruppen und lesbare Secondaries in SQL Server 2012 gelöst wird, aber ich halte es für einen „Trickschuss“, wenn Sie das Problem lösen können, ohne High-End-Lizenzen auf das Problem zu werfen oder eine zu replizieren gesamte Datenbank, um einige Tabellen überflüssig zu machen. :-)

Schlussfolgerung

Wenn Sie mit den Einschränkungen hier leben können, ist dieser Ansatz möglicherweise leistungsstärker als ein Szenario, in dem Sie im Wesentlichen eine Tabelle mithilfe von SSIS oder Ihrer eigenen MERGE / UPSERT-Routine offline schalten, aber stellen Sie sicher, dass Sie beide Techniken testen. Der wichtigste Punkt ist, dass der Endbenutzer, der auf den Tisch zugreift, zu jeder Tageszeit genau dieselbe Erfahrung haben sollte, selbst wenn er mitten in Ihrem regelmäßigen Update auf den Tisch trifft.