Знайдіть нестиснений розмір усіх таблиць у базі даних


12

У Dynamics AX існує механізм кешування, за допомогою якого таблиці можуть бути налаштовані для завантаження в пам'ять і кешування. Цей кеш обмежений певною кількістю КБ, щоб запобігти проблемам із пам'яттю. Налаштування, про яке я говорю, викликається entiretablecacheі завантажує всю таблицю в пам'ять, як тільки запитається один запис.

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

Однак тепер компресія вступає в гру і такі речі, як sp_spaceused або sys.allocation_units, здається, повідомляють про простір, який фактично використовується стислими даними.

Очевидно, сервер додатків працює з нестисненими даними, тому розмір даних на диску в SQL Server не має значення. Мені потрібен фактичний розмір, який матимуть нестиснені дані.

Я знаю sp_estimate_data_compression_savings, але, як випливає з назви, це лише оцінка.
Я вважаю за краще, щоб розмір був максимально правильним.

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

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

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

Припустимо, буфер в додатку - це розмір даних. Bigint - це завжди розмір bigint, а тип даних символів - 2 байти на символ (unicode). Дані BLOB також приймають розмір даних, перерахунок - це, в основному, int, а числові дані - числові (38,12), datetime - це розмір дати. Також NULLзначень немає , вони або зберігаються як порожній рядок, 1900-01-01або нуль.

Немає документації про те, як це реалізується, але припущення засновані на деякому тестуванні та сценаріях, які використовуються PFE та командою підтримки (яка також ігнорує стиснення, мабуть, оскільки перевірка вбудована в додаток, і додаток не може визначити якщо базові дані стиснуті), які також перевіряють розміри таблиці. Наприклад, у цьому посиланні зазначено:

Уникайте використання кеш-файлів EntireTable для великих таблиць (в AX 2009 понад 128 Кб або 16 сторінок, в AX 2012 над налаштуваннями програми «Весь розмір кеша таблиці» [за замовчуванням: 32 КБ або 4 сторінки]) - замість цього перейдіть до запису кешування.


3
Це хакі, але, можливо, відновлена ​​копія з відключеною компресією була б найбільш точною. Потім ви також тестуєте відновлення, завдяки чому ви виглядаєте як ТОП-1 DBA.
Ерік Дарлінг

Повірте, це було б найкращим варіантом. Можуть існувати способи сортування спроб і занять математикою. Скільки рядків за визначеними стовпчиковими типами даних і довжинами, помноженими, потім додайте в індекси тощо. Це набагато більше роботи, ніж створення сценаріїв відновлення та відключення стиснення. @Sp_BlitzErik пропонує вище. А хто б не хотів бути топ-1 DBA?
Майк Уолш

SUM (datalength ()) для всіх стовпців отримують нестиснений розмір даних?
Tapakah Ua

@sp_BlitzErik Це може бути відповіддю замість коментаря.
Том V - спробуйте topanswers.xyz

Відповіді:


7

Мені потрібен фактичний розмір, який матимуть нестиснені дані.
...
Я вважаю за краще, щоб розмір був максимально правильним.

Хоча прагнення до цієї інформації, безумовно, зрозуміле, одержання цієї інформації, особливо в контексті "максимально правильного", складніше, ніж всі очікують через помилкові припущення. Незалежно від того, чи йдеться про ідею нестисненої тіньової таблиці, згадану у запитанні, чи пропозицію @ sp_BlitzErik у коментарі про відновлення БД та її видалення для перевірки, не слід вважати, що розмір нестисненої таблиці == розмір зазначених даних у пам'яті на сервері додатків:

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

    Питання було оновлено до стану: так, всі рядки кешуються.

  2. Будова над головою

    1. На стороні БД:
      Сторінка та накладні рядки на стороні БД: кількість рядків, розміщених на сторінці, визначається багатьма чинниками, які можуть скинути оцінки. Навіть при FILLFACTOR100 (або 0) все ще є певне невикористане місце на сторінці, оскільки його не вистачить для цілого ряду. І це додатково до заголовка сторінки. Крім того, якщо включена будь-яка функція Snapshot Isolation, я вважаю, що додаткові 13 байтів на рядок будуть зайняті номером версії, і це скине оцінки. Є й інші деталі, пов’язані з фактичним розміром рядка (растрова карта NULL, стовпчики змінної довжини тощо), але пункти, згадані до цих пір, мають вказувати лише на це.
    2. На стороні сервера додатків:
      Який тип колекції використовується для зберігання кешованих результатів? Я припускаю, що це .NET додаток, так це DataTable? Загальний список? Сортований словник? Кожен тип колекції має різну кількість підслуханих. Я б не очікував, що будь-який із варіантів обов'язково відображатиме накладні сторінки та рядки на стороні БД, особливо в масштабі (я впевнений, що невелика кількість рядків може не мати достатньо різноманітних для значення, але ви не шукаєте відмінностей в сотнях байт або всього в декількох кБ).
  3. Типи даних
    1. На стороні БД:
      CHAR/ VARCHARдані зберігаються в 1 байті на символ (на даний момент ігноруючи двобайтові символи). XMLоптимізовано не займати майже стільки місця, скільки передбачає подання тексту. Цей тип даних створює словник імен елементів та атрибутів та замінює фактичні посилання на них у документі їх відповідними ідентифікаторами (начебто добре). В іншому випадку рядкові значення - це всі UTF-16 (2 або 4 байти на "символ"), як і NCHAR/ NVARCHAR. DATETIME2становить від 6 до 8 байт. DECIMALстановить від 5 до 17 байт (залежно від точності).
    2. На стороні сервера додатків:
      рядки (знову ж таки, припускаючи .NET) завжди UTF-16. Немає оптимізації для 8-бітових рядків, таких як те, що VARCHARмає місце. Але НЕ, рядки також можуть бути "інтернованими", що є спільною копією, на яку можна посилатися багато разів (але я не знаю, чи це працює для рядків у колекціях, або якщо так, якщо це працює для всіх типів колекцій). XMLможе або не може зберігатися однаково в пам'яті (мені доведеться це переглянути). DateTimeзавжди 8 байт (наприклад , T-SQL DATETIME, але не так, як DATE, TIMEабо DATETIME2). Decimalце завжди 16 байт .

