Огромная разница в производительности при использовании группировки по сравнению с отдельными

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

РЕДАКТИРОВАТЬ : я провел дополнительные тесты. С 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чтобы увидеть, возможно, некоторое кеширование искажает время ...
Яхия
Если у вас один и тот же план для каждого запроса, похоже, что либо данные таблицы, либо результат были кэшированы.
а'р
Я запускал их так много раз, что считаю, что кеширование не проблема. Выкладываю 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
Может ли специалист по SME объяснить это более подробно с примерами ... У меня была эта проблема много раз, но, кажется, я не могу ее решить ... Я знаю исправление, но хочу знать, как и ПОЧЕМУ
singhswat 03