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


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.