Warum wird empfohlen, BLOBs in separaten SQL Server-Tabellen zu speichern?

28

Diese vielbeachtete SO-Antwort empfiehlt, Bilder in separaten Tabellen abzulegen , auch wenn nur eine 1: 1-Beziehung zu einer anderen Tabelle besteht:

Wenn Sie Ihre Bilder in eine SQL Server-Tabelle einfügen möchten, empfehle ich dringend, eine separate Tabelle zum Speichern dieser Bilder zu verwenden. Speichern Sie das Mitarbeiterfoto nicht in der Mitarbeitertabelle. Bewahren Sie sie in einer separaten Tabelle auf. Auf diese Weise kann die Mitarbeitertabelle schlank und effizient bleiben, vorausgesetzt, Sie müssen nicht immer auch das Mitarbeiterfoto als Teil Ihrer Abfragen auswählen.

Warum? Ich hatte den Eindruck, dass SQL Server nur einen Zeiger auf eine bestimmte BLOB-Datenstruktur in der Tabelle speichert. Warum sollten Sie also manuell eine weitere Indirektionsebene erstellen? Verbessert es die Leistung wirklich erheblich? Wenn ja warum

Heinzi
quelle

Antworten:

15

Ich bin zwar nicht der Meinung, dass BLOBs nur in einer anderen Tabelle enthalten sein sollten - sie sollten überhaupt nicht in der Datenbank enthalten sein . Speichern Sie einen Zeiger darauf, wo sich die Datei auf der Festplatte befindet, und rufen Sie ihn dann einfach aus der Datenbank ab ...

Das Hauptproblem, das sie (für mich) verursachen, ist die Indizierung. Wenn Sie XML mit Abfrageplänen verwenden, lassen Sie uns eine Tabelle erstellen, da jeder sie hat:

SELECT TOP 1000
ID = IDENTITY(INT,1,1),
deq.query_plan
INTO dbo.index_test
FROM sys.dm_exec_cached_plans AS dec
CROSS APPLY sys.dm_exec_query_plan(dec.plan_handle) AS deq

ALTER TABLE dbo.index_test ADD CONSTRAINT pk_id PRIMARY KEY CLUSTERED (ID)

Es ist nur 1000 Zeilen, aber überprüfen Sie die Größe ...

sp_BlitzIndex @DatabaseName = 'StackOverflow', @SchemaName = 'dbo', @TableName = 'index_test'

Es ist über 40 MB für nur 1000 Zeilen. Angenommen, Sie fügen alle 1000 Zeilen 40 MB hinzu, dann kann das ziemlich schnell ziemlich hässlich werden. Was passiert, wenn Sie eine Million Zeilen erreichen? Das sind dort nur etwa 1 TB Daten.

NÜSSE

Alle Abfragen, die Ihren Clustered-Index verwenden müssen, müssen jetzt alle diese BLOB-Daten in die Speicheraufklärung einlesen : wenn auf die BLOB-Datenspalte verwiesen wird.

Gibt es bessere Möglichkeiten, den SQL Server-Speicher zu nutzen, als BLOBs zu speichern? Weil ich es sicher kann.

Erweitern auf nicht gruppierte Indizes:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

CREATE INDEX ix_returnoftheblob ON dbo.index_test (ID) INCLUDE (query_plan)

Sie können Ihre nicht gruppierten Indizes so entwerfen, dass die BLOB-Spalte weitgehend vermieden wird, sodass bei regelmäßigen Abfragen der gruppierte Index vermieden wird. Sobald Sie diese BLOB-Spalte benötigen, benötigen Sie den gruppierten Index.

Wenn Sie es INCLUDEDeinem nicht gruppierten Index als Spalte hinzufügen, um ein Schlüsselsuchszenario zu vermeiden, erhalten Sie gigantische nicht gruppierte Indizes:Bildbeschreibung hier eingeben

Weitere Probleme, die sie verursachen:

  • Wenn jemand eine SELECT *Abfrage ausführt, erhält er alle diese BLOB-Daten.
  • Sie belegen Speicherplatz in Sicherungen und Wiederherstellungen und verlangsamen diese
  • Sie werden langsamer DBCC CHECKDB, weil ich weiß, dass Sie nach Korruption suchen, oder?
  • Und wenn Sie einen Index pflegen, verlangsamen sie dies ebenfalls.

