Pourquoi est-il recommandé de stocker des BLOB dans des tables SQL Server distinctes?


29

Cette réponse SO très appréciée recommande de placer les images dans des tableaux séparés, même s'il n'y a qu'une relation 1: 1 avec une autre table:

Si vous décidez de placer vos images dans une table SQL Server, je vous recommande fortement d'utiliser une table distincte pour stocker ces images - ne stockez pas la photo de l'employé dans la table des employés - conservez-les dans une table distincte. De cette façon, la table Employé peut rester légère et moyenne et très efficace, en supposant que vous n'avez pas toujours besoin de sélectionner la photo de l'employé également dans le cadre de vos requêtes.

Pourquoi? J'avais l'impression que SQL Server stocke uniquement un pointeur vers une structure de données BLOB dédiée dans la table, alors pourquoi s'embêter à créer manuellement une autre couche d'indirection? Cela améliore-t-il vraiment les performances de manière significative? Si oui, pourquoi?

Réponses:


15

Bien que je ne sois pas d'accord pour dire que les BLOB devraient simplement être dans une autre table - ils ne devraient pas du tout être dans la base de données . Stockez un pointeur vers l'emplacement du fichier sur le disque, puis récupérez-le dans la base de données ...

Le principal problème qu'ils provoquent (pour moi) concerne l'indexation. Utiliser XML avec des plans de requête, parce que tout le monde en a un, créons un tableau:

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)

Ce n'est que 1000 lignes, mais vérifier la taille ...

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

C'est plus de 40 Mo pour seulement 1000 lignes. En supposant que vous ajoutez 40 Mo toutes les 1000 lignes, cela peut devenir assez moche assez rapidement. Que se passe-t-il lorsque vous atteignez 1 million de lignes? C'est à peu près 1 To de données, là.

DES NOISETTES

Toutes les requêtes qui doivent utiliser votre index cluster doivent désormais lire toutes ces données BLOB dans la clarification de la mémoire : lorsque la colonne de données BLOB est référencée.

Pouvez-vous imaginer de meilleures façons d'utiliser la mémoire SQL Server que de stocker des BLOB? Parce que je le peux.

Extension à des index non clusterisés:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

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

Vous pouvez concevoir vos index non clusterisés pour éviter largement la colonne BLOB afin que les requêtes régulières puissent éviter l'index clusterisé, mais dès que vous avez besoin de cette colonne BLOB, vous avez besoin de l'index clusterisé.

Si vous l'ajoutez en tant que INCLUDEDcolonne à un index non cluster pour éviter un scénario de recherche de clé, vous vous retrouvez avec de gigantesques index non cluster:entrez la description de l'image ici

Plus de problèmes qu'ils causent:

  • Si quelqu'un exécute une SELECT *requête, il obtient toutes ces données BLOB.
  • Ils occupent de l'espace dans les sauvegardes et les restaurations, les ralentissant
  • Ils ralentissent DBCC CHECKDB, parce que je sais que vous vérifiez la corruption, non?
  • Et si vous effectuez une maintenance d'index, cela ralentit également.

J'espère que cela t'aides!


7
Parce que les utilisateurs tapent généralement SELECT *.
Brent Ozar

Je pense que les inconvénients que vous mentionnez expliquent pourquoi il a recommandé de mettre les photos dans un tableau séparé. Si j'exécute divers rapports sur les utilisateurs, je n'ai pas besoin de leur fichier image. Si je charge la page de profil d'un seul utilisateur, c'est à ce moment-là que je me joins à la table d'objets blob, non? Suis-je en train de manquer quelque chose ici (c'est-à-dire que vos inconvénients s'appliquent toujours même dans ce scénario que j'ai décrit?)
BVernon

11

Quelle est la taille de ces images et combien pensez-vous en avoir? Bien que je sois principalement d'accord avec @sp_BlitzErik , je pense qu'il y a certains scénarios où c'est correct de le faire, et donc cela aiderait à avoir une image plus claire de ce qui est réellement demandé ici.

Voici quelques options à considérer qui atténuent la plupart des aspects négatifs signalés par Erik:

Ces deux options sont conçues pour être un juste milieu entre le stockage de BLOBs entièrement dans SQL Server ou entièrement à l'extérieur (à l'exception d'un colun de chaîne pour conserver le chemin d'accès). Ils permettent aux BLOB de faire partie du modèle de données et de participer aux transactions sans gaspiller d'espace dans le pool de mémoire tampon (c'est-à-dire la mémoire). Les données BLOB sont toujours incluses dans les sauvegardes, ce qui leur fait prendre plus d'espace et prendre plus de temps pour la sauvegarde etrestaurer. Cependant, j'ai du mal à voir cela comme un vrai négatif étant donné que s'il fait partie de l'application, il doit être sauvegardé d'une manière ou d'une autre, et le fait de n'avoir qu'une colonne de chaîne contenant le chemin est complètement déconnecté et permet aux fichiers BLOB d'obtenir supprimé sans indication de cela dans la base de données (c'est-à-dire pointeurs invalides / fichiers manquants). Il permet également de «supprimer» des fichiers dans la base de données mais existe toujours sur le système de fichiers qui devra éventuellement être nettoyé (c.-à-d. Maux de tête). Mais, si les fichiers sont ÉNORMES, il est peut-être préférable de les laisser entièrement en dehors de SQL Server, à l'exception de la colonne du chemin.

