Чому простір даних таблиці може займати в 4 рази більше розміру необроблених даних?


18

У мене є таблиця з 490 М рядками та 55 ГБ простору таблиці, тобто близько 167 байт у рядку. У таблиці є три стовпці: a VARCHAR(100), a DATETIME2(0)і a SMALLINT. Середня довжина тексту в VARCHARполі становить близько 21,5, тому необроблені дані повинні становити близько 32 байтів у рядку: 22 + 2 для числа VARCHAR, 6 для DATETIME2цілого і 2 для 16-бітного цілого числа.

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

Звичайно, повинно бути трохи накладних витрат, але 135 байт на рядок здається багато, особливо для великої таблиці. Чому це може бути? Хтось ще бачив подібні множники? Які фактори можуть впливати на кількість додаткового простору?

Для порівняння я спробував створити таблицю з двома INTполями та 1 М рядками. Необхідний простір даних становив 16,4 Мб: 17 байт на рядок, порівняно з 8 байтами необроблених даних. Ще одна тестова таблиця з текстовим INTта VARCHAR(100)заповненим тим самим текстом, що і реальна таблиця, використовує 39 байт у рядку (44 К рядків), де я б очікував 28 плюс трохи.

Тож виробничий стіл має значно більше накладних витрат. Це тому, що вона більша? Я очікую, що розміри індексу будуть приблизно N * log (N), але я не бачу, чому простір, необхідний для фактичних даних, має бути нелінійним.

Заздалегідь дякую за будь-які покажчики!

Редагувати:

Усі перелічені поля є NOT NULL. Реальна таблиця має кластеризовану ПК на VARCHARполі та в DATETIME2полі, у такому порядку. Для двох тестів першим INTбув (кластерний) ПК.

Якщо це має значення: таблиця - це запис результатів ping. Поля - це URL-адреса, дата / час ping та затримка в мілісекундах. Дані постійно додаються та ніколи не оновлюються, але дані періодично видаляються, щоб зменшити їх до кількох записів на годину за URL.

Редагувати:

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

Відповіді:


11

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

Завжди варто перевірити стан фрагментації за допомогою sys.dm_db_index_physical_stats в цих ситуаціях.

Редагувати: Після оновлення в коментарях

Середня щільність сторінки (до відновлення кластерного індексу) становила 24%, що ідеально відповідає початковому питанню. Сторінок було заповнено лише на 1/4, тож загальний розмір становив у 4 рази більше необробленого розміру даних.


7

Дискові структури мають накладні витрати:

  • заголовок рядка
  • null bitmap + pointer
  • Зсуви стовпців змінної довжини
  • вказівники на рядкові версії (необов’язково)
  • ...

Беручи 2 x 4 байти int стовпців, у вас є

  • Заголовок рядка 4 байти
  • 2-байтний покажчик на NULL растрову карту
  • 8 байт за 2 int стовпці
  • 3 байти растрової карти NULL

Вау 17 байт!

Ви можете зробити те ж саме для своєї другої тестової таблиці, яка має більше накладних витрат, як ваша оригінальна:

  • 2 байти для підрахунку стовпців змінної довжини
  • 2 байти на стовпчик змінної довжини

Чому різниця? Крім того (я не буду посилатися на них)

  • ви коли-небудь перебудовували індекси, щоб дефрагментувати їх?
  • делети не повертають пробіл
  • сторінки даних розділяться, якщо вставити їх в середину
  • оновлення можуть спричинити покажчики вперед (залишає пробіл)
  • рядок переповнення
  • вилучений стовпчик varchar без відновлення індексу або DBCC CLEANTABLE
  • купа або таблиця (у heap немає кластерного індексу = записи, розкидані по всьому)
  • Рівень ізоляції RCSI (додаткові 14 байт у рядку)
  • трейлінг пробілів (SET ANSI_PADDING за замовчуванням увімкнено) у varchar. Використовуйте DATALENGTH для checl, а не LEN
  • Запустіть sp_spaceused з @updateusage = 'true'
  • ...

Дивіться це: SQL Server: як створити таблицю, яка заповнює одну сторінку 8 Кб?

Від SO:


Зразок колонки 2x4 байт int не на 100% правильний. У вас буде 4-байтний заголовок рядка (2 байти статусу та 2 байти для розміру даних фіксованої довжини). Тоді у вас буде 2х4 байти для даних. Два байти для підрахунку стовпців і один байт для нульового растрового зображення, даючи загальну довжину запису 15 байт, а не 17.
Марк С. Расмуссен

@Mark S. Rasmussen: Звідки ви отримуєте "2 байти для фіксованого розміру даних"? MSDN? А нульова растрова карта
gbn

Ух, чудова деталь! Я враховував поле довжини VARCHARs в моїй оцінці вище, але не для підрахунку стовпців. У цій таблиці немає полів NULLable (слід було б це згадати), чи все ж вона виділяє для них байти?
Йон усіх торгів

Чи вплине перебудова індексів на частину даних, необхідного простору? Можливо, перебудова кластерного індексу дозволила б. Вставки трапляються в середині, багато, хоча якби я змінив порядок кластеризуючих полів, який би зупинився. Більшість решти не повинна застосовуватися в цьому випадку, але це велике посилання для загальної справи. Я перевірю ваші посилання. Хороший матеріал!
Йон усіх торгів

1
@gbn 2 байти для розміру даних фіксованої довжини є частиною 4-байтного заголовка рядка, який ви згадуєте. Це вказівник, який вказує на кінець частини фіксованої довжини даних / початок відліку стовпців / нульову растрову карту. Растровий файл NULL - це не завжди три байти. Якщо ви включите кількість стовпців, то це буде мінімум три байти, але може бути більше - я розділив растрову карту і кількість стовпців у своєму описі. Крім того, растровий файл NULL не завжди присутній, хоча він буде в цьому випадку.
Марк С. Расмуссен

5

Чи змінилися типи даних з часом? Видалено стовпці змінної довжини? Чи часто дефрагментовані індекси, але ніколи не відбудовувалися? Видалено чимало рядків чи значно оновлено стовпчики змінної довжини? Деякий гарне обговорення тут .


Я на 97% впевнений, що я не змінив тип даних і не видалив поле. Якби я це зробив, було б насправді рано, коли стіл мав набагато менше рядів. Видалення чи оновлення немає, дані лише додаються.
Йон усіх торгів

Виправлення: делетів є , і зовсім небагато. Таблиця має чистий приріст, тому я думаю, що цей простір буде швидко використаний.
Йон усіх торгів

З великою кількістю видалених даних можна або не можуть повторно використовувати. Який ключ кластеризації таблиці? Чи вставлені посередині столу чи в кінці?
mrdenny

Кластерний ключ є складовим, на VARCHARі DATETIME2полях, в такому порядку. Вкладиші будуть розподілені рівномірно для першого поля. Для другого поля нові значення і завжди будуть більше, ніж будь-яке існуюче.
Джон усіх торгів
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.