Hoffe das hilft!

Erik Darling
quelle
7
Weil Benutzer normalerweise SELECT * eingeben.
Brent Ozar
Ich denke, die Nachteile, die Sie erwähnen, sind Teil dessen, warum er empfohlen hat, die Bilder in einer separaten Tabelle abzulegen. Wenn ich verschiedene Berichte über Benutzer ausführe, benötige ich deren Bilddatei nicht. Wenn ich die Profilseite eines einzelnen Benutzers lade, trete ich dann der Blob-Tabelle bei, richtig? Fehlt mir hier etwas (dh
treffen
11

Wie groß sind diese Bilder und wie viele erwarten Sie? Obwohl ich @sp_BlitzErik größtenteils zustimme , denke ich, dass es einige Szenarien gibt, in denen dies in Ordnung ist, und daher wäre es hilfreich, ein klareres Bild davon zu haben, was hier tatsächlich angefordert wird.

Einige Optionen, um zu berücksichtigen, dass die meisten negativen Aspekte, auf die Erik hingewiesen hat, gemildert werden, sind:

Beide Optionen dienen als Mittelweg zwischen dem vollständigen Speichern von BLOBs in SQL Server oder dem vollständigen Speichern außerhalb von BLOBs (mit Ausnahme einer Zeichenfolge, die den Pfad beibehält). Sie ermöglichen es BLOBs, Teil des Datenmodells zu sein und an Transaktionen teilzunehmen, ohne Platz im Pufferpool (dh Speicher) zu verschwenden. Die BLOB-Daten sind weiterhin in den Sicherungen enthalten, wodurch sie mehr Speicherplatz beanspruchen und länger für die Sicherung und die Sicherung benötigenetwas wiederherstellen. Es fällt mir jedoch schwer, dies als echtes Negativ zu sehen, da es, wenn es Teil der App ist, irgendwie gesichert werden muss und nur eine Zeichenfolgenspalte mit dem Pfad vollständig getrennt ist und BLOBs-Dateien abrufen können gelöscht, ohne dass dies in der Datenbank angegeben ist (dh ungültige Zeiger / fehlende Dateien). Es ermöglicht auch, dass Dateien in der Datenbank "gelöscht" werden, aber immer noch im Dateisystem vorhanden sind, das eventuell bereinigt werden muss (z. B. Kopfschmerzen). Wenn die Dateien jedoch RIESIG sind, ist es möglicherweise am besten, sie mit Ausnahme der Pfadspalte vollständig außerhalb von SQL Server zu belassen.

Dies hilft bei der Frage nach innen oder außen, berührt jedoch nicht die Frage nach einer einzelnen Tabelle gegenüber der Frage nach mehreren Tabellen. Ich kann sagen, dass es über diese spezifische Frage hinaus durchaus gültige Fälle gibt, in denen Tabellen auf der Grundlage von Verwendungsmustern in Spaltengruppen aufgeteilt werden. Bei 50 oder mehr Spalten wird häufig auf einige zugegriffen, bei anderen nicht. Einige Spalten werden häufig beschrieben, während andere meistens gelesen werden. Das Aufteilen von Spalten, auf die häufig zugegriffen wird, und Spalten, auf die selten zugegriffen wird, in mehrere Tabellen mit einer 1: 1-Beziehung ist häufig von Vorteil, da der Speicherplatz im Pufferpool für Daten, die Sie wahrscheinlich nicht verwenden, verschwendet wird (ähnlich wie beim regulären Speichern großer Bilder)VARBINARY(MAX)Spalten ist ein Problem)? Sie erhöhen auch die Leistung der Spalten, auf die häufig zugegriffen wird, indem Sie die Zeilengröße verringern und somit mehr Zeilen auf eine Datenseite passen, wodurch die Lesevorgänge (sowohl physisch als auch logisch) effizienter werden. Natürlich führen Sie auch eine gewisse Ineffizienz ein, indem Sie die PK duplizieren müssen, und jetzt müssen Sie manchmal die beiden Tabellen verknüpfen, was auch einige Abfragen (wenn auch nur geringfügig) kompliziert.