Cela aide à la question «à l'intérieur ou à l'extérieur», mais ne touche pas la question à table unique vs question à tables multiples. Je peux dire qu'au-delà de cette question spécifique, il existe certainement des cas valables pour fractionner des tableaux en groupes de colonnes en fonction des modèles d'utilisation. Souvent, quand on a 50 colonnes ou plus, il y en a qui sont consultées fréquemment et d'autres qui ne le sont pas. Certaines colonnes sont écrites fréquemment tandis que d'autres sont pour la plupart lues. Séparer les colonnes à accès fréquent et à accès fréquent en plusieurs tables ayant une relation 1: 1 est très souvent bénéfique, car pourquoi gaspiller l'espace dans le pool de mémoire tampon pour les données que vous n'utilisez probablement pas (similaire à la raison pour laquelle le stockage d'images volumineuses en format régulierVARBINARY(MAX)colonnes est un problème)? Vous augmentez également les performances des colonnes qui accèdent fréquemment en réduisant la taille des lignes et en ajustant ainsi davantage de lignes sur une page de données, ce qui rend les lectures (physiques et logiques) plus efficaces. Bien sûr, vous introduisez également une certaine inefficacité en ayant besoin de dupliquer le PK, et maintenant vous devez parfois joindre les deux tables, ce qui complique également (même légèrement) certaines requêtes.

Il existe donc plusieurs approches que vous pouvez adopter, et ce qui dépend le mieux de votre environnement et de ce que vous essayez d'accomplir.


J'avais l'impression que SQL Server stocke uniquement un pointeur vers une structure de données BLOB dédiée dans la table

Pas si simple. Vous pouvez trouver de bonnes informations ici, quelle est la taille du pointeur LOB pour les types (MAX) comme Varchar, Varbinary, Etc? , mais les bases sont les suivantes:

  • TEXT, NTEXTEt les IMAGEtypes de données (par défaut): pointeur de 16 octets
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(Par défaut):
    • Si les données peuvent tenir dans la ligne, elles seront placées là
    • Si les données sont inférieures à env. 40000 octets (le billet de blog lié indique 40000 comme limite supérieure, mais mes tests ont montré une valeur légèrement supérieure) ET s'il y a de la place sur la ligne pour cette structure, il y aura entre 1 et 5 liens directs vers les pages LOB, à partir de 24 octets pour le premier lien vers les 8000 premiers octets, et en augmentant de 12 octets pour chaque lien supplémentaire pour chaque ensemble supplémentaire de 8000 octets, jusqu'à 72 octets max.
    • Si les données dépassent env. 40 000 octets OU il n'y a pas assez de place pour stocker le nombre approprié de liens directs (par exemple, il ne reste que 40 octets sur la ligne et une valeur de 20 000 octets nécessite 3 liens, soit 24 octets pour le premier plus 12 pour les deux liens supplémentaires pour 48 octets) total requis dans l'espace en ligne), alors il y aura juste un pointeur de 24 octets vers une page d'arborescence de texte qui contient les liens vers les pages LOB).

7

Si les données doivent être stockées dans SQL Server pour une raison quelconque, je peux penser à quelques avantages de les stocker dans une table séparée. Certains sont plus convaincants que d'autres.

  1. Placer les données dans une table séparée signifie que vous pouvez les stocker dans une base de données distincte. Cela peut présenter des avantages pour la maintenance planifiée. Par exemple, vous ne pouvez exécuter DBCC CHECKDBque sur la base de données qui contient les données BLOB.

  2. Si vous ne placez pas toujours plus de 8 000 octets dans le BLOB, il est possible qu'il soit stocké en ligne pour certaines lignes. Vous ne le souhaiterez peut-être pas, car cela ralentira les requêtes qui accèdent aux données à l'aide de l'index cluster, même si la colonne n'est pas nécessaire à la requête. Le fait de placer les données dans un tableau séparé supprime ce risque.

  3. Lorsqu'il est stocké hors ligne, SQL Server utilise un pointeur jusqu'à 24 octets pour pointer vers la nouvelle page. Cela prend de l'espace et limite le nombre total de colonnes BLOB que vous pouvez ajouter à une seule table. Voir la réponse de srutzky pour plus de détails.

  4. Un index clusterstore columnstore ne peut pas être défini sur une table contenant une colonne BLOB. Cette limitation a été supprimée sera supprimée dans SQL Server 2017.

  5. Si vous décidez finalement que les données doivent être déplacées en dehors de SQL Server, il peut être plus facile d'effectuer cette modification si les données sont déjà dans une table distincte.


1
Quelques bons points ici (+1). Mais pour être clair sur # 3 (re: pointeur de 24 octets pour les données hors ligne), ce n'est pas toujours correct. J'explique (brièvement) au bas de ma réponse comment le type de données, la taille de la valeur et la quantité d'espace libre sur la ligne déterminent la taille du pointeur.
Solomon Rutzky
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.