Величезна різниця в продуктивності при використанні групи за відмінністю


81

Я виконую деякі тести на HSQLDBсервері з таблицею, що містить 500 000 записів. У таблиці немає індексів. Є 5000 різних ділових ключів. Мені потрібен їхній список. Звичайно, я почав із DISTINCTзапиту:

SELECT DISTINCT business_key FROM memory WHERE
   concept <> 'case' or 
   attrib <> 'status' or 
   value <> 'closed'

Це займає близько 90 секунд !!!

Потім я спробував використовувати GROUP BY:

SELECT business_key FROM memory WHERE
       concept <> 'case' or 
       attrib <> 'status' or 
       value <> 'closed'
GROUP BY business_key

І це займає 1 секунду !!!

Намагаючись з'ясувати різницю, я натрапив, EXLAIN PLAN FORале, схоже, даю однакову інформацію для обох запитів.

EXLAIN PLAN FOR DISTINCT ...

isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

EXLAIN PLAN FOR SELECT ... GROUP BY ...

isDistinctSelect=[false]
isGrouped=[true]
isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
groupColumns=[
COLUMN: PUBLIC.MEMORY.BUSINESS_KEY]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

EDIT : Я зробив додаткові тести. Завдяки 500 000 записів HSQLDBз усіма чіткими діловими клавішами, продуктивність DISTINCTтепер краща - 3 секунди, GROUP BYщо зайняло близько 9 секунд.

В MySQLобох запитах заготовте однаково:

MySQL: 500 000 рядків - 5000 різних бізнес-ключів: Обидва запити: 0,5 секунди MySQL: 500 000 рядків - усі різні бізнес-ключі: SELECT DISTINCT ...- 11 секунд SELECT ... GROUP BY business_key- 13 секунд

Тож проблема пов’язана лише з HSQLDB.

Буду дуже вдячний, якщо хтось зможе пояснити, чому існує така різка різниця.


2
будь-ласка, покажіть результат EXPLAIN PLANІ Спробуйте запустити DISTINCTзапит ПІСЛЯ запуску, GROUP BYщоб побачити, чи, можливо, кешування не зміщує час ...
— Yahia

Якщо ви отримаєте однаковий план для кожного запиту, це схоже на те, що дані таблиці або результат кешовано.
— a'r

Я запускав їх стільки разів, що вважаю, що кешування не є проблемою. Я розміщую EXLAIN PLAN FORрезультати.
— Мартін Димитров

У мене є ідея, але я насправді не впевнений - будь ласка, спробуйте SELECT DISTINCT business_key FROM (SELECT business_key FROM memory WHERE concept <> 'case' or attrib <> 'status' or value <> 'closed')- це повинно показати таку ж продуктивність, яку ви бачите, GROUP BYЯКЩО моя ідея правильна.
— Yahia

@Yahia: все ще дуже повільно - 94 секунди. Я буду запускати ті самі запити в MySQL, щоб побачити, що буде показано
— Мартін Димитров

Відповіді:


77

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

  • Скопіюйте всі business_keyзначення у тимчасову таблицю
  • Сортувати тимчасову таблицю
  • Скануйте тимчасову таблицю, повертаючи кожен елемент, який відрізняється від попереднього

group byМоже бути виконаний як:

  • Відскануйте повну таблицю, зберігаючи кожне значення business keyв хеш- таблиці
  • Поверніть ключі хеш-таблиці

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

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


Дякую за відповідь. Ваші здогади видно з EXPLAINрезультату? Мені обидва здаються однаковими.
— Мартін Димитров

Наскільки я бачу, у плані не вказано, як він буде виконувати приєднання. Я навіть не впевнений, чому це зробило б об'єднання. Ймовірно, потрібен спеціаліст HSQLDB, щоб прочитати пояснення.
— Andomar

Як вказує відповідь, другий метод використовує більше пам'яті і може занадто часто впливати на збір сміття (GC). Якщо ви збільшите розміщення пам'яті JVM, між цими двома запитами не повинно бути величезної різниці.
— Fredt

Я зробив додатковий тест, ввівши всі різні ключі в таблицю (див. Вище). Ви вважаєте, що результат підтверджує вашу думку? Дуже дякую.
— Мартін Димитров

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