SQL Server: Verwenden von zwei Datenbanken für die Leistung?

7

Wir haben eine SQL-Datenbank, in der Anwendungsnutzungsprotokolle für etwa 3000 PCs gespeichert sind. Diese PCs senden ihre Nutzungsdaten etwa 10 bis 20 Mal pro Tag an den SQL Server. Früher haben wir nur die letzten 60 Tage der Anwendungsnutzung gespeichert, aber der Kunde hat uns gebeten, keine Daten mehr zu löschen. Jetzt, da wir Daten im Wert von ungefähr einem Jahr (ungefähr 6.000.000 Zeilen) haben, leidet die SQL-Datenbank unter einigen Leistungsproblemen. Wohlgemerkt nicht signifikant, aber weitaus mehr als jede andere Datenbank, die wir haben. Pro Stunde wird eine erhebliche Anzahl von Datensätzen hinzugefügt ( offene Datensätze für Anwendungen ). Innerhalb weniger Stunden wird dieser Datensatz nur einmal aktualisiert, wenn die zugehörige Anwendung geschlossen wird . Es sind diese Updates, die Sie über SQL Activity Monitor sehen können, deren Fertigstellung viel Zeit in Anspruch nimmt.

Diese UPDATE-Abfrage ist einfach:

SELECT TOP 1 f_ID 
from tb_applicationusage 
WHERE f_application = 'xxxxxxx' AND 
      f_computername = 'xxxxxxxxx' AND 
      f_endtime IS NULL 
ORDER BY f_starttime DESC

Tatsächlich wird der letzte übereinstimmende Anwendungsstart für einen bestimmten Computer gefunden, dem noch keine zugeordnete Anwendung geschlossen ist. Ich kann mir keine effizientere Methode zum Ausführen der Abfrage vorstellen. Daher erwäge ich die folgende Alternative:

Wechseln Sie zu zwei Datenbanken:

  1. Arbeitsdatenbank mit nur den neuesten 24-Stunden-Datensätzen
  2. Endgültige Datenbank mit allen anderen Datensätzen

Ich bin kein SQL-Guru, daher fehlen mir wahrscheinlich einige Nachteile dieser Methode. Das Ziel wäre, dass ein SQL Agent-Job die abgeschlossenen Datensätze jede Nacht in die endgültige Datenbank verschiebt. Wenn der Kunde dann seine monatlichen Berichte ausführen möchte, kann dieser Bericht nur die endgültige Datenbank und nicht die Arbeitsdatenbank abfragen. Mit nur vielleicht 10.000 Datensätzen, die in der Arbeitsdatenbank abgefragt werden müssen, anstatt 6.000.000, erscheint es logisch, dass es schneller funktionieren würde. Aber auch hier scheint es so einfach zu sein, dass mir wahrscheinlich etwas Offensichtliches fehlt.

Version: Microsoft SQL Server 2008 R2

Beems
quelle
3
Auf welchen Indizes INDEXgibt es tb_applicationusage?
TT.
1
Können Sie uns einen Beispiel-Abfrageplan und die Tabellendefinition geben? Das Beispiel ist auch eine select-Anweisung, kein Update. Im Allgemeinen ist es besser, uns die genaue Abfrage zu geben, die das Problem verursacht, als eine ähnliche. Manchmal steckt der Teufel im Detail und Ihre Probe zeigt nicht die gleiche langsame Leistung.
Erik

Antworten:

5

Sie können es besser machen als zwei Datenbanken. Es gibt zwei Dinge, die Sie in Ihrer vorhandenen Datenbank beachten sollten, bevor Sie einen Teil der alten Daten entfernen.

  1. Wählen Sie einen guten Clustered-Index. Es gibt drei Regeln, die Sie befolgen sollten, damit der Clustered-Index mit diesen Daten gut funktioniert:

    1. Es sollte ein zunehmender Wert verwendet werden, damit neue Datensätze immer in Clusterreihenfolge oder zumindest auf der letzten Seite am Ende der Tabelle stehen. Dies ist besonders wichtig, wenn Sie wie in diesem Fall viele Einsätze haben. So etwas wie ein Identitäts- / Autoinkrementierungsfeld, aber Sie werden gleich sehen, warum wir es besser machen können.
    2. Der Datensatz sollte eindeutig oder nahezu eindeutig identifiziert werden, damit die Aktualisierungen für die Anwendungen zum Schließen von Datensätzen schnell erfolgen.
    3. Sie sollten in der Lage sein, den Clustered-Index anhand der in der Datenbank eingehenden Anwendungsabschlussdatensätze zu kennen (dies schließt die Identitätsspalte von früher aus).
    4. Sie möchten nicht, dass durch die Anwendung geschlossene Datensätze Teil des Index sind, da dies dazu führen kann, dass die Datenbank den Datensatz bei Aktualisierungen an einen neuen Speicherort auf der Festplatte verschieben muss.

    Wenn es einen zunehmenden Zeitstempel gibt (dh :) f_starttime, kann dies für das erste Feld im Index gut sein, sofern es auch Teil des Abschlussdatensatzes ist, wie in Anforderung 3 angegeben. Fügen Sie weitere Felder hinzu, die Sie benötigen, um einen Datensatz eindeutig oder nahezu eindeutig zu identifizieren. Beachten Sie, dass Sie weiterhin eine Identitätsspalte für die Tabelle verwenden können. Verwenden Sie es einfach nicht als erste Spalte im Clustered-Index. Basierend auf dem SQL-Code in der Frage könnte ich mit gehen f_starttime, f_computername, f_application, f_ID.

    Selbst wenn Sie sich für die in der anderen Antwort vorgeschlagene Staging-Tabelle entscheiden, sind diese Indexänderungen möglicherweise immer noch eine gute Idee.

  2. Tabellenpartitionierung. Durch die Tabellenpartitionierung behält der Datenbankserver nur die letzten Datensätze im Speicher, sodass ältere Daten aus derselben Tabelle auf der Festplatte verbleiben können. Mit SQL Server 2016 können Sie die Verlaufsdaten sogar über die Stretch-Datenbank in den Cloud-Speicher von Azure übertragen.