Все, що говорити: на базі БД майже нічого не можна зробити, щоб отримати навіть досить точний розмір площі пам’яті на стороні сервера додатків. Вам потрібно знайти спосіб допитувати сам сервер додатків після завантаження певної таблиці, тому знайте, наскільки він великий. І я не впевнений, чи дозволить налагоджувач побачити розмір виконуваної колекції. Якщо немає, то єдиний спосіб підібратися б пройти через всі рядки таблиці, множачи кожен стовпець у відповідному .NET розмірі (наприклад , INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃, і т.д.), але по- , як і раніше залишає питання накладних витрат колекції плюс кожен елемент колекції.

З огляду на деяке нове визначення у питанні, ймовірно, можна зробити наступний запит, щоб наблизитися. І неважливо, стискається чи ні таблиця, хоча кожен повинен визначити, чи сканування всіх рядків підходить у виробництві (можливо, це зроблено під час відновлення або в неробочий час):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Але пам’ятайте, що це не враховує накладні елементи колекції або колекції. І не впевнений, чи зможемо ми отримати це значення без налагоджувача (або, можливо, щось на зразок ILSpy, але я не рекомендую цього, оскільки це може порушити EULA залежно від місцевих законів).


Ми закінчили реалізовувати перевірки в коді, щоб бути впевненим у розмірі буфера під час подання програми.
Том V - спробуйте topanswers.xyz

6

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

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

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

  1. Якщо таблиця містить лише статичні типи даних (без рядків і крапок), ви можете приблизно визначити кількість рядків, переглянувши sys.partitionsта обчисливши розмір таблиці, використовуючи визначення стовпців.
  2. Якщо таблиця з великою кількістю рядків містить достатньо статичних стовпців типів даних, ви, можливо, зможете усунути її як занадто велику, не дивлячись на її дані. Наприклад, таблиця з 10 мільйонами рядків і 5 BIGINTстовпців може мати розмір цих даних розміром 10000000 * (8 + 8 + 8 + 8 + 8) = 400 М байт, що може бути більше, ніж обмеження розміру кешу S. Не має значення, чи є в ньому також колонка з рядками.
  3. Якщо таблиця з кількома рядками є достатньо малою, ви, можливо, зможете підтвердити, що вона знаходиться нижче межі, просто припустивши, що кожен тип динамічних даних має максимально можливий розмір. Наприклад, таблиця з 100 рядками зі BIGINTстовпцем та NVARCHAR(20)стовпцем не може перевищувати 100 * (8 + 2 * 20) = 4800 байт.
  4. Може бути правдою, що якщо таблиця має стислий розмір у SQL Server, який більший на деякий фактор S, то в кеш вкрай малоймовірно. Вам доведеться зробити тестування, щоб з’ясувати, чи існує таке значення.
  5. Вам може пощастити, що у всіх динамічних стовпцях є статистика про них. Статистика містить інформацію про середню довжину, яка може бути достатньо точною для ваших цілей.

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

Ось декілька ідей щодо швидшого запиту, який переглядає дані таблиці:

  1. Для великих таблиць ви можете використовуватись до TABLESAMPLEтих пір, поки розмір вибірки буде досить великим.
  2. Для великих таблиць із кластерним ключем може бути корисно обробляти їх партіями кластерного ключа. На жаль, я не знаю способу обчислити SUM()те, що виходить зранку на основі значення цього сукупності. Я тільки коли-небудь бачив цю роботу ROW_NUMBER(). Але ви можете сканувати перші 10% таблиці, зберегти обчислений розмір даних, сканувати наступні 10% тощо. Для таблиць, які є занадто великими для кешу, ви можете зберегти значну кількість роботи при такому підході, закривши достроково.
  3. Для деяких таблиць вам може пощастить мати індекси покриття у всіх динамічних стовпцях. Залежно від розміру рядка чи інших факторів сканування кожного індексу одночасно може бути швидшим, ніж сканування таблиці. Ви також можете припинити цей процес достроково, якщо розмір таблиці занадто великий після прочитання індексу в одному стовпчику.
  4. Середня довжина ваших динамічних стовпців може не сильно змінюватися з часом. Це може бути практичним, щоб заощадити середні довжини, які ви обчислюєте, і використовувати ці значення у своїх розрахунках на деякий час. Ви можете скинути ці значення на основі активності DML у таблицях або на основі іншої метрики.
  5. Якщо можливо створити тести над усіма таблицями для розробки алгоритму, то, можливо, ви зможете скористатися шаблонами даних. Наприклад, якщо ви обробляєте таблиці, починаючи з найменших спочатку, ви можете виявити, що коли ви обробляєте 10 (я зробив це число) таблиць у рядку, який є занадто великим для кешу, то малоймовірно, що будь-які великі таблиці помістяться в кеш. Це може бути прийнятним, якщо нормально виключити кілька таблиць, які, можливо, могли б вміститися в кеш.

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


2
Я не думав про підхід виключити такі таблиці, мені подобається підхід
Том V - спробуйте topanswers.xyz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.