it-swarm.com.de

Riesige Daten und Leistung in SQL Server

Ich habe eine Anwendung mit einem SQL Server-Backend geschrieben, das extrem viele Datensätze sammelt und speichert. Ich habe berechnet, dass in der Spitze die durchschnittliche Anzahl von Datensätzen irgendwo in der Allee von 3-4 Milliarden pro Tag liegt (20 Betriebsstunden).

Meine ursprüngliche Lösung (bevor ich die eigentliche Berechnung der Daten durchgeführt hatte) bestand darin, dass meine Anwendung Datensätze in dieselbe Tabelle einfügte, die von meinen Kunden abgefragt wurde. Das stürzte offensichtlich ziemlich schnell ab und brannte, weil es unmöglich ist, eine Tabelle abzufragen, in die so viele Datensätze eingefügt wurden.

Meine zweite Lösung bestand darin, zwei Datenbanken zu verwenden, eine für von der Anwendung empfangene Daten und eine für clientseitige Daten.

Meine Anwendung würde Daten empfangen, sie in Stapel von ~ 100.000 Datensätzen aufteilen und in die Staging-Tabelle einfügen. Nach ca. 100.000 Datensätzen erstellt die Anwendung im laufenden Betrieb eine weitere Staging-Tabelle mit demselben Schema wie zuvor und beginnt mit dem Einfügen in diese Tabelle. Es würde einen Datensatz in einer Jobtabelle mit dem Namen der Tabelle erstellen, die 100.000 Datensätze enthält, und eine gespeicherte Prozedur auf der SQL Server-Seite würde die Daten von den Staging-Tabellen in die Client-fähige Produktionstabelle verschieben und dann die löschen Tabelle temporäre Tabelle von meiner Anwendung erstellt.

Beide Datenbanken haben denselben Satz von 5 Tabellen mit demselben Schema, mit Ausnahme der Staging-Datenbank mit der Jobtabelle. Die Staging-Datenbank enthält keine Integritätsbeschränkungen, Schlüssel, Indizes usw. in der Tabelle, in der sich der Großteil der Datensätze befindet. Der unten gezeigte Tabellenname lautet SignalValues_staging. Das Ziel war, dass meine Anwendung die Daten so schnell wie möglich in SQL Server überträgt. Der Workflow zum Erstellen von Tabellen im laufenden Betrieb, damit diese problemlos migriert werden können, funktioniert recht gut.

Im Folgenden sind die 5 relevanten Tabellen aus meiner Staging-Datenbank sowie meine Jobtabelle aufgeführt:

Staging tables Die gespeicherte Prozedur, die ich geschrieben habe, behandelt das Verschieben der Daten aus allen Staging-Tabellen und das Einfügen in die Produktion. Unten ist der Teil meiner gespeicherten Prozedur, der aus den Staging-Tabellen in die Produktion eingefügt wird:

-- Signalvalues jobs table.
SELECT *
      ,ROW_NUMBER() OVER (ORDER BY JobId) AS 'RowIndex'
INTO #JobsToProcess
FROM 
(
    SELECT JobId 
           ,ProcessingComplete  
           ,SignalValueStagingTableName AS 'TableName'
           ,(DATEDIFF(SECOND, (SELECT last_user_update
                              FROM sys.dm_db_index_usage_stats
                              WHERE database_id = DB_ID(DB_NAME())
                                AND OBJECT_ID = OBJECT_ID(SignalValueStagingTableName))
                     ,GETUTCDATE())) SecondsSinceLastUpdate
    FROM SignalValueJobs
) cte
WHERE cte.ProcessingComplete = 1
   OR cte.SecondsSinceLastUpdate >= 120

