it-swarm.com.de

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

Diese hoch bewertete SO Antwort empfiehlt, Bilder in separate Tabellen zu stellen, 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, würde ich dringend empfehlen, 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 gemein und sehr 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 speichert nur einen Zeiger auf eine dedizierte BLOB-Datenstruktur in der Tabelle. Warum also manuell eine weitere Indirektionsebene erstellen? Verbessert es die Leistung wirklich erheblich? Wenn ja, warum?

31
Heinzi

Ich bin zwar nicht der Meinung, dass BLOBs nur in einer anderen Tabelle enthalten sein sollten - sie sollten überhaupt nicht in der Datenbank . 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. Verwenden von XML mit Abfrageplänen, da jeder sie hat, erstellen wir eine Tabelle:

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 sind nur 1000 Zeilen, aber überprüft 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, kann dies ziemlich schnell ziemlich hässlich werden. Was passiert, wenn Sie 1 Million Zeilen treffen? Das ist nur ungefähr 1 TB von Daten, dort.

(NUTS

Bei allen Abfragen, die Ihren Clustered-Index verwenden müssen, müssen jetzt alle BLOB-Daten in den Speicher eingelesen werden . Erläuterung: , wenn auf die BLOB-Datenspalte verwiesen wird.

Können Sie sich bessere Möglichkeiten zur Verwendung des SQL Server-Speichers vorstellen als das Speichern von BLOBs? 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 gestalten, dass die BLOB-Spalte weitgehend vermieden wird, sodass regelmäßige Abfragen den gruppierten Index vermeiden können. Sobald Sie diese BLOB-Spalte benötigen, benötigen Sie den gruppierten Index.

Wenn Sie es als INCLUDED -Spalte zu einem nicht gruppierten Index hinzufügen, um ein Szenario für die Schlüsselsuche zu vermeiden, erhalten Sie gigantische nicht gruppierte Indizes: enter image description here

Weitere Probleme, die sie verursachen:

  • Wenn jemand ein SELECT * Abfrage, sie erhalten alle diese BLOB-Daten.
  • Sie belegen Speicherplatz in Backups und Wiederherstellungen und verlangsamen sie
  • Sie verlangsamen DBCC CHECKDB, weil ich weiß, dass Sie nach Korruption suchen, richtig?
  • Und wenn Sie eine Indexpflege durchführen, verlangsamen sie dies ebenfalls.

Hoffe das hilft!

16
Erik Darling

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

Einige Optionen, die berücksichtigt werden müssen, um die meisten der von Erik hervorgehobenen negativen Aspekte zu mildern, sind:

Beide Optionen sind als Mittelweg zwischen dem Speichern von BLOBs entweder vollständig in SQL Server oder vollständig außerhalb von SQL Server gedacht (mit Ausnahme einer Zeichenfolgenspalte, um den Pfad beizubehalten). Sie ermöglichen es BLOBs, Teil des Datenmodells zu sein und an Transaktionen teilzunehmen, ohne Speicherplatz im Pufferpool (d. H. Speicher) zu verschwenden. Die BLOB-Daten sind weiterhin in Sicherungen enthalten, wodurch sie mehr Speicherplatz beanspruchen und länger für die Sicherung und für die Wiederherstellung benötigen. Es fällt mir jedoch schwer, dies als echtes Negativ zu betrachten, da es, wenn es Teil der App ist, irgendwie gesichert werden muss und nur eine Zeichenfolgenspalte, die den Pfad enthält, vollständig getrennt ist und das Abrufen von BLOB-Dateien ermöglicht ohne Hinweis darauf in der Datenbank gelöscht (dh ungültige Zeiger/fehlende Dateien). Es ermöglicht auch, dass Dateien innerhalb der Datenbank "gelöscht" werden, aber immer noch im Dateisystem vorhanden sind, das eventuell bereinigt werden muss (d. H. 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.

Das hilft bei der Frage "innen oder außen", berührt aber nicht die Frage "Einzelne Tabelle" oder "Mehrfache Tabelle". Ich kann sagen, dass es über diese spezielle Frage hinaus durchaus gültige Fälle für die Aufteilung von Tabellen in Gruppen von Spalten gibt, die auf Verwendungsmustern basieren. Wenn man 50 oder mehr Spalten hat, gibt es oft einige, auf die häufig zugegriffen wird, und einige, auf die nicht zugegriffen wird. Einige Spalten werden häufig geschrieben, während andere meistens gelesen werden. Die Trennung von Spalten mit häufigem Zugriff und Spalten mit seltenem Zugriff in mehrere Tabellen mit einer 1: 1-Beziehung ist häufig von Vorteil, da der Speicherplatz im Pufferpool für Daten verschwendet wird, die Sie wahrscheinlich nicht verwenden (ähnlich wie beim Speichern großer Bilder in regulären VARBINARY(MAX) Spalten ist ein Problem)? Sie erhöhen auch die Leistung der Spalten mit häufigem Zugriff, indem Sie die Zeilengröße verringern und somit mehr Zeilen auf eine Datenseite anpassen, wodurch das Lesen (sowohl physisch als auch logisch) effizienter wird. Natürlich führen Sie auch zu einer gewissen Ineffizienz, indem Sie die PK duplizieren müssen, und jetzt müssen Sie manchmal die beiden Tabellen verbinden, 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 dedizierte 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:

  • Datentypen TEXT, NTEXT und IMAGE (standardmäßig): 16-Byte-Zeiger
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX) (standardmäßig):
    • Wenn die Daten in die Zeile passen, werden sie dort platziert
    • Wenn die Daten kleiner als ca. 40.000 Bytes (der verlinkte Blog-Beitrag zeigt 40.000 als Obergrenze, aber meine Tests zeigten einen etwas höheren Wert) [~ # ~] und [~ # ~] Wenn in der Zeile Platz für diese Struktur vorhanden ist, gibt es zwischen 1 und 5 direkte Links zu LOB-Seiten, beginnend bei 24 Byte für den ersten Link zu den ersten 8000 Bytes und um 12 Bytes pro zusätzlichem Link für jeden weiteren Satz von 8000 Bytes maximal max. 72 Bytes.
    • Wenn die Daten über ca. 40.000 Bytes [~ # ~] oder [~ # ~] Es ist nicht genügend Platz vorhanden, um die entsprechende Anzahl direkter Links zu speichern (z. B. nur noch 40 Bytes) In der Zeile und für einen Wert von 20.000 Byte werden 3 Links benötigt, dh 24 Bytes für den ersten plus 12 für die zwei zusätzlichen Links für insgesamt 48 Bytes, die in der Zeile benötigt werden. Dann gibt es nur einen 24-Byte-Zeiger auf eine Textbaumseite welches die Links zu den LOB-Seiten enthält).
12
Solomon Rutzky

Wenn die Daten aus irgendeinem Grund in SQL Server gespeichert werden müssen, kann ich mir einige Vorteile vorstellen, wenn sie in einer separaten Tabelle gespeichert werden. 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 CHECKDB nur in der Datenbank, die die BLOB-Daten enthält.

  2. Wenn Sie nicht immer mehr als 8000 Bytes in das BLOB einfügen, kann es für einige Zeilen in Zeile gespeichert sein. Möglicherweise möchten Sie dies nicht, da dadurch Abfragen verlangsamt werden, die über den Clustered-Index auf Daten zugreifen, auch 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. Bei der Speicherung außerhalb der Zeile verwendet SQL Server einen Zeiger mit bis zu 24 Byte, um auf die neue Seite zu verweisen. Das 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 schließlich entscheiden, dass die Daten außerhalb von SQL Server verschoben werden sollen, ist es möglicherweise einfacher, diese Änderung vorzunehmen, wenn sich die Daten bereits in einer separaten Tabelle befinden.

8
Joe Obbish