LOB_DATA, повільне сканування таблиці та деякі питання вводу / виводу


19

У мене досить велика таблиця, в одному з стовпців яких є дані XML, середній розмір запису XML становить ~ 15 кілобайт. Усі інші стовпці - це звичайні вставки, біґінти, GUID тощо. Щоб мати конкретні цифри, скажімо, таблиця має мільйон рядків і розміром становить ~ 15 ГБ.

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

SELECT TOP 1000 * FROM TABLE

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

Підрахунок сканування 1, логічне зчитування 364, фізичне зчитування 24, зчитування вперед зчитування 7191, лобічне зчитування 7924, лобічне фізичне зчитування 1690, лоб зчитування вперед зчитування 3968.

Він захоплює ~ 15 Мб даних. План виконання показує кластерне сканування індексів, як я очікував.

На диску нічого, крім моїх запитів, не відбувається; Я також перевірив, що фрагментація індексів кластера близька до 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, запобігаючи використанню сторінок даних LOB. Це випробовує SELECTs 20 разів у моїх тестах.


Олексій: не впевнений, чи бачив ви дискусію, пов’язану з моєю відповіддю (посилання знаходиться в коментарі нижче моєї відповіді), але я зміг наблизитися до відтворення вашого сценарію. Я заповнив таблицю, що відповідає (стільки, скільки я мав інформації) для вашого опису, і отримав статистику вводу / виводу, дуже схожу. За винятком того, що "Фізичні читання LOB" ніколи навіть не були близькими. Тож мені було цікаво, чи ви оновили XML (але не інші стовпці) та / або маєте багато фізичної фрагментації ваших файлів даних. Я все ще не заперечував би отримати DDL таблиці та налаштування автоматичного зростання для кожного файлу даних, а чи зменшуєте ви файли даних?
Соломон Руцький

Перш за все - велике спасибі за детальну відповідь, я не зміг брати участь у обговоренні в той час через брак часу. Тепер, коли ви згадали про це (я не думав про це, коли задавали питання) - поле XML оновлюється кілька разів після його створення, і воно створюється малим. Тому я б підозрював, що спочатку він зберігається в рядку, а після деяких оновлень він переміщується в структуру сторінки LOB, а потім отримує ще кілька оновлень.
Олександр Шелемін

(Продовження) Я перевірив фізичну фрагментацію файлів, перш ніж задавати питання, і вбудований інструмент Windows вважав, що це нормально, тому більше не переглядав це. Автозростання за замовчуванням - на 1 МБ, я вважаю, а файли даних не були зменшені.
Олександр Шелемін

Вибір топ-1000 * має важливе значення в моєму конкретному випадку. Я, звичайно, розумію, що це вважається поганою практикою, проте деякі рішення щодо дизайну додатків насправді важко змінити після їх тривалого часу. Select * в основному використовується як стратегія реплікації між базами даних між різними компонентами нашого додатку. Для цього є плюси, наприклад, ми можемо зробити багато довільних маніпуляцій з даними / схемами на льоту, що було б важко із вбудованими методами реплікації, але це пов'язано зі своїми проблемами.
Олександр Шелемін

Алекс, SELECT *це не проблема, якщо вам потрібні дані XML. Проблема полягає лише в тому випадку, якщо ви не хочете даних XML, і в цьому випадку навіщо сповільнювати запит, щоб отримати назад дані, які ви не використовуєте? Я запитав про оновлення XML, цікаво, чи не було повідомлено про фрагментацію на сторінках LOB. Тому я у своїй відповіді запитав, як саме ви визначили, що кластерний індекс не фрагментований? Чи можете ви надати команду, яку ви виконували? А ви зробили повний ПІДГОТОВКА на кластерному індексі? (продовження)
Соломон Руцький

Відповіді:


11

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

Просто наявність стовпця XML у таблиці не має цього ефекту. Це наявність XML - даних , які, за певних умов , викликає деяку частину даних поспіль, щоб зберігати від рядка, на сторінках LOB_DATA. І хоча один (або, можливо, кілька ;-) може стверджувати, що так, XMLстовпець має на увазі, що дійсно будуть дані XML, не гарантується, що дані XML потрібно буде зберігати поза рядком: якщо тільки рядок вже майже заповнений за винятком того, що вони містять будь-які XML-дані, невеликі документи (до 8000 байт) можуть вміщуватися в рядку і ніколи не переходити на сторінку LOB_DATA.

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

