¿Por qué se recomienda almacenar BLOB en tablas separadas de SQL Server?


29

Esta respuesta SO altamente votada recomienda colocar imágenes en tablas separadas, incluso si solo hay una relación 1: 1 con otra tabla:

Si decide colocar sus imágenes en una tabla de SQL Server, le recomiendo encarecidamente que use una tabla separada para almacenar esas imágenes, no almacene la foto del empleado en la tabla del empleado, guárdelas en una tabla separada. De esa manera, la tabla de empleados puede mantenerse delgada, mala y muy eficiente, suponiendo que no siempre necesite seleccionar la foto del empleado también, como parte de sus consultas.

¿Por qué? Tenía la impresión de que SQL Server solo almacena un puntero a alguna estructura de datos BLOB dedicada en la tabla, entonces, ¿por qué molestarse en crear manualmente otra capa de indirección? ¿Realmente mejora significativamente el rendimiento? ¿Si es así por qué?

Respuestas:


15

Si bien no estoy de acuerdo con que los BLOB solo estén en otra tabla, no deberían estar en la base de datos . Almacene un puntero a donde vive el archivo en el disco, y luego simplemente obtenga eso de la base de datos ...

El problema principal que causan (para mí) es con la indexación. Usando XML con planes de consulta, porque todos los tienen, hagamos una tabla:

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)

Son solo 1000 filas, pero verificando el tamaño ...

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

Tiene más de 40 MB para solo 1000 filas. Suponiendo que agregue 40 MB cada 1000 filas, eso puede volverse bastante feo rápidamente. ¿Qué sucede cuando golpeas 1 millón de filas? Eso es solo alrededor de 1 TB de datos, allí.

NUECES

Cualquier consulta que necesite usar su índice agrupado ahora debe leer todos esos datos BLOB en la aclaración de la memoria : cuando se hace referencia a la columna de datos BLOB.

¿Se te ocurren mejores formas de usar la memoria de SQL Server que almacenar BLOB? Porque seguro que puedo.

Expandiéndolo a índices no agrupados:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

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

Puede diseñar sus índices no agrupados para evitar en gran medida la columna BLOB para que las consultas regulares puedan evitar el índice agrupado, pero tan pronto como necesite esa columna BLOB, necesitará el índice agrupado.

Si lo agrega como una INCLUDEDcolumna a un índice no agrupado para evitar un escenario de búsqueda de claves, terminará con gigantescos índices no agrupados:ingrese la descripción de la imagen aquí

Más problemas que causan:

  • Si alguien ejecuta una SELECT *consulta, obtiene todos esos datos BLOB.
  • Ocupan espacio en las copias de seguridad y restauraciones, ralentizándolas
  • Disminuyen la velocidad DBCC CHECKDB, porque sé que estás buscando corrupción, ¿verdad?
  • Y si realiza algún mantenimiento de índice, también lo ralentizarán.

¡Espero que esto ayude!


77
Porque los usuarios suelen escribir SELECT *.
Brent Ozar

Creo que las desventajas que mencionas son parte de por qué recomendó poner las imágenes en una tabla separada. Si estoy ejecutando varios informes sobre usuarios, no necesito su archivo de imagen. Si estoy cargando la página de perfil de un solo usuario, entonces es cuando me uno a la tabla de blobs, ¿verdad? ¿Me estoy perdiendo algo aquí (es decir, sus desventajas todavía se aplican incluso en este escenario que describí?)
BVernon

11

¿Qué tan grandes son estas imágenes y cuántas espera tener? Si bien estoy mayormente de acuerdo con @sp_BlitzErik , creo que hay algunos escenarios en los que está bien hacer esto, por lo que sería útil tener una idea más clara de lo que realmente se solicita aquí.

Algunas opciones a considerar que alivian la mayoría de los aspectos negativos señalados por Erik son:

Ambas opciones están diseñadas para ser un punto intermedio entre el almacenamiento de BLOB, ya sea completamente en SQL Server o completamente fuera (excepto por un colun de cadena para retener la ruta). Permiten que los BLOB sean parte del modelo de datos y participen en las Transacciones sin desperdiciar espacio en el grupo de búferes (es decir, la memoria). Los datos BLOB todavía se incluyen en las copias de seguridad, lo que hace que ocupen más espacio y tarden más en realizar copias de seguridad yrestaurar. Sin embargo, me resulta difícil ver esto como un verdadero negativo dado que si es parte de la aplicación, entonces debe respaldarse de alguna manera, y tener solo una columna de cadena que contenga la ruta está completamente desconectada y permite que los archivos BLOB se obtengan eliminado sin indicación de eso en la base de datos (es decir, punteros inválidos / archivos faltantes). También permite que los archivos se "eliminen" dentro de la base de datos, pero que aún existan en el sistema de archivos, que eventualmente deberán limpiarse (es decir, dolor de cabeza). Pero, si los archivos son ENORMES, entonces quizás sea mejor dejar completamente fuera de SQL Server, excepto la columna de ruta.