DECLARE @i INT = (SELECT COUNT(*) FROM #JobsToProcess)

DECLARE @jobParam UNIQUEIDENTIFIER
DECLARE @currentTable NVARCHAR(128) 
DECLARE @processingParam BIT
DECLARE @sqlStatement NVARCHAR(2048)
DECLARE @paramDefinitions NVARCHAR(500) = N'@currentJob UNIQUEIDENTIFIER, @processingComplete BIT'
DECLARE @qualifiedTableName NVARCHAR(128)

WHILE @i > 0
BEGIN

    SELECT @jobParam = JobId, @currentTable = TableName, @processingParam = ProcessingComplete
    FROM #JobsToProcess 
    WHERE RowIndex = @i 

    SET @qualifiedTableName = '[Database_Staging].[dbo].['[email protected]+']'

    SET @sqlStatement = N'

        --Signal values staging table.
        SELECT svs.* INTO #sValues
        FROM '+ @qualifiedTableName +' svs
        INNER JOIN SignalMetaData smd
            ON smd.SignalId = svs.SignalId  


        INSERT INTO SignalValues SELECT * FROM #sValues

        SELECT DISTINCT SignalId INTO #uniqueIdentifiers FROM #sValues

        DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

        DROP TABLE #sValues
        DROP TABLE #uniqueIdentifiers

        IF NOT EXISTS (SELECT TOP 1 1 FROM '+ @qualifiedTableName +') --table is empty
        BEGIN
            -- processing is completed so drop the table and remvoe the entry
            IF @processingComplete = 1 
            BEGIN 
                DELETE FROM SignalValueJobs WHERE JobId = @currentJob

                IF '''[email protected]+''' <> ''SignalValues_staging''
                BEGIN
                    DROP TABLE '+ @qualifiedTableName +'
                END
            END
        END 
    '

    EXEC sp_executesql @sqlStatement, @paramDefinitions, @currentJob = @jobParam, @processingComplete = @processingParam;

    SET @i = @i - 1
END

DROP TABLE #JobsToProcess

Ich verwende sp_executesql, Da die Tabellennamen für die Staging-Tabellen als Text aus den Datensätzen in der Jobtabelle stammen.

Diese gespeicherte Prozedur wird alle 2 Sekunden mit dem Trick ausgeführt, den ich aus this dba.stackexchange.com post gelernt habe.

Das Problem, das ich für mein ganzes Leben nicht lösen kann, ist die Geschwindigkeit, mit der die Einsätze in die Produktion ausgeführt werden. Meine Anwendung erstellt temporäre Staging-Tabellen und füllt sie unglaublich schnell mit Datensätzen. Der Einsatz in der Produktion kann nicht mit der Anzahl der Tabellen mithalten, und schließlich gibt es einen Überschuss an Tabellen zu Tausenden. Die einzige einzige Möglichkeit, mit den eingehenden Daten Schritt zu halten, besteht darin, alle Schlüssel, Indizes, Einschränkungen usw. in der Produktion zu entfernen Tabelle SignalValues. Das Problem, mit dem ich dann konfrontiert bin, ist, dass die Tabelle so viele Datensätze enthält, dass keine Abfrage mehr möglich ist.

Ich habe versucht, die Tabelle mit [Timestamp] Als Partitionierungsspalte ohne Erfolg zu partitionieren. Jede Form der Indizierung verlangsamt die Einfügungen so sehr, dass sie nicht mithalten können. Außerdem müsste ich Jahre im Voraus Tausende von Partitionen erstellen (eine jede Minute? Stunde?). Ich konnte nicht herausfinden, wie ich sie im laufenden Betrieb erstellen kann

Ich habe versucht, eine Partitionierung zu erstellen, indem ich der Tabelle TimestampMinute eine berechnete Spalte hinzugefügt habe, deren Wert auf INSERTDATEPART(MINUTE, GETUTCDATE()) war. Immer noch zu langsam.

Ich habe versucht, daraus eine speicheroptimierte Tabelle gemäß dieser Microsoft-Artikel zu machen. Vielleicht verstehe ich nicht, wie es geht, aber der TÜV hat die Einsätze irgendwie langsamer gemacht.

Ich habe den Ausführungsplan der gespeicherten Prozedur überprüft und festgestellt, dass (glaube ich?) Die intensivste Operation ist

SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
    ON smd.SignalId = svs.SignalId

Für mich macht das keinen Sinn: Ich habe der gespeicherten Prozedur eine Wanduhrprotokollierung hinzugefügt, die das Gegenteil bewiesen hat.

In Bezug auf die Zeitprotokollierung wird diese bestimmte Anweisung oben in ~ 300 ms in 100.000 Datensätzen ausgeführt.

Die Aussage

INSERT INTO SignalValues SELECT * FROM #sValues

wird in 2500-3000ms auf 100.000 Datensätzen ausgeführt. Löschen der betroffenen Datensätze aus der Tabelle per:

DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

dauert weitere 300ms.

Wie kann ich das schneller machen? Kann SQL Server Milliarden von Datensätzen pro Tag verarbeiten?

Wenn dies relevant ist, ist dies SQL Server 2014 Enterprise x64.

Hardwarekonfiguration:

Ich habe vergessen, Hardware in den ersten Durchgang dieser Frage aufzunehmen. Mein Fehler.

Ich werde dies mit folgenden Aussagen vorwegnehmen: Ich weiß , dass ich aufgrund meiner Hardwarekonfiguration etwas an Leistung verliere. Ich habe es oft versucht, aber aufgrund des Budgets, des C-Levels, der Ausrichtung der Planeten usw. kann ich leider nichts tun, um ein besseres Setup zu erhalten. Der Server läuft auf einer virtuellen Maschine und ich kann den Speicher nicht einmal vergrößern, weil wir einfach keine mehr haben.

Hier sind meine Systeminformationen:

System Info

Der Speicher wird über eine iSCSI-Schnittstelle an den Server VM Server) an eine NAS Box) angeschlossen (dies beeinträchtigt die Leistung). Die NAS = Box verfügt über 4 Laufwerke in einer RAID 10-Konfiguration. Es handelt sich um 4 TB WD WD4000FYYZ-Laufwerke mit 6 GB/s SATA-Schnittstelle. Auf dem Server ist nur ein Datenspeicher konfiguriert, sodass sich Tempdb und meine Datenbank im selben Datenspeicher befinden.

