Невозможно улучшить скорость соединения SQL с индексами - PullRequest
0 голосов
/ 24 октября 2019

Я совершенно новичок в SQL и пытаюсь ускорить запросы на соединение для очень больших данных. Я начал добавлять индексы (но, честно говоря, у меня нет глубокого понимания их) и, не видя особых изменений, я решил сравнить с более простым, смоделированным примером. Я использую psql интерфейс PostgreSQL 11.5 на MacOS 10.14.6. Сервер данных размещен локально на моем компьютере. Я прошу прощения за недостаток соответствующей информации, впервые публикуя информацию о SQL.

Структуры баз данных

Я создал две изначально идентичные базы данных, db и db_idx. Я никогда не помещал индексы или ключи в таблицы в db, хотя я пытаюсь поместить индексы и ключи в таблицы в db_idx. Затем я выполняю простые запросы на соединение внутри db и db_idx отдельно и сравниваю производительность. В частности, db_idx состоит из двух таблиц:

  • Таблица клиентов с 100 000 строк и следующей структурой:
                   Table "public.client"
       Column    |  Type   | Collation | Nullable | Default
    -------------+---------+-----------+----------+---------
     client_id   | integer |           | not null |
     client_name | text    |           |          |
    Indexes:
        "pkey_c" PRIMARY KEY, btree (client_id)
  • Таблица client_addition с 70000строки и следующая структура:
             Table "public.client_additional"
       Column   |  Type   | Collation | Nullable | Default
    ------------+---------+-----------+----------+---------
     client_id  | integer |           | not null |
     client_age | integer |           |          |
    Indexes:
        "pkey_ca" PRIMARY KEY, btree (client_id)
        "cov_idx" btree (client_id, client_age)

Столбец client_id в таблице client_additional содержит подмножество значений client_id клиента. Обратите внимание на первичные ключи и другой индекс, который я создал для client_additional. Я думал, что это увеличит скорость запроса эталонного теста (см. Ниже), но это не так.

Важно, что база данных базы данных точно такая же (та же структура, те же значения) за исключением того, что не имеет ни индекса, ни ключа .

Примечание: таблица client и client_additional, возможно, должна представлять собой одну таблицу, поскольку они дают информацию точно втот же уровень (уровень клиента). Однако база данных, которую я использую в реальной жизни, была структурирована таким образом: некоторые таблицы разбиты на несколько таблиц по «темам», хотя они дают информацию на одном уровне. Я не знаю, имеет ли это значение для моей проблемы.

Benchmark Query

Я использую следующий запрос, который во многом имитирует то, что мне нужно делать с реальными данными:

    SELECT 
      client_additional.client_id, 
      client_additional.client_age,
      client.client_name
    FROM client
    INNER JOIN client_additional 
    ON client.client_id = client_additional.client_id;

Результаты теста

В обеих базах данных запрос теста занимает около 630 мс. Удаление ключей и / или индексов в db_idx ничего не меняет. Эти результаты теста переносятся на большие объемы данных: скорость одинакова в индексированных и неиндексированных случаях.

Вот где я. Как мне объяснить эти результаты? Можно ли улучшить скорость соединения и как?

Ответы [ 3 ]

1 голос
/ 24 октября 2019

Используйте глагол EXPLAIN, чтобы увидеть, как механизм SQL намеревается разрешить запрос. (Различные механизмы SQL представляют это по-разному.) Вы можете окончательно увидеть, будет ли использоваться индекс.

Кроме того, сначала вам нужно будет загрузить таблицы с лотом теста. данные, потому что EXPLAIN скажет вам, что движок SQL намеревается сделать прямо сейчас, , и это решение частично основано на размере таблицы и различной другой статистике. Если таблица практически пуста, механизм SQL может решить, что индекс не будет полезен сейчас.

Механизмы SQL используют все виды очень умных приемов для оптимизации производительности, так что на самом деле этоДостаточно сложно получить полезную временную проверку. Но, если EXPLAIN говорит вам, что индекс используется, это в значительной степени ответ, который вы ищете.

