LOB_DATA, медленное сканирование таблицы и некоторые вопросы ввода / вывода

19

У меня есть довольно большая таблица с одним из столбцов, представляющих собой данные XML, со средним размером записи XML ~ 15 килобайт. Все остальные столбцы - это обычные числа, большие буквы, идентификаторы GUID и т. Д. Чтобы получить конкретные числа, скажем, таблица имеет миллион строк и размер ~ 15 ГБ.

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

SELECT TOP 1000 * FROM TABLE

считывание данных с диска занимает около 20-25 секунд, хотя я не определяю порядок результатов. Я запускаю запрос с холодным кешем (т.е. после DBCC DROPCLEANBUFFERS). Вот результаты статистики IO:

Сканирование 1, логическое чтение 364, физическое чтение 24, чтение с опережением 7191, логическое чтение lob 7924, физическое чтение за 1690, чтение с опережением чтение 3968.

Захватывает ~ 15 МБ данных. План выполнения показывает Clustered Index Scan, как я ожидал.

На диске нет ввода-вывода, кроме моих запросов; Я также проверил, что фрагментация кластерного индекса близка к 0%. Это SATA-накопитель потребительского уровня, однако я все же думаю, что SQL Server сможет сканировать таблицу быстрее, чем ~ 100-150 МБ / мин.

Наличие поля XML приводит к тому, что большая часть данных таблицы располагается на страницах LOB_DATA (на самом деле ~ 90% страниц таблицы составляют LOB_DATA).

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

Еще более широко - разумно ли иметь такую ​​структуру таблицы / шаблон данных? В рекомендациях по использованию Filestream обычно указываются гораздо большие размеры полей, поэтому я не хочу идти по этому пути. Я действительно не нашел никакой хорошей информации об этом конкретном сценарии.

Я думал о сжатии XML, но это должно быть сделано на клиенте или с SQLCLR и потребовало бы довольно много работы для реализации в системе.

