Производительность SQL-запроса в большой таблице

У меня есть таблица postgres, которая выглядит следующим образом:

id: int [PK]
scenario_id: int [FK]
node_id: int Optional[FK]
element_id: int Optional[FK]
result: char(20)
value: double
unit: char(12)

Где я сохраняю строки для различных результатов как для узлов, так и для элементов моего графика. Если результирующая переменная позволяет сказать, что x действителен только для узла, тогда строка выглядит следующим образом:

id    scenario_id    node_id    element_id    result    value    unit
1     1              100        [null]        x         0.1      'MW'

Я понял, что после добавления различных сценариев эта таблица очень быстро становится очень большой (40 миллионов записей в 5000 сценариях). И мой запрос на получение всех результатов 1 сценария занимает в среднем 7 секунд, согласно pgadmin.

Как я могу сделать эту таблицу более эффективной, чтобы запрос не занимал 7 секунд?


ЗАПРОС:

SELECT * FROM simulation.scenario_results t
    WHERE t.scenario_id = 5000

Объяснять:

СКРИПТ DLL:

-- Table: simulation.scenario_results

-- DROP TABLE IF EXISTS simulation.scenario_results;

CREATE TABLE IF NOT EXISTS simulation.scenario_results
(
    id integer NOT NULL DEFAULT nextval('simulation.scenario_results_id_seq'::regclass),
    scenario_id integer NOT NULL,
    node_id integer,
    element_id integer,
    result character varying(20) COLLATE pg_catalog."default" NOT NULL,
    value double precision NOT NULL,
    unit character varying(12) COLLATE pg_catalog."default",
    CONSTRAINT scenario_results_pkey PRIMARY KEY (id),
    CONSTRAINT scenario_results_element_id_fkey FOREIGN KEY (element_id)
        REFERENCES simulation.elements (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE,
    CONSTRAINT scenario_results_node_id_fkey FOREIGN KEY (node_id)
        REFERENCES simulation.nodes (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE,
    CONSTRAINT scenario_results_scenario_id_fkey FOREIGN KEY (scenario_id)
        REFERENCES simulation.scenarios (id) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS simulation.scenario_results
    OWNER to postgres;

Пожалуйста, обновите свой вопрос, указав 1) запрос, который вы выполняете 2) подробное ОБЪЯСНЕНИЕ запроса 3) производительность, которую вы хотите достичь

NickW 20.06.2024 09:57

8 тыс. строк на сценарий — это не так уж много, и тот факт, что в общей сложности таблица имеет 40 млн, не должен иметь особого значения для отдельных сценариев, если вы используете адекватное индексирование, секционирование и кластеризацию. Помимо того, что NickW уже запросил, добавьте определения индексов и укажите, какая это версия PostgreSQL и на чем она работает. Если вы используете pgAdmin, вы можете щелкнуть правой кнопкой мыши по таблице>Scripts>CREATE Script, чтобы получить полный оператор DDL для таблицы вместе со всеми ее индексами и ограничениями. Это будет более информативно, а также позволит воспроизвести ваш случай.

Zegarek 20.06.2024 10:17

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

Charlieface 20.06.2024 11:53

@NickW обновлено!

oakca 20.06.2024 12:21

Поместите индекс на сценарий_id

NickW 20.06.2024 12:32

@NickW всегда ли полезно размещать индексы на всех внешних ключах? или актуально только для этого запроса?

oakca 20.06.2024 12:57

Вы помещаете индексы в столбцы (в основном) для повышения производительности запросов - поэтому это зависит от того, какие запросы вы выполняете, какие индексы вам нужны (и есть затраты на поддержание индексов, поэтому вы не просто индексируете все). FK, скорее всего, будут использоваться в запросах, поэтому они обычно индексируются. Вы также можете рассмотреть возможность индексирования столбцов, используемых в предложениях WHERE, как в этом случае.

NickW 20.06.2024 13:32

На самом деле все FK должны иметь индексы из-за проверок ограничений (когда вы удаляете родительскую строку, необходимо проверить, существуют ли какие-либо дочерние строки).

Charlieface 20.06.2024 17:40
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
8
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, единственный индекс, который у вас есть, — это unique, который сопровождается primary key. Даже простое btree по умолчанию для столбца, по которому вы фильтруете, ускорит процесс:

create index on simulation.scenario_results(scenario_id);

После этого не забудьте vacuum analyze simulation.scenario_results;, а затем снова проверьте свой запрос, чтобы увидеть, что последовательное сканирование заменено гораздо более быстрым индексным сканированием. Первое означает, что вашим запросам приходилось каждый раз читать все целиком, чтобы найти ваш конкретный сценарий, второе означает, что теперь он проверяет, какие части таблицы можно полностью пропустить и где найти конкретный scenario_id.

В этом тесте на 300 тысячах случайных строк parallel seq scan требовалось 90ms, а index scan опускалось ниже 1ms.

Вы можете ускорить этот процесс двумя способами: либо сделать индекс покрывающим индексом, что означает, что все данные, которые вам могут понадобиться из таблицы, будут находиться в нем, поэтому запрос сможет читать все прямо из индекса, даже не чтобы посетить таблицу, которая представляет собой сканирование только индекса, которое в тесте сводилось к 0.3ms:

create index on simulation.scenario_results(scenario_id)
   include(id,node_id,element_id,result,value,unit);

Или вы можете кластеризовать таблицу таким образом, чтобы ускорить переход от обычного легковесного индекса, который занимает всего лишь 0.4ms, без необходимости дублировать данные между таблицей и индексом:

create index idx1 on simulation.scenario_results(scenario_id);
cluster verbose simulation.scenario_results using idx1;
analyze simulation.scenario_results ;

Другие вопросы по теме