Max DOP ist Null. Sollte ich dies in einen konstanten Wert ändern oder nur SQL Server damit umgehen lassen? Ich habe mich über RCSI informiert: Bin ich zu Recht davon ausgegangen, dass der einzige Vorteil von RCSI in Zeilenaktualisierungen besteht? Es wird niemals Aktualisierungen für einen dieser bestimmten Datensätze geben, sie werden INSERT und SELECT bearbeitet. Wird RCSI mir noch zugute kommen?

Mein Tempdb ist 8mb. Basierend auf der Antwort von jyao unten habe ich die #sValues ​​in eine reguläre Tabelle geändert, um Tempdb insgesamt zu vermeiden. Die Leistung war jedoch ungefähr gleich. Ich werde versuchen, die Größe und das Wachstum von Tempdb zu erhöhen, aber da die Größe von #sValues ​​mehr oder weniger immer dieselbe Größe hat, erwarte ich keinen großen Gewinn.

Ich habe einen Ausführungsplan erstellt, den ich unten angehängt habe. Dieser Ausführungsplan ist eine Iteration einer Staging-Tabelle - 100.000 Datensätze. Die Ausführung der Abfrage war ziemlich schnell, ungefähr 2 Sekunden, aber denken Sie daran, dass dies ohne Indizes für die Tabelle SignalValues und die Tabelle SignalValues ist, das Ziel der Tabelle INSERT, enthält keine Datensätze.

Execution plan

20
Brandon

Ich habe berechnet, dass in der Spitze die durchschnittliche Anzahl von Datensätzen irgendwo in der Allee von 3-4 Milliarden pro Tag liegt (20 Betriebsstunden).

In Ihrem Screenshot haben Sie NUR 8 GB Arbeitsspeicher RAM und 6 GB für SQL Server. Dies ist viel zu niedrig für das, was Sie erreichen möchten.

Ich empfehle Ihnen, den Speicher auf einen höheren Wert (256 GB) zu aktualisieren und Ihre VM CPUs ebenfalls zu erhöhen).

Zu diesem Zeitpunkt müssen Sie für Ihre Arbeitslast in Hardware investieren.

Siehe auch Leistungshandbuch zum Laden von Daten - Es werden intelligente Methoden zum effizienten Laden der Daten beschrieben.

Mein Tempdb ist 8mb.

Basierend auf Ihrer Bearbeitung sollten Sie eine vernünftige Tempdb haben - vorzugsweise mehrere Tempdb-Datendateien gleicher Größe zusammen mit TF 1117 und 1118 aktivierten Instanzen.

Ich würde Ihnen empfehlen, einen professionellen Gesundheitscheck durchzuführen und von dort aus zu beginnen.

Sehr empfehlenswert

  1. Erhöhen Sie Ihre Serverspezifikation.

  2. Lassen Sie eine professionelle * Person eine Integritätsprüfung Ihrer Datenbankserverinstanz durchführen und befolgen Sie die Empfehlungen.

  3. Einmal. und B. Wenn Sie fertig sind, tauchen Sie ein in die Optimierung von Abfragen und andere Optimierungen wie das Betrachten von Wartestatistiken, Abfrageplänen usw.

Hinweis: Ich bin ein professioneller SQL Server-Experte at hackhands.com - ein Pluralsight-Unternehmen, empfehle Ihnen jedoch keinesfalls, mich für Hilfe zu engagieren. Ich schlage Ihnen lediglich vor, professionelle Hilfe nur aufgrund Ihrer Änderungen in Anspruch zu nehmen.

HTH.

7
Kin Shah

Allgemeine Hinweise für solche Probleme mit Big Data, wenn Sie vor einer Wand stehen und nichts funktioniert:

Ein Ei wird ungefähr 5 Minuten gekocht. 10 Eier werden gleichzeitig gekocht, wenn genügend Strom und Wasser vorhanden sind.

Oder mit anderen Worten:

Schauen Sie sich zunächst die Hardware an. Zweitens: Trennen Sie die Prozesslogik (Datenumgestaltung) und führen Sie sie parallel aus.

Es ist durchaus möglich, eine benutzerdefinierte vertikale Partitionierung dynamisch und automatisiert pro Tabellenanzahl und pro Tabellengröße zu erstellen. Wenn ich Quarter_1_2017, Quarter_2_2017, Quarter_3_2017, Quarter_4_2017, Quarter_1_2018 ... habe und nicht weiß, wo sich meine Datensätze befinden und wie viele Partitionen ich habe, führen Sie dieselben Abfragen für alle benutzerdefinierten Partitionen zur gleichen Zeit aus, trennen Sie Sitzungen und Assembly Das Ergebnis soll für meine Logik weitergeleitet werden.

1
Goran Mancevski