Я попробовал сжатие, и поскольку XML-файлы сильно избыточны, я могу (в приложении ac #) сжать XML с 20 КБ до ~ 2,5 КБ и сохранить его в столбце VARBINARY, предотвращая использование страниц данных больших объектов. Это ускоряет SELECT в 20 раз в моих тестах.

Александр Шелемин
источник
Алекс: не уверен, что вы видели обсуждение, связанное с моим ответом (ссылка в комментарии ниже моего ответа), но я смог приблизиться к воспроизведению вашего сценария. Я заполнил таблицу, соответствующую (насколько у меня была информация) вашему описанию, и получил статистику ввода / вывода, которая очень похожа. За исключением того, что «Физические чтения LOB» никогда не были даже близко. Поэтому мне было интересно, обновили ли вы XML (но не другие столбцы) и / или имели ли вы физическую фрагментацию ваших файлов данных. Я все еще не возражаю против получения DDL вашей таблицы и настроек автоматического роста для каждого файла данных, а вы уменьшаете свои файлы данных?
Соломон Руцки
Прежде всего - большое спасибо за подробный ответ, я не смог принять участие в обсуждении из-за нехватки времени. Теперь, когда вы упомянули это (я не подумал об этом, когда задавали вопрос) - поле XML обновляется несколько раз после его создания, и оно создается маленьким. Поэтому я подозреваю, что изначально он хранится в строке, а после некоторых обновлений он перемещается в структуру LOB-страницы, а затем получает еще несколько обновлений.
Александр Шелемин
(Продолжение) Я проверил физическую фрагментацию файлов перед тем, как задавать вопрос, и встроенный инструмент Windows решил, что все в порядке, поэтому я не стал вдаваться в подробности. Авто-рост по умолчанию, на 1 МБ я считаю, и файлы данных не были сжаты.
Александр Шелемин
Выбор топ 1000 * имеет значение в моем конкретном случае. Я, конечно, понимаю, что это считается плохой практикой, однако некоторые решения по разработке приложений действительно трудно изменить после того, как они были приняты в течение длительного времени. Select * в основном используется в качестве стратегии репликации базы данных между различными компонентами в нашем приложении. В этом есть свои плюсы, например, мы можем сделать много произвольных манипуляций с данными / схемой на лету, что было бы сложно при использовании встроенных методов репликации, но это сопряжено с проблемами.
Александр Шелемин
Алекс, SELECT *это не проблема, если вам нужны данные XML. Это проблема только в том случае, если вам не нужны данные XML. В таком случае зачем замедлять запрос, чтобы получить данные, которые вы не используете? Я спросил об обновлениях в XML, задаваясь вопросом, не сообщалось ли точно о фрагментации на страницах больших объектов. Вот почему я спросил в своем ответе, как именно вы определили, что кластерный индекс не был фрагментирован? Можете ли вы предоставить команду, которую вы выполнили? И вы сделали полный REBUILD по кластерному индексу? (продолжение)
Соломон Руцкий

Ответы:

11

Наличие поля XML приводит к тому, что большая часть данных таблицы располагается на страницах LOB_DATA (на самом деле ~ 90% страниц таблицы составляют LOB_DATA).

Простое наличие столбца XML в таблице не имеет такого эффекта. Это наличие XML - данных , которые, при определенных условиях , вызывает некоторую часть данных подряд, чтобы хранить от строки, на страницах LOB_DATA. И хотя один (или, может быть, несколько ;-) может утверждать, что да, в XMLстолбце подразумевается, что действительно будут данные XML, не гарантируется, что данные XML нужно будет хранить вне строки: если строка в значительной степени уже не заполнена вне каких-либо данных XML небольшие документы (до 8000 байт) могут помещаться в строку и никогда не переходить на страницу LOB_DATA.

Правильно ли я считаю, что страницы LOB_DATA могут вызывать медленное сканирование не только из-за их размера, но и потому, что SQL Server не может эффективно сканировать кластеризованный индекс, когда в таблице много страниц LOB_DATA?

Сканирование относится к просмотру всех строк. Конечно, когда читается страница данных, считываются все данные в строке , даже если вы выбрали подмножество столбцов. Разница с данными больших объектов заключается в том, что если вы не выберете этот столбец, данные вне строки не будут прочитаны. Следовательно, не совсем справедливо делать вывод о том, насколько эффективно SQL Server может сканировать этот кластеризованный индекс, поскольку вы точно не тестировали его (или тестировали половину его). Вы выбрали все столбцы, включая столбец XML, и, как вы упомянули, именно там находится большая часть данных.

Итак, мы уже знаем, что SELECT TOP 1000 *тест не просто читал серию 8k страниц данных, все подряд, а вместо этого переходил в другие места в каждой строке . Точная структура этих больших данных может варьироваться в зависимости от их размера. Основываясь на исследованиях, показанных здесь ( каков размер указателя большого объекта для (MAX) типов, таких как Varchar, Varbinary, Etc? ), Существует два типа размещения больших объектов вне строки:

  1. Встроенный корень - для данных размером от 8001 до 40000 (на самом деле 42000) байтов, если позволяет пространство, в строке будет находиться от 1 до 5 указателей (24–72 байта), которые указывают непосредственно на страницы больших объектов.
  2. TEXT_TREE - для данных размером более 42 000 байт или если указатели от 1 до 5 не могут поместиться в строке, тогда будет только 24-байтовый указатель на начальную страницу списка указателей на страницы больших объектов (т.е. text_tree ").

Одна из этих двух ситуаций возникает каждый раз, когда вы извлекаете данные больших объектов размером более 8000 байт или просто не помещаются в строку. Я разместил тестовый скрипт на PasteBin.com (скрипт T-SQL для проверки выделения и чтения больших объектов ), который показывает 3 типа выделения больших объектов (в зависимости от размера данных), а также влияние каждого из них на логические и физическое чтение. В вашем случае, если данные XML на самом деле меньше 42 000 байт на строку, то ни один из них (или их очень мало) не должен быть в наименее эффективной структуре TEXT_TREE.

Если вы хотите проверить, насколько быстро SQL Server может сканировать этот кластеризованный индекс, сделайте, SELECT TOP 1000но укажите один или несколько столбцов, не включая этот столбец XML. Как это влияет на ваши результаты? Это должно быть немного быстрее.

Разумно ли иметь такую ​​структуру таблицы / шаблон данных?

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

Я могу (в приложении ac #) сжать XML с 20 КБ до ~ 2,5 КБ и сохранить его в столбце VARBINARY, предотвращая использование страниц данных больших объектов. Это ускоряет SELECT в 20 раз в моих тестах.

Это ускорило выбор всех столбцов или даже только данных XML (теперь они есть VARBINARY), но на самом деле это повреждает запросы, которые не выбирают данные «XML». Предполагая, что у вас есть около 50 байтов в других столбцах и значение FILLFACTOR100, тогда:

  • Сжатие XMLне требуется: для 15 КБ данных требуется 2 страницы LOB_DATA, для чего требуется 2 указателя для встроенного корня. Первый указатель имеет длину 24 байта, а второй - 12, что составляет 36 байтов, хранимых в строке для данных XML. Общий размер строки составляет 86 байт, и вы можете разместить около 93 из этих строк на странице данных размером 8060 байт. Следовательно, 1 миллион строк требует 10 753 страниц данных.

  • Пользовательское сжатие: 2,5 тыс. VARBINARYДанных помещается в строку. Общий размер строки составляет 2610 (2,5 * 1024 = 2560) байт, и вы можете разместить только 3 из этих строк на странице данных размером 8060 байт. Следовательно, 1 миллион строк требует 333,334 страниц данных.

Следовательно, реализация пользовательского сжатия приводит к увеличению в 30 раз количества страниц данных для кластерного индекса. Значение, все запросы с использованием индекса кластерного сканирования теперь около 322,500 больше страниц данных для чтения. Пожалуйста, ознакомьтесь с подробным разделом ниже, чтобы узнать о дополнительных последствиях такого типа сжатия.

Я бы предостерег от проведения любого рефакторинга, основанного на производительности SELECT TOP 1000 *. Это вряд ли будет запрос, который приложение даже выдаст, и его не следует использовать в качестве единственной основы для потенциально ненужной оптимизации.

Для получения более подробной информации и дополнительных тестов см. Раздел ниже.


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

Что мы знаем:

  1. Таблица содержит около 1 миллиона строк
  2. Размер таблицы составляет около 15 ГБ
  3. Таблица содержит одну XMLколонку и несколько других столбцов типов: INT, BIGINT, UNIQUEIDENTIFIER, « и т.д.»
  4. XML«размер» столбца составляет в среднем около 15 тыс.
  5. После запуска DBCC DROPCLEANBUFFERSдля выполнения следующего запроса требуется 20-25 секунд:SELECT TOP 1000 * FROM TABLE
  6. Кластерный индекс сканируется
  7. Фрагментация кластерного индекса близка к 0%

Что мы думаем, мы знаем:

  1. Никакой другой дисковой активности вне этих запросов. Вы уверены? Даже если нет других пользовательских запросов, выполняются ли фоновые операции? Существуют ли процессы, внешние по отношению к SQL Server, работающие на том же компьютере, которые могут занимать некоторые операции ввода-вывода? Может и не быть, но это не ясно, основываясь исключительно на предоставленной информации.
  2. Возвращается 15 МБ данных XML. На чем основано это число? Оценка, полученная из 1000 строк, умноженных на среднее значение 15 КБ данных XML на строку? Или программная агрегация того, что было получено для этого запроса? Если это просто оценка, я бы не стал полагаться на нее, поскольку распределение данных XML может быть даже не таким, как подразумевается простым средним значением.
  3. Сжатие XML может помочь. Как именно вы будете делать сжатие в .NET? Через классы GZipStream или DeflateStream ? Это не вариант с нулевой стоимостью. Это, безусловно, будет сжимать некоторые данные на большой процент, но это также потребует больше ЦП, поскольку вам потребуется дополнительный процесс для сжатия / распаковки данных каждый раз. Этот план также полностью лишит вас возможности:

    • запрос данных XML с помощью тех .nodes, .value, .queryи .modifyфункции XML.
    • индексировать данные XML.

      Помните (поскольку вы упомянули, что XML «сильно избыточен»), XMLтип данных уже оптимизирован, поскольку он сохраняет имена элементов и атрибутов в словаре, присваивая целочисленный идентификатор индекса каждому элементу, а затем использует этот целочисленный идентификатор. во всем документе (следовательно, он не повторяет полное имя для каждого использования и не повторяет его снова как закрывающий тег для элементов). В реальных данных также удалены посторонние пробелы. Вот почему извлеченные XML-документы не сохраняют свою первоначальную структуру и почему извлекаются пустые элементы, <element />даже если они были введены как<element></element>, Таким образом, любой выигрыш от сжатия с помощью GZip (или чего-либо еще) будет найден только путем сжатия значений элемента и / или атрибута, который представляет собой гораздо меньшую площадь поверхности, которую можно улучшить, чем многие ожидают, и, скорее всего, не стоит потери Возможности, как указано выше.

      Также имейте в виду, что сжатие данных XML и сохранение VARBINARY(MAX)результата не исключит доступ к LOB, а только уменьшит его. В зависимости от размера оставшихся данных в строке сжатое значение может помещаться в строку или для него все еще могут потребоваться страницы больших объектов.

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

Что мы не знаем, но нужно:

  1. Почему производительность имеет SELECT *значение? Это шаблон, который вы используете в коде. Если так, то почему?
  2. Какова производительность выбора только столбца XML? Какова статистика и сроки, если вы просто SELECT TOP 1000 XmlColumn FROM TABLE;:?
  3. Сколько из 20-25 секунд, необходимых для возврата этих 1000 строк, связано с сетевыми факторами (получение данных по сети), а также с клиентскими факторами (что составляет примерно 15 МБ плюс остальные XML-данные в сетку в SSMS или, возможно, сохранение на диск)?

    Выделяя эти два аспекта операции, иногда можно просто не возвращать данные. Теперь можно подумать, что нужно выбрать временную таблицу или переменную таблицы, но это будет просто ввести несколько новых переменных (например, дисковый ввод-вывод для tempdb, запись журнала транзакций, возможный автоматический рост данных tempdb и / или файла журнала, необходимость пространство в буферном пуле и т. д.). Все эти новые факторы могут фактически увеличить время запроса. Вместо этого я обычно сохраняю столбцы в переменные (соответствующего типа данных; нет SQL_VARIANT), которые перезаписываются каждой новой строкой (т.е. SELECT @Column1 = tab.Column1,...).

    ОДНАКО , как было отмечено @PaulWhite в этом DBA.StackExchange Q & A, логическое чтение отличается при доступе к одним и тем же данным большого объекта, с дополнительным исследованием моего собственного, опубликованного на PasteBin ( сценарий T-SQL для проверки различных сценариев для чтения большого объекта ) , LOBs не обращались последовательно между SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), и SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Таким образом, наши возможности здесь немного более ограничены, но вот что можно сделать:

    1. Исключите проблемы с сетью, выполнив запрос на сервере с SQL Server, либо в SSMS, либо в SQLCMD.EXE.
    2. Исключите проблемы клиента в SSMS, перейдя в Параметры запроса -> Результаты -> Сетка и отметив опцию «Отменить результаты после выполнения». Обратите внимание, что этот параметр предотвратит вывод ВСЕХ, включая сообщения, но все же может быть полезным, чтобы исключить время, которое требуется SSMS для выделения памяти для каждой строки, а затем нарисовать ее в сетке.
      Кроме того , можно выполнить запрос через Sqlcmd.exe и направить выход идти в никуда через: -o NUL:.
  4. Есть ли тип ожидания, связанный с этим запросом? Если да, что это за тип ожидания?
  5. Каков фактический размер данных для возвращаемыхXML столбцов ? Средний размер этого столбца по всей таблице на самом деле не имеет значения, если строки «TOP 1000» содержат непропорционально большую часть общих данных. Если вы хотите узнать о ТОП 1000 строк, посмотрите на эти строки. Пожалуйста, запустите следующее:XML

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
  6. Точная схема таблицы. Пожалуйста, предоставьте полный CREATE TABLE отчет, включая все индексы.
  7. План запроса? Это то, что вы можете опубликовать? Эта информация, вероятно, ничего не изменит, но лучше знать, что это не так, чем догадываться, что это не так и будет неправильно ;-)
  8. Есть ли физическая / внешняя фрагментация в файле данных? Хотя это может и не быть существенным фактором, поскольку вы используете «потребительские SATA», а не SSD или даже супердорогие SATA, эффект неоптимально упорядоченных секторов будет более заметным, особенно с учетом количества этих секторов. это должно быть прочитано увеличивается.
  9. Каковы точные результаты следующего запроса:

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');

ОБНОВИТЬ

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

Я обнаружил, что выполнение SELECT TOP 1000 * FROM TABLEзавершается за 8 секунд в первый раз, а затем через 2–4 секунды каждый раз (да, выполняется DBCC DROPCLEANBUFFERSперед каждым запуском SELECT *запроса). Мой ноутбук, которому несколько лет, не быстр: SQL Server 2012 SP2 Developer Edition, 64-битная, 6 ГБ оперативной памяти, двухъядерный 2,5 ГГц Core i5 и диск SATA 5400 об / мин. Я также использую SSMS 2014, SQL Server Express 2014, Chrome и некоторые другие.

Основываясь на времени отклика моей системы, я повторю, что нам нужно больше информации (т.е. подробностей о таблице и данных, результатах предлагаемых тестов и т. Д.), Чтобы помочь сузить причину времени отклика в 20-25 секунд. что ты видишь.

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

И, поскольку мы хотим учесть время, затрачиваемое на чтение страниц без LOB, я выполнил следующий запрос, чтобы выбрать все, кроме столбца XML (один из тестов, которые я предложил выше). Это возвращается через 1,5 секунды довольно последовательно.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

Заключение (на данный момент)
Исходя из моей попытки воссоздать ваш сценарий, я не думаю, что мы можем указать ни на диск SATA, ни на непоследовательный ввод / вывод в качестве основной причины 20 - 25 секунд, особенно потому, что мы все еще не знаю, как быстро возвращается запрос, не включая столбец XML. И я не смог воспроизвести большое количество логических операций чтения (не больших объектов), которые вы показываете, но у меня есть ощущение, что мне нужно добавить больше данных в каждую строку в свете этого и утверждения:

~ 90% страниц таблицы составляют LOB_DATA

Моя таблица содержит 1 миллион строк, каждая из которых содержит более 15 000 XML-данных, и sys.dm_db_index_physical_statsпоказывает, что существует 2 миллиона страниц LOB_DATA. Тогда оставшиеся 10% будут 222 тыс. Страниц данных IN_ROW, но у меня их всего 11 630. Итак, еще раз, нам нужно больше информации относительно фактической схемы таблицы и фактических данных.

Соломон Руцкий
источник
Это обсуждение было перенесено в чат .
Пол Уайт говорит GoFundMonica
10

Правильно ли я считаю, что страницы LOB_DATA могут вызывать медленное сканирование не только из-за их размера, но и потому, что SQL Server не может эффективно сканировать кластерный индекс

Да, чтение данных LOB, не сохраненных в строке, приводит к случайному вводу-выводу вместо последовательного ввода-вывода. Метрика производительности диска, используемая здесь, чтобы понять, почему она быстрая или медленная, это IOPS с произвольным чтением.

Данные больших объектов хранятся в древовидной структуре, где страница данных в кластерном индексе указывает на страницу данных больших объектов с корневой структурой больших объектов, которая, в свою очередь, указывает на фактические данные больших объектов. При обходе корневых узлов в кластерном индексе SQL Server может получать данные в строке только при последовательном чтении. Чтобы получить данные больших объектов, SQL Server должен пойти куда-нибудь на диск.

Я полагаю, что если вы перейдете на SSD-диск, вы не сильно пострадаете от этого, поскольку случайный IOPS для SSD намного выше, чем для вращающегося диска.

Разумно ли иметь такую ​​структуру таблицы / шаблон данных?

Да, это может быть. Зависит от того, что этот стол делает для вас.

Обычно проблемы с производительностью XML в SQL Server возникают, когда вы хотите использовать T-SQL для запросов в XML, и даже больше, когда вы хотите использовать значения из XML в предикате в предложении where или объединении. Если это так, вы можете вместо этого взглянуть на продвижение свойств или выборочные индексы XML или перепроектировать структуры таблиц, вместо этого разбивая XML на таблицы.

Я пробовал компрессию

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

Микаэль Эрикссон
источник
Большое спасибо за ответ. Относительно сжатия: я не уверен, оправдана ли такая строгая антирекоменация, поскольку необходимость фактически запрашивать эти данные из T-SQL, очевидно, зависит от природы хранимых данных. В моем случае я решил пойти со сжатием на данный момент.
Александр Шелемин