Сканування стосується перегляду всіх рядів. Звичайно, коли читається сторінка даних, всі рядкові дані читаються, навіть якщо ви вибрали підмножину стовпців. Відмінність даних LOB полягає в тому, що якщо ви не виберете цей стовпець, дані, що не входять у рядок, не будуть зчитуватися. Отже, не дуже справедливо робити висновок про те, наскільки ефективно SQL Server може сканувати цей індекс кластеру, оскільки ви точно не тестували це (або ви тестували половину його). Ви вибрали всі стовпці, до яких належить стовпець XML, і як ви вже згадували, саме там розташована більшість даних.

Тож ми вже знаємо, що SELECT TOP 1000 *тест був не просто читанням серії 8k даних, підряд, а замість цього, перескакуванням в інші місця в кожному рядку . Точна структура даних LOB може змінюватись залежно від того, наскільки вони великі. На основі досліджень, показаних тут ( Який розмір покажчика LOB для (MAX) типів, таких як Varchar, Varbinary, Etc? ), Існує два типи позарядних виділень LOB:

  1. Вбудований корінь - для даних між 8001 і 40 000 (дійсно 42 000) байтів, дозволяючи простір, буде від 1 до 5 покажчиків (24 - 72 байти) В РЯДКІ, що вказують безпосередньо на сторінку LOB.
  2. TEXT_TREE - для даних, що перевищують 42 000 байт, або якщо від 1 до 5 покажчиків не може вміститися в рядку, буде лише 24-байтовий покажчик на початкову сторінку списку покажчиків на сторінки LOB (тобто " text_tree "сторінка).

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

Якщо ви хотіли перевірити, наскільки швидко SQL Server може сканувати цей кластерний індекс, зробіть, SELECT TOP 1000але вкажіть один або кілька стовпців, не включаючи цей стовпець XML. Як це впливає на ваші результати? Це повинно бути трохи швидше.

чи вважається розумним мати таку структуру таблиці / шаблон даних?

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

Я можу (у додатку AC #) стискати XML від 20 КБ до ~ 2,5 КБ і зберігати його у стовпці VARBINARY, запобігаючи використанню сторінок даних LOB. Це випробовує SELECTs 20 разів у моїх тестах.

Це зробило VARBINARYшвидше вибір усіх стовпців або навіть лише XML-даних (зараз це ), але це насправді шкодить запитам, які не вибирають дані "XML". Якщо припустити, що в інших стовпцях є близько 50 байт і має FILLFACTOR100, то:

  • Без стиснення: на XML15 тис. Даних потрібно вимагати 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, він просто зменшить його. Залежно від розміру решти даних у рядку, стиснене значення може відповідати рядку, або воно все ще потребує сторінок 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, Логіка читає різні під час доступу до тих же даних LOB , з додатковими дослідженнями мого власного розміщення на PasteBin ( сценарій T-SQL для тестування різних сценаріїв для читання LOB ) , 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 стовпців ? Середній розмір стовпця по всій таблиці насправді не має значення, якщо рядки "ТОП 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 та 5400 об / хв SATA-накопичувач. Я також запускаю 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 І я не зміг відтворити велику кількість логічних читань (non-LOB), які ви показуєте, але у мене є відчуття, що мені потрібно додати більше даних у кожен рядок у світлі цього та заяви:

~ 90% сторінок таблиці - LOB_DATA

Моя таблиця має 1 мільйон рядків, кожен з яких має трохи більше sys.dm_db_index_physical_stats15 тис. XML-даних, і показує, що є 2 мільйони сторінок LOB_DATA. Тоді решта 10% становитимуть 222 тис. IN_ROW сторінок даних, але я маю лише 11 630. Тому ще раз нам потрібна додаткова інформація щодо фактичної схеми таблиці та фактичних даних.



10

чи правильно я вважаю, що сторінки LOB_DATA можуть спричинити повільне сканування не тільки через їх розмір, але й тому, що SQL Server не може ефективно сканувати кластерний індекс

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

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

Я думаю, що якби ви перейшли на SSD-диск, ви цього не постраждаєте, оскільки випадковий IOPS для SSD набагато вищий, ніж для спінінг-диска.

чи вважається розумним мати таку структуру таблиці / шаблон даних?

Так, це могло бути. Залежить від того, що ця таблиця робить для вас.

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

Я спробував компресію

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


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