Der andere Vorschlag, ausgefüllte Aufzeichnungen von offenen Aufzeichnungen zu trennen, ist ebenfalls gut. Trotzdem kann die Indizierung und Tabellenpartitionierung hilfreich sein, wenn die Größe der Tabelle für abgeschlossene Datensätze groß wird. Sie können erst dann versuchen, alte Daten in eine separate (verknüpfte) Datenbank zu verschieben, wenn alle diese Optionen fehlgeschlagen sind.

Wirklich, Sql Server ist jedoch leicht in der Lage, sechs Millionen Datensätze zu verarbeiten, ohne auf diese Art von Tricks zurückzugreifen (eine Änderung des Index kann sich jedoch immer noch lohnen). Sind Sie sicher, dass der Server dafür korrekt bereitgestellt ist? Sie können genauso gut einfach RAM zum Server hinzufügen.

Schließlich ist es auch üblich, eine Berichtsdatenbank von der Live-Verarbeitungsdatenbank zu trennen, und es ist überhaupt keine schlechte Sache. Wir nennen dies manchmal ein "Data Warehouse", obwohl dies häufig auch Schemaänderungen und einen SSIS-Prozess zum Verschieben der Daten umfasst. Dies ist eine gute Funktion, da verhindert wird, dass ein versehentlicher Fehler in einer Datenanalyse-Abfrage Leistungsprobleme in der Produktion verursacht. Sie können dies am besten über Datenbankspiegelung / Protokollversand an einen schreibgeschützten Slave oder in jüngerer Zeit über eine AlwaysOn-Verfügbarkeitsgruppe erreichen.

Joel Coehoorn
quelle
Vielen Dank für die sehr ausführliche Antwort. @Hogan unten erwähnte auch die Indizierung, aber dies ist kein Konzept, mit dem ich vertraut bin. "Indexierbar" ist für jedes Feld in der Datenbank auf "Ja" gesetzt, aber ich weiß nicht, ob dies das gleiche ist, über das Sie sprechen. Ich habe eine ID-Spalte, die sonst nicht verwendet wird. Aktuelles Tabellenlayout: i.imgur.com/c4sNaUy.jpg . Was ist die optimale Methode (haben Sie einen Link), um diesen Clustered-Index zu aktivieren?
Indizierbar auf Ja ist definitiv nicht dasselbe. Ich würde sicherstellen, dass der Clustered-Index (Primärschlüssel) in der Tabelle die Felder f_starttime, f_computername, f_application, f_endtime und f_ID verwendet, ähnlich wie in der Antwort vorgeschlagen, und sicherstellen, dass die Spalten in dieser Reihenfolge in den Index eingefügt werden .
Joel Coehoorn
Vielen Dank für die bisherige Hilfe. Ich habe so viel wie möglich über Ihre Vorschläge gelesen. Ich habe die Spalten im Index, wie Sie angegeben haben. Meine letzten Fragen lauten daher: Sollte die Option "Ist eindeutig" auf "Ja" und die Option "Als Cluster erstellen" auf "Ja" gesetzt werden? Screenshot der Einstellungen: imgur.com/uQ3fF31.jpg
Beems
Sie möchten, dass "Is Clustered" hier "yes" ist. "Ist eindeutig" sollte nur dann "Ja" sein, wenn Sie garantieren können, dass diese Spalten immer auf einen bestimmten Datensatz verweisen. Wenn es sogar aus der Ferne möglich ist, dass zwei Instanzen derselben App auf demselben Computer dieselbe Startzeit melden, müssen Sie entweder die Datensätze voneinander unterscheiden oder "Is Unique" auf "No" setzen. . In der Regel möchten Sie, dass Ihre Clustered-Indizes eindeutig sind. Sie möchten dies auf "Ja" setzen, wenn Sie können. Es ist jedoch nicht erforderlich, und Kollisionen sollten in diesem Fall selten genug sein, um Probleme zu vermeiden, wenn Sie dies nicht garantieren können
Joel Coehoorn
2
Wenn dies helfen würde, würde ich gerne einige vor und nach Zahlen hören.
Joel Coehoorn
6

