Чому рекомендується зберігати BLOB в окремих таблицях SQL Server?


29

Ця високооцінена відповідь на відповідь рекомендує розміщувати зображення в окремі таблиці, навіть якщо існує співвідношення лише 1: 1 з іншою таблицею:

Якщо ви вирішили помістити свої зображення в таблицю SQL Server, я настійно рекомендую використовувати окрему таблицю для зберігання цих зображень - не зберігайте фотографії співробітника в таблиці співробітників - зберігайте їх в окремій таблиці. Таким чином, таблиця Співробітника може залишатися худорлявою, середньою і дуже ефективною, припускаючи, що вам не завжди потрібно вибирати фотографію працівника як частину ваших запитів.

Чому? У мене склалося враження, що SQL Server зберігає в таблиці лише вказівник на якусь виділену структуру даних BLOB , то чому б не турбуватися створювати ще один шар непрямості вручну? Чи справді це значно покращує продуктивність? Якщо так, то чому?

Відповіді:


15

Хоча я не погоджуюся, що BLOB повинні просто знаходитися в іншій таблиці - вони взагалі не повинні знаходитись у базі даних . Зберігайте вказівник на те, де файл живе на диску, а потім просто дістаньте його з бази даних ...

Основна проблема, яку вони викликають (для мене), - це індексація. Використовуючи XML для планів запитів, тому що всі мають, давайте зробимо таблицю:

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)

Це всього 1000 рядків, але перевірка розміру ...

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

Це понад 40 Мб всього за 1000 рядків. Якщо припустити, що ви додаєте 40 Мб на кожні 1000 рядків, це може отримати досить некрасиво досить швидко. Що відбувається, коли ти потрапив на 1 мільйон рядків? Це приблизно близько 1 ТБ даних.

Горіхи

Будь-які запити, які потребують використання кластерного індексу, тепер повинні прочитати всі ці дані BLOB для уточнення пам'яті : коли посилається стовпець даних BLOB.

Чи можете ви придумати кращі способи використання пам'яті SQL Server, ніж зберігання BLOB? Бо я впевнений, що може.

Розширення його на некластеризовані індекси:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

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

Ви можете спроектувати некластеризовані індекси, щоб значною мірою уникнути стовпця BLOB, тому звичайні запити можуть уникнути кластерного індексу, але як тільки вам потрібен стовпець BLOB, вам потрібен кластерний індекс.

Якщо ви додасте його як INCLUDEDстовпець до некластеризованого індексу, щоб уникнути ключового сценарію пошуку, ви отримуєте гігантські некластеризовані індекси:введіть тут опис зображення

Більше проблем вони викликають:

  • Якщо хтось виконує SELECT *запит, він отримує всі дані BLOB.
  • Вони займають місце в резервних копіях і відновлюються, сповільнюючи їх
  • Вони сповільнюються DBCC CHECKDB, бо я знаю, що ви перевіряєте на корупцію, правда?
  • І якщо ви робите будь-яке обслуговування індексу, вони також сповільнюють це.

Сподіваюся, це допомагає!


7
Оскільки користувачі зазвичай вводять SELECT *.
Брент Озар

Я думаю, мінуси, які ви згадуєте, є частиною того, чому він рекомендував помістити фотографії в окрему таблицю. Якщо я створюю різні звіти про користувачів, мені не потрібен їх файл із зображеннями. Якщо я завантажую сторінку профілю одного користувача, то тоді я приєднаюся до таблиці блоків, правда? Я щось тут пропускаю (тобто чи ваші недоліки насправді все ще застосовуються навіть у тому сценарії, який я описав?)
BVernon

11

Наскільки великі ці зображення, і скільки ви очікуєте мати? Хоча я здебільшого погоджуюся з @sp_BlitzErik , я думаю, що є певні сценарії, коли це нормально робити, і тому це допоможе мати чіткішу картину того, що насправді тут запитують.

Деякі варіанти врахування, які полегшують більшість негативних аспектів, на які вказував Ерік, є:

  • FILESTREAM (починаючи з SQL Server 2008)
  • FileTables (починаючи з SQL Server 2012)