Es gibt also verschiedene Ansätze, und was am besten ist, hängt von Ihrer Umgebung und dem ab, was Sie erreichen möchten.


Ich hatte den Eindruck, dass SQL Server nur einen Zeiger auf eine bestimmte BLOB-Datenstruktur in der Tabelle speichert

Nicht so einfach. Hier finden Sie einige gute Informationen: Wie groß ist der LOB-Zeiger für (MAX) -Typen wie Varchar, Varbinary usw.? , aber die Grundlagen sind:

  • TEXT, NTEXTUnd IMAGEDatentypen (Standard): 16 - Byte - Pointer
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(Standardeinstellung):
    • Wenn die Daten in die Zeile passen, werden sie dort platziert
    • Wenn die Daten weniger als ca. 40.000 Bytes (der verlinkte Blog-Beitrag zeigt 40.000 als Obergrenze, aber meine Tests ergaben einen etwas höheren Wert) UND wenn in der Zeile Platz für diese Struktur ist, gibt es zwischen 1 und 5 direkte Links zu LOB-Seiten, beginnend bei 24 Bytes für die erste Verbindung zu den ersten 8000 Bytes und 12 Bytes mehr für jede weitere Verbindung für jede weitere Gruppe von 8000 Bytes, bis zu 72 Bytes max.
    • Wenn die Daten über ca. 40.000 Bytes ODER es ist nicht genug Platz für die Speicherung der entsprechenden Anzahl von Direktverbindungen (z. B. nur noch 40 Bytes in der Zeile und ein Wert von 20.000 Bytes benötigt 3 Verbindungen, dh 24 Bytes für die ersten plus 12 für die zwei zusätzlichen Verbindungen für 48 Bytes insgesamt benötigter Platz in der Zeile), dann gibt es nur einen 24-Byte-Zeiger auf eine Textbaumseite, die die Links zu den LOB-Seiten enthält.
Solomon Rutzky
quelle
7

Wenn die Daten aus irgendeinem Grund in SQL Server gespeichert werden müssen, kann ich mir einige Vorteile vorstellen, wenn ich sie in einer separaten Tabelle speichere. Einige überzeugen mehr als andere.

  1. Wenn Sie die Daten in einer separaten Tabelle ablegen, können Sie sie in einer separaten Datenbank speichern. Dies kann Vorteile für die geplante Wartung haben. Beispielsweise können Sie DBCC CHECKDBnur auf der Datenbank ausgeführt werden, die die BLOB-Daten enthält.

  2. Wenn Sie nicht immer mehr als 8000 Bytes in das BLOB einfügen, kann es sein, dass es für einige Zeilen in Reihe gespeichert wird . Möglicherweise möchten Sie dies nicht, da dadurch Abfragen verlangsamt werden, die über den Clustered-Index auf Daten zugreifen, selbst wenn die Spalte von der Abfrage nicht benötigt wird. Durch das Einfügen der Daten in eine separate Tabelle wird dieses Risiko beseitigt.

  3. Beim Speichern außerhalb der Zeile verwendet SQL Server einen Zeiger von bis zu 24 Byte, um auf die neue Seite zu verweisen. Dies nimmt Platz in Anspruch und begrenzt die Gesamtzahl der BLOB-Spalten, die Sie einer einzelnen Tabelle hinzufügen können. Siehe Srutzkys Antwort für weitere Details.

  4. Ein Clustered Columnstore-Index kann nicht für eine Tabelle definiert werden, die eine BLOB-Spalte enthält. Diese Einschränkung wurde entfernt und wird in SQL Server 2017 entfernt.

  5. Wenn Sie irgendwann entscheiden, dass die Daten aus SQL Server verschoben werden sollen, ist es möglicherweise einfacher, diese Änderung vorzunehmen, wenn sich die Daten bereits in einer separaten Tabelle befinden.

Joe Obbish
quelle
1
Einige gute Punkte hier (+1). Um jedoch klar zu sein, dass # 3 (re: 24-Byte-Zeiger für Off-Row-Daten) nicht immer korrekt ist. Ich erkläre (kurz) am Ende meiner Antwort, wie der Datentyp, die Größe des Werts und der freie Speicherplatz in der Zeile die Größe des Zeigers bestimmen.
Solomon Rutzky