Zwei Dinge

  1. Sie sagen nicht wirklich, dass Sie einen Index auf dem Tisch haben - ich gehe davon aus, dass dies nur Ihr Problem lösen würde. Ein Index für f_application, f_computername, f_endtime, f_starttime sollte Ihre Aktualisierungszeit mit nur 6 Millionen Datensätzen winzig machen.

  2. Wenn Sie es aufteilen möchten, machen Sie es nicht so, wie Sie es beschreiben. Erstellen Sie eine Tabelle für offene, aber nicht geschlossene Datensätze, die Sie vor Ihrer aktuellen Tabelle verwenden. Wenn dann etwas "aktualisiert" wird, löschen Sie es aus der Staging-Tabelle und fügen Sie es in Ihre große Tabelle ein. Die Verwendung einer Staging-Tabelle auf diese Weise wird als führende / bewährte Methode angesehen. Es ist immer ein Albtraum, wenn eine Tabelle willkürlich in zwei Teile geteilt wird

Hogan
quelle
Vielen Dank, dass Sie sich die Zeit genommen haben, um zu antworten. Verzeihen Sie mir, wie zuvor, als ich sagte "Ich bin kein SQL-Guru", hätte ich klarstellen und sagen sollen: "Ich benutze gelegentlich SQL und ehrlich gesagt bin ich mir nicht sicher, wie ich so weit gekommen bin wie ich". Ich bin mit dem Konzept eines Index nicht vertraut. Bei der Suche im Internet vor dem Posten sind die zurückgegebenen Informationen recht umfangreich. Haben Sie einen Artikel, auf den Sie mich verweisen können, der die Indizierung beschreibt und wie Sie ihn einer vorhandenen Tabelle / Datenbank hinzufügen können? BEARBEITEN: Ich ging jedes Feld in der Tabelle durch und stellte fest, dass "Indexierbar" auf "Ja" gesetzt war. Haben Sie danach gesucht?
2

Ich denke, ein gefilterter Index würde ganz gut zur Rechnung passen. Basierend auf Ihren Kommentaren zu den anderen Antworten sieht es so aus, als wäre eine Beispielanweisung zum Erstellen eines Index für Sie nützlich. Die Anweisung create index würde ungefähr so ​​aussehen:

CREATE NONCLUSTERED INDEX <index name> ON 
   <schema>.tb_applicationusage(f_application, f_computername, f_starttime) INCLUDE (f_ID)
   WHERE f_endtime IS NULL;

Es wäre schön, wenn Sie UNIQUEdiesen Index einschränken könnten , aber ich bezweifle, dass Sie damit durchkommen können, da immer etwas passieren wird, das ein normales Abmelden / Herunterfahren der Anwendung verhindert. Natürlich sollten Sie (wahrscheinlich ) durch <index name>einen geeigneten Namen und <schema>durch das Schema der Tabelle ersetzen dbo. Ich habe die INCLUDEAnweisung nur hinzugefügt , weil sie in Ihrer Beispielabfrage enthalten war. Wenn Sie feststellen, dass Sie das nicht brauchen, können Sie es fallen lassen.

Ihre Update-Anweisung, wie sie geschrieben wurde, ist kein Update, sondern eine Auswahl. Hier ist ein Beispiel der vollständigen Abfrage, die als Update geschrieben wurde:

WITH LastLogin_CTE AS 
(
  SELECT TOP 1
    * -- Generally avoid * notation but I used it here because we're just driving an update
  FROM tb_applicationusage 
  WHERE 
    f_application = @ApplicationName -- Parameterize the name
    AND 
    f_computername = @ComputerName -- Parameterize the name 
    AND 
    f_endtime IS NULL 
  ORDER BY
    f_starttime DESC
)
UPDATE LastLogin_CTE
SET
  f_endtime = SYSUTCDATETIME();

Dieses Update hinterlässt natürlich verwaiste Zeilen, die nie abgemeldet wurden. Ich vermute, das ist es, was der monatliche Bericht erkennen soll.


Abschiedsgedanken:

  • Sie sollten wahrscheinlich die Präfixe ,, und andere vermeiden tb_, f_wenn möglich. Sie fügen Ihren Objektnamen nur Rauschen hinzu, das das Lesen erschwert und nicht als Best Practice der Branche zu gelten scheint.
  • Bei Leistungsproblemen sollten Sie Ihrer Frage wirklich einen Abfrageplan hinzufügen . Dies hilft uns, fehlende Indizes zu erkennen und nützlichere Informationen bereitzustellen.
Erik
quelle