web-dev-qa-db-fra.com

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

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

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 épurée 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 ne stocke qu'un pointeur vers une structure de données BLOB dédiée dans le tableau, 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?

31
Heinzi

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

NUTS

Toutes les requêtes qui doivent utiliser votre index cluster doivent désormais lire toutes ces données BLOB en mémoire clarification: 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 colonne INCLUDED à un index non cluster pour éviter un scénario de recherche de clé, vous vous retrouvez avec de gigantesques index non cluster: enter image description here

Plus de problèmes qu'ils causent:

  • Si quelqu'un exécute un SELECT * requête, ils obtiennent 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 tu vérifies la corruption, non?
  • Et si vous effectuez une maintenance d'index, cela ralentit également.

J'espère que cela t'aides!

16
Erik Darling

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 existe certains scénarios où il 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 perdre 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 à sauvegarder et à restaurer. Cependant, j'ai du mal à voir cela comme un vrai négatif étant donné que si elle fait partie de l'application, elle doit être sauvegardée 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'est-à-dire des 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. La séparation des colonnes à accès fréquent et à accès fréquent en plusieurs tables ayant une relation 1: 1 est 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 dans des VARBINARY(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.

Vous pouvez donc adopter plusieurs approches, 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:

  • TEXT, NTEXT et IMAGE types 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, alors il y aura entre 1 et 5 liens directs vers les pages LOB, commençant à 24 octets pour le premier lien vers les 8000 premiers octets, et 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, ce qui correspond à 24 octets pour le premier plus 12 pour les deux liens supplémentaires pour un total de 48 octets d'espace de ligne requis), il n'y aura alors qu'un pointeur de 24 octets vers une page d'arborescence de texte qui contient les liens vers les pages LOB).
12
Solomon Rutzky

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 distincte. 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 pouvez exécuter DBCC CHECKDB uniquement sur la base de données contenant les données BLOB.

  2. Si vous ne mettez pas toujours plus de 8000 octets dans le BLOB, il est possible qu'il soit stocké dans la 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.

8
Joe Obbish