Eso ayuda con la pregunta "dentro o fuera", pero no toca la pregunta de una sola tabla versus la de varias tablas. Puedo decir que, más allá de esta pregunta específica, existen casos válidos para dividir las tablas en grupos de columnas según los patrones de uso. A menudo, cuando uno tiene 50 o más columnas, hay algunas a las que se accede con frecuencia y otras no. Algunas columnas se escriben con frecuencia, mientras que otras se leen principalmente. Separar las columnas de acceso frecuente frente a las que se accede con poca frecuencia en varias tablas que tienen una relación 1: 1 a menudo es beneficioso porque por qué desperdiciar el espacio en el Buffer Pool para los datos que probablemente no esté utilizando (similar a por qué almacenar imágenes grandes en forma regularVARBINARY(MAX)columnas es un problema)? También aumenta el rendimiento de las columnas de acceso frecuente reduciendo el tamaño de la fila y, por lo tanto, ajustando más filas en una página de datos, haciendo que las lecturas (tanto físicas como lógicas) sean más eficientes. Por supuesto, también introduce cierta ineficiencia al necesitar duplicar el PK, y ahora a veces necesita unir las dos tablas, lo que también complica (aunque solo sea ligeramente) algunas consultas.

Por lo tanto, hay varios enfoques que podría adoptar, y lo mejor depende de su entorno y de lo que está tratando de lograr.


Tenía la impresión de que SQL Server solo almacena un puntero a alguna estructura de datos BLOB dedicada en la tabla

No es tan simple. Puede encontrar buena información aquí. ¿Cuál es el tamaño del puntero LOB para tipos (MAX) como Varchar, Varbinary, Etc? , pero los conceptos básicos son:

  • TEXT, NTEXTy IMAGEtipos de datos (por defecto): puntero de 16 bytes
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(Por defecto):
    • Si los datos pueden caber en la fila, se colocarán allí
    • Si los datos son inferiores a aprox. 40,000 bytes (la publicación del blog vinculada muestra 40,000 como el límite superior, pero mis pruebas mostraron un valor ligeramente mayor) Y si hay espacio en la fila para esta estructura, entonces habrá entre 1 y 5 enlaces directos a páginas LOB, comenzando en 24 bytes para el primer enlace a los primeros 8000 bytes, y subiendo en 12 bytes por cada enlace adicional para cada conjunto adicional de 8000 bytes, hasta 72 bytes como máximo.
    • Si los datos han terminado aprox. 40,000 bytes O no hay suficiente espacio para almacenar el número apropiado de enlaces directos (por ejemplo, solo quedan 40 bytes en la fila y un valor de 20,000 bytes necesita 3 enlaces que son 24 bytes para el primero más 12 para los dos enlaces adicionales para 48 bytes espacio total requerido en la fila), entonces solo habrá un puntero de 24 bytes a una página del árbol de texto que contiene los enlaces a las páginas LOB).

7

Si los datos deben almacenarse en SQL Server por alguna razón, puedo pensar en algunos beneficios para almacenarlos en una tabla separada. Algunos son más convincentes que otros.

  1. Poner los datos en una tabla separada significa que puede almacenarlos en una base de datos separada. Esto puede tener ventajas para el mantenimiento programado. Por ejemplo, DBCC CHECKDBsolo puede ejecutarse en la base de datos que contiene los datos BLOB.

  2. Si no siempre coloca más de 8000 bytes en el BLOB, es posible que se almacene en fila para algunas filas. Es posible que no desee eso porque ralentizará las consultas que acceden a los datos utilizando el índice agrupado, incluso si la consulta no necesita la columna. Poner los datos en una tabla separada elimina este riesgo.

  3. Cuando se almacena fuera de la fila, SQL Server utiliza un puntero de hasta 24 bytes para apuntar a la nueva página. Eso ocupa espacio y limita el número total de columnas BLOB que puede agregar a una sola tabla. Vea la respuesta de srutzky para más detalles.

  4. Un índice de almacén de columnas agrupado no se puede definir en una tabla que contiene una columna BLOB. Esta limitación eliminada se eliminará en SQL Server 2017.

  5. Si finalmente decide que los datos se deben mover fuera de SQL Server, puede ser más fácil hacer ese cambio si los datos ya están en una tabla separada.


1
Algunos buenos puntos aquí (+1). Pero para ser claros sobre el n. ° 3 (re: puntero de 24 bytes para datos fuera de fila), eso no siempre es correcto. Explico (brevemente) en la parte inferior de mi respuesta cómo el tipo de datos, el tamaño del valor y la cantidad de espacio libre en la fila determinan el tamaño del puntero.
Solomon Rutzky
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.