Найти несжатый размер всех таблиц в базе данных

12

В Dynamics AX существует механизм кэширования, в котором таблицы можно настроить для загрузки в память и кэширования. Этот кэш ограничен определенным объемом в КБ для предотвращения проблем с памятью. Настройка, о которой я говорю, вызывается entiretablecacheи загружает всю таблицу в память, как только запрашивается одна запись.

До недавнего времени мы использовали некоторые сценарии, чтобы проверить размер таблиц с этим параметром, чтобы увидеть, превышает ли размер таблицы этот предел.

Однако теперь сжатие вступает в игру, и такие вещи, как sp_spaceused или sys.allocation_units, по- видимому, сообщают пространство, фактически используемое сжатыми данными.

Очевидно, что сервер приложений работает с несжатыми данными, поэтому размер данных на диске в SQL Server не имеет значения. Мне нужен реальный размер несжатых данных.

Я знаю о sp_estimate_data_compression_savings, но, как следует из названия, это только оценка.
Я бы предпочел, чтобы размер был максимально правильным.

Единственный способ, которым я мог придумать, - это какой-то сложный динамический SQL, создающий несжатые таблицы с той же структурой, что и сжатые таблицы, вставляя сжатые данные в эту теневую таблицу и затем проверяя размер этой теневой таблицы.
Излишне говорить, что это немного утомительно и требует времени для работы с базой данных в несколько сотен ГБ.

Powershell мог бы быть вариантом, но я не хотел бы перебирать все таблицы, чтобы выполнить select *над ними проверку размера в сценарии, так как это просто заполнило бы кэш и, вероятно, заняло бы много времени.

Короче говоря, мне нужен способ получить размер для каждой таблицы, поскольку она будет однажды распакована и с фрагментацией из уравнения, представленного приложению, если это возможно. Я открыт для разных подходов, T-SQL предпочтительнее, но я не против Powershell или других творческих подходов.

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

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

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

Том V - попробуйте topanswers.xyz
источник
3
Это смешно, но, возможно, наиболее точной будет восстановленная копия с отключенным сжатием. Затем вы также тестируете восстановление, что делает вас похожим на TOP 1 DBA.
Эрик Дарлинг
Поверьте, это будет вашим лучшим выбором. Там могут быть способы, чтобы попытаться сделать математику. Сколько строк на определенные столбчатые типы данных и длины умножают, затем добавляют в индексы и т. Д. Это гораздо больше, чем сценарий восстановления и отключение сжатия, предложенное @sp_BlitzErik выше. И кто не хотел бы быть ТОП 1 DBA?
Майк Уолш
SUM (длина данных ()) для всех столбцов получить несжатый размер данных?
Tapakah Ua
@sp_BlitzErik Это может быть ответ, а не комментарий.
Том V - попробуйте topanswers.xyz

Ответы:

7

Мне нужен реальный размер несжатых данных.
...
Я бы предпочел, чтобы размер был максимально правильным.

Хотя стремление к этой информации, безусловно, понятно, получение этой информации, особенно в контексте «как можно более точного», сложнее, чем все ожидают из-за ошибочных предположений. Делаете ли вы идею несжатой теневой таблицы, упомянутую в вопросе, или предложение @ sp_BlitzErik в комментарии о восстановлении БД и распаковке там для проверки, не следует предполагать, что размер несжатой таблицы == размер указанных данных в памяти на сервере приложений:

  1. Есть все строки в таблице в кэше? Или просто в пределах диапазона? Здесь предполагается, что это все, и это может быть правильно, но я полагал, что по крайней мере следует упомянуть, что это может быть не так (если в документации не указано иное, но в любом случае это второстепенный вопрос, просто не хотелось это не говоря уже).

    Вопрос был обновлен до состояния: да, все строки кэшируются.

  2. Структура накладных

    1. На стороне БД:
      страница и накладные расходы на стороне БД: сколько строк помещается на странице, определяется многими факторами, которые могут отбросить оценки. Даже при значении FILLFACTOR100 (или 0) на странице все еще остается неиспользованное пространство, так как его недостаточно для всей строки. И это в дополнение к заголовку страницы. Кроме того, если активирована какая-либо функция изоляции моментальных снимков, я полагаю, что будет дополнительно 13 байт на строку, занятую номером версии, и это приведет к сбою оценок. Существуют и другие мелочи, связанные с фактическим размером строки (растровое изображение NULL, столбцы переменной длины и т. Д.), Но упомянутые до сих пор элементы сами по себе должны иметь значение.
    2. На стороне сервера приложений:
      какой тип коллекции используется для хранения кэшированных результатов? Я предполагаю, что это приложение .NET, так это DataTable? Общий список? SortedDictionary? Каждый тип коллекции имеет различное количество подслушанных. Я не ожидал бы, что какой-либо из вариантов обязательно отразит накладные расходы Page и Row на стороне БД, особенно в масштабе (я уверен, что небольшое количество строк может не иметь достаточно различных значений, но вы не ищете различий в сотнях байтов или просто несколько кБ).
  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, но я не рекомендую этого, поскольку это может нарушать лицензионное соглашение в зависимости от местных законов).

Соломон Руцкий
источник
В итоге мы реализовали проверки в коде, чтобы быть уверенными в размере буфера, который представлен приложению.
Том 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