Продуктивність таблиці пам'яті гірше, ніж на основі диска


10

У мене в SQL Server 2014 є таблиця, яка виглядає наступним чином:

CREATE TABLE dbo.MyTable
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
)

при цьому (id1, id2) є ПК. В основному, id1 - це ідентифікатор для групування набору результатів (id2, col1, col2), pk - id2.

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

  • Дані в таблиці записуються -> читати -> видаляються один раз.
  • Кожне значення id1 має кілька (десятки / сотні) тисяч id2.
  • Дані зберігаються в таблиці протягом дуже короткого часу, наприклад, 20 секунд.

Запитання, виконані в цій таблиці, такі:

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

-- READ:
SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

-- DELETE:
DELETE FROM MyTable WHERE id1 = @value

Ось поточне визначення, яке я використав для таблиці:

CREATE TABLE dbo.SearchItems
(
  [id1] [bigint] NOT NULL,
  [id2] [bigint] NOT NULL,
  [col1] [int] NOT NULL default(0),
  [col2] [int] NOT NULL default(0)

  CONSTRAINT PK_Mem PRIMARY KEY NONCLUSTERED (id1,id2),
  INDEX idx_Mem HASH (id1,id2) WITH (BUCKET_COUNT = 131072)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)

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

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

Запитання:

  • який правильний BUCKET_COUNT встановити?
  • який індекс я повинен використовувати?
  • чому продуктивність гірша, ніж у таблиці на основі диска?

Звертається запит sys.dm_db_xtp_hash_index_stats :

total_bucket_count = 131072
empty_bucket_count = 0
avg_chain_len = 873
max_chain_length = 1009

Я змінив кількість відра, щоб вихід з sys.dm_db_xtp_hash_index_stats був:

total_bucket_count = 134217728
empty_bucket_count = 131664087
avg_chain_len = 1
max_chain_length = 3

І все-таки результати майже однакові, якщо не гірші.


Ви впевнені, що не стикаєтеся з нюханням параметрів? Ви спробували запустити запити OPTION(OPTIMIZE FOR UNKNOWN)(див. Підказки таблиці )?
ТТ.

Напевно, ви стикаєтеся з проблемами ланцюжка рядків. Чи можете ви дати нам вихід select * from sys.dm_db_xtp_hash_index_stats ? Також це посилання повинно відповісти на більшість / всі ваші запитання: msdn.microsoft.com/en-us/library/…
Шон Галларді

4
Хеш-індекс корисний лише для предикатів обох включених стовпців. Ви пробували без хеш-індексу на столі?
Мікаель Ерікссон

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

@DanielHutmacher FWIW Я бачив зустрічні приклади, коли вся користь полягала в тому, щоб зняти засувку та додавати до власне складених процедур дали нульове або незначне поліпшення. Я не думаю, що немає місця для твердої заяви (хоча ви, мабуть, маєте рацію в цьому випадку, я навіть не переглядав деталі).
Аарон Бертран

Відповіді:


7

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

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

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

Це викликає занепокоєння, оскільки це точно не повинно бути. Певні робочі навантаження не передбачені в таблицях пам'яті (SQL 2014), і деякі робочі навантаження піддаються цьому. У більшості ситуацій може бути мінімальний приріст продуктивності лише шляхом міграції та вибору належних індексів.

Спочатку я дуже вузько думав над вашими питаннями щодо цього:

Запитання:

  • який правильний BUCKET_COUNT встановити?
  • який індекс я повинен використовувати?
  • чому продуктивність гірша, ніж у таблиці на основі диска?

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

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

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

Коли я зробив швидкий тест на вкладку 100000 рядків з таблиці на основі диска після завантаження даних у пам'ять - це було час другого відгуку. Однак більшість ваших даних зберігаються лише протягом дуже короткого часу, менше 20 секунд. Це не дає йому багато часу, щоб реально жити в кеші. Окрім того, я не впевнений, наскільки AnotherTableнасправді великий , і не знаю, чи відчиняються цінні з диска чи ні. Ми повинні розраховувати на вас за ці відповіді.

За допомогою запиту Вибір:

SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

Знову ж таки, ми перебуваємо на милі ефективність таблиці на основі interop + диска. Крім того, сортування не є дешевим для індексів HASH, і слід використовувати некластеризований індекс. Про це йдеться у посібнику з індексів, який я пов'язав у коментарях.

Щоб навести фактичні факти, засновані на дослідженнях, я завантажив SearchItemsтаблицю пам'яті з 10 мільйонами рядків і AnotherTableзі 100 000, оскільки не знав фактичного розміру чи статистики. Потім я використовував вищезазначений запит для виконання. Крім того, я створив розширений сеанс подій на wait_completed і помістив його в кільце. Її прибирали після кожного пробігу. Я також побіг DBCC DROPCLEANBUFFERSімітувати обстановку, де всі дані можуть не мати пам'яті.

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

Результати надійшли без інформації про очікування після 5 запусків запиту лише в таблиці на основі пам'яті (видалення об'єднання та відсутність підзапитів). Це майже як і очікувалося.

Однак, використовуючи оригінальний запит, у мене були очікування. У цьому випадку сенс, коли дані зчитуються з диска, має сенс PAGEIOLATCH_SH. Оскільки я є єдиним користувачем у цій системі і не витрачав час на створення масивного тестового середовища для вставок, оновлень, видалень із об'єднаної таблиці, я не очікував, що будь-яке блокування чи блокування набуде чинності.

У цьому випадку ще раз значна частина часу була витрачена на таблицю на основі диска.

Нарешті, запит на видалення. Пошук рядків на основі просто ID1 не є надзвичайно ефективним, якщо має індекс. Хоча це правда, що предикати рівності - це те, для чого хеш-індекси належать, відро, в яке потрапляють дані, базується на всіх хешованих стовпцях. Таким чином, id1, id2, де id1 = 1, id2 = 2, і id1 = 1, id2 = 3 буде хеш в різні відра, оскільки хеш буде поперек (1,2) і (1,3). Це не буде простим скануванням діапазону B-Tree, оскільки хеш-індекси не структуровані однаково. Тоді я б очікував, що це не буде ідеальним показником для цієї операції, однак я б не очікував, що він прийме замовлення на величину довше, ніж досвід. Мені було б цікаво подивитися на цю інформацію на wait_info.

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

Хоча це правда, що замки використовуються для логічної послідовності, операції все одно повинні бути атомними. Це робиться за допомогою спеціального оператора порівняння на основі процесора (саме тому In-Memory працює лише з певними [хоча й майже усіма процесорами, зробленими за останні 4 роки] процесорами). Таким чином, ми не отримуємо все безкоштовно, ще буде якийсь час для завершення цих операцій.

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

Слідувати:

  1. Створіть розширений сеанс події для wait_completed та вкажіть відомий вам SPID. Запустіть запит і дайте нам висновок або споживайте його внутрішньо.

  2. Повідомте нам про вихід з №1.

  3. Немає магічного числа для визначення кількості відра для хеш-індексів. В основному до тих пір, поки відра не будуть повністю заповнені і ланцюги рядків залишаються нижче 3 або 4, продуктивність повинна залишатися прийнятною. Це на зразок запитання: "На що мені слід встановити файл журналу?" - це залежатиме від процесу, від бази даних, від типу використання.

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