Обидва ці варіанти розроблені як посередництво між збереженням BLOB або повністю в SQL Server, або повністю зовні (за винятком рядкового кола для збереження шляху). Вони дозволяють BLOB, щоб вони були частиною моделі даних та брали участь в Операціях, не витрачаючи при цьому місця в буферному пулі (тобто пам'яті). Дані BLOB все ще включаються в резервні копії, що дозволяє їм займати більше місця та займати більше часу для резервного копіювання тавідновити. Однак мені важко сприймати це як справжній негатив, враховуючи, що якщо воно є частиною програми, тоді його потрібно якось створити резервну копію, а лише стовпець з рядком, що містить шлях, повністю відключений і дозволяє файлам BLOBs отримувати видалено без вказівки на це в БД (тобто недійсні вказівники / відсутні файли). Це також дозволяє файли "видаляти" всередині БД, але все ще існують у файловій системі, яку в кінцевому підсумку потрібно буде очистити (тобто головний біль). Але якщо файли ВЕЛИЧЕЗНІ, то, можливо, найкраще залишити повністю поза SQL Server, крім стовпця шляху.

Це допомагає у питанні "всередині або зовні", але не стосується питання однієї таблиці проти кількох таблиць. Я можу сказати, що поза цим конкретним питанням, безумовно, є вагомі випадки поділу таблиць на групи стовпців на основі моделей використання. Часто, коли в них є 50 або більше стовпців, є такі, до яких часто звертаються, а інші - ні. Деякі колонки записуються часто, а деякі в основному читаються. Відокремлення часто доступних та нечасто доступних стовпців до кількох таблиць із співвідношенням 1: 1 досить часто вигідно, тому що чому витрачати простір у буферному пулі на дані, які ви, мабуть, не використовуєте (подібно до того, як зберігати великі зображення в звичайнихVARBINARY(MAX)стовпці - це проблема)? Ви також збільшуєте продуктивність стовпців, що часто отримують доступ, зменшуючи розмір рядків і, отже, поміщаючи більше рядків на сторінку даних, роблячи читання (фізичні та логічні) більш ефективними. Звичайно, ви також вводите деяку неефективність, потребуючи дублювання ПК, і тепер іноді вам потрібно з'єднати дві таблиці, що також ускладнює (навіть якщо трохи) деякі запити.

Отже, ви можете скористатися кількома підходами, і що найкраще залежить від вашого оточення та того, що ви намагаєтеся виконати.


У мене було враження, що SQL Server зберігає лише вказівник на деяку виділену структуру даних BLOB у таблиці

Не так просто. Тут ви можете знайти добру інформацію. Який розмір покажчика LOB для типів (MAX) типу Varchar, Varbinary, Etc? , але основи:

  • TEXT, NTEXTта IMAGEтипи даних (за замовчуванням): 16-байтний покажчик
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(За замовчуванням):
    • Якщо дані можуть вміститися в рядку, то вони будуть розміщені там
    • Якщо дані менше ок. 40 000 байтів (пов’язана публікація в блозі показує 40 000 як верхня межа, але моє тестування показало дещо вищу величину) І якщо в рядку буде місце для цієї структури, то буде між 1 і 5 прямими посиланнями на сторінки LOB, починаючи з 24 байти за перше посилання на перші 8000 байт, і збільшиться на 12 байт за кожне додаткове посилання для кожного додаткового набору 8000 байт, максимум до 72 байт.
    • Якщо дані перевищують прибл. 40 000 байт АБО не вистачає місця для зберігання відповідної кількості прямих посилань (наприклад, лише 40 байтів, що залишилися в рядку, а значення 20 000 байт потребує 3 посилання, що становить 24 байти для першого плюс 12 для двох додаткових посилань на 48 байт загальний необхідний простір у рядку), тоді буде просто 24-байтний вказівник на сторінку текстового дерева, яка містить посилання на сторінки LOB).

7

Якщо дані необхідно зберігати в SQL Server з будь-якої причини, я можу придумати кілька переваг для їх зберігання в окремій таблиці. Деякі переконливіші за інші.

  1. Якщо помістити дані в окрему таблицю, ви можете зберігати їх в окремій базі даних. Це може мати переваги для планового обслуговування. Наприклад, ви можете запускатись DBCC CHECKDBлише в базі даних, яка містить дані BLOB.

  2. Якщо ви не завжди поміщаєте більше 8000 байт у BLOB, тоді можливо зберігати його в рядку для деяких рядків. Ви можете цього не хотіти, оскільки це сповільнить запити, які отримують доступ до даних за допомогою кластерного індексу, навіть якщо стовпець не потрібен для запиту. Поміщення даних в окрему таблицю знімає цей ризик.

  3. При зберіганні поза рядком SQL Server використовує до 24 байт-покажчик, щоб вказати на нову сторінку. Це займає простір і обмежує загальну кількість стовпців BLOB, які ви можете додати до однієї таблиці. Дивіться відповідь srutzky для отримання більш детальної інформації.

  4. Кластерний індекс стовпців стовпців не може бути визначений у таблиці, що містить стовпець BLOB. Це обмеження було знято, буде видалено в SQL Server 2017.

  5. Якщо ви врешті вирішите, що дані слід переміщувати за межі SQL Server, можливо, це буде простіше зробити це, якщо дані вже є в окремій таблиці.


1
Тут є кілька хороших моментів (+1). Але щоб бути зрозумілим щодо №3 (re: 24 байт-покажчик для даних, що не входять у рядок), це не завжди правильно. Я пояснюю (коротко) внизу своєї відповіді, як тип даних, розмір значення та кількість вільного місця в рядку визначають розмір вказівника.
Соломон Руцький
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.