0 голосов
/ 25 октября 2019

Настройка небольшой тестовой БД, добавление нескольких строк и выполнение запроса:

CREATE TABLE client
(
   client_id integer PRIMARY KEY,
   client_name text
);

CREATE TABLE client_additional
(
   client_id integer PRIMARY KEY,
   client_age integer
);

INSERT INTO client (client_id, client_name) VALUES (generate_series(1,100000),'Phil');
INSERT INTO client_additional (client_id, client_age) VALUES (generate_series(1,70000),21);

ANALYZE;

EXPLAIN ANALYZE SELECT 
   client_additional.client_id, 
   client_additional.client_age,
   client.client_name
FROM
   client
INNER JOIN
   client_additional 
ON
   client.client_id = client_additional.client_id;

дал мне этот план:

 Hash Join  (cost=1885.00..3590.51 rows=70000 width=11) (actual time=158.958..44 1.222 rows=70000 loops=1)
   Hash Cond: (client.client_id = client_additional.client_id)
   ->  Seq Scan on client  (cost=0.00..1443.00 rows=100000 width=7) (actual time =0.019..100.318 rows=100000 loops=1)
   ->  Hash  (cost=1010.00..1010.00 rows=70000 width=8) (actual time=158.785..15 8.786 rows=70000 loops=1)
         Buckets: 131072  Batches: 1  Memory Usage: 3759kB
         ->  Seq Scan on client_additional  (cost=0.00..1010.00 rows=70000 width =8) (actual time=0.016..76.507 rows=70000 loops=1)
 Planning Time: 0.357 ms
 Execution Time: 506.739 ms

То, что вы можете видеть из этой таблицы, - обе таблицыбыли последовательно отсканированы, значения из каждой таблицы были хэшированы, и было выполнено хеш-соединение. Постгрес определил, что это оптимальный способ выполнить этот запрос.

Если вы воссоздаете таблицы без первичного ключа (и, следовательно, удаляете неявный индекс для каждого столбца PK), вы получаете точно такой же планТак как Postgres определил, что самый быстрый способ выполнить этот запрос - игнорировать индексы и хэшировать значения таблицы, а затем выполнить хеш-соединение с двумя наборами хэшированных значений, чтобы получить результат.

После измененияколичество строк в клиентской таблице примерно так:

TRUNCATE Client;

INSERT INTO client (client_id, client_name) VALUES (generate_series(1,200000),'phil');

ANALYZE;

Затем я повторно запустил тот же запрос и вместо этого вижу этот план:

Merge Join  (cost=1.04..5388.45 rows=70000 width=13) (actual time=0.050..415.50
3 rows=70000 loops=1)
   Merge Cond: (client.client_id = client_additional.client_id)
   ->  Index Scan using client_pkey on client  (cost=0.42..6289.42 rows=200000 width=9) (actual time=0.022..86.897 rows=70001 loops=1)
   ->  Index Scan using client_additional_pkey on client_additional  (cost=0.29..2139.29 rows=70000 width=8) (actual time=0.016..86.818 rows=70000 loops=1)
 Planning Time: 0.517 ms
 Execution Time: 484.264 ms

Здесь вы можете видеть, что сканирование индексаЭто было сделано, так как Postgres определил, что этот план лучше, исходя из текущего числа строк в таблицах.

Дело в том, что Postgres будет использовать индексы, когда почувствует, что даст более быстрый результат,но пороги до их использования несколько выше, чем вы могли ожидать.

Всего наилучшего,

Phil

0 голосов
/ 24 октября 2019

У вас есть первичный ключ в двух таблицах, который будет использоваться для join s. Если вы действительно хотите, чтобы запросы замедлялись, удалите первичные ключи.

Что происходит? Ну, я предполагаю, что планы выполнения одинаковы с вторичными индексами или без них. Вам нужно будет взглянуть на сами планы.

В отличие от большинства других баз данных, Postgres не получает выгоды от покрытия индексов, поскольку информация о блокировке хранится только на страницах данных. Таким образом, страницы данных всегда должны быть доступны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...