Почему подсчет PostgresQL такой медленный даже при сканировании только по индексу - PullRequest
0 голосов
/ 29 апреля 2018

У меня есть простой запрос подсчета, который может использовать сканирование только по индексу, но в PostgresQL это все еще занимает много времени!

У меня есть таблица cars с 2 столбцами type bigint и active boolean, у меня также есть индекс по нескольким столбцам для этих столбцов

CREATE TABLE cars
(
id BIGSERIAL NOT NULL
    CONSTRAINT cars_pkey PRIMARY KEY ,
type BIGINT NOT NULL ,
name VARCHAR(500) NOT NULL ,
active            BOOLEAN DEFAULT TRUE NOT NULL,
created_at        TIMESTAMP(0) WITH TIME ZONE default NOW(),
updated_at        TIMESTAMP(0) WITH TIME ZONE default NOW(),
deleted_at        TIMESTAMP(0) WITH TIME ZONE
);
CREATE INDEX cars_type_active_index ON cars(type, active);

Я вставил некоторые тестовые данные с записями 950 тыс., Тип = 1 имеет 600 тыс. Записей

INSERT INTO cars (type, name) (SELECT 1, 'car-name' FROM generate_series(1,600000));
INSERT INTO cars (type, name) (SELECT 2, 'car-name' FROM generate_series(1,200000));
INSERT INTO cars (type, name) (SELECT 3, 'car-name' FROM generate_series(1,100000));
INSERT INTO cars (type, name) (SELECT 4, 'car-name' FROM generate_series(1,50000));

Запустим VACUUM ANALYZE и заставим PostgresQL использовать сканирование только по индексу

VACUUM ANALYSE;
SET enable_seqscan = OFF;
SET enable_bitmapscan = OFF;

ОК, у меня простой запрос по type и active

EXPLAIN (VERBOSE, BUFFERS, ANALYSE) 
SELECT count(*) 
FROM cars 
WHERE type = 1 AND active = true;

Результат:

Aggregate  (cost=24805.70..24805.71 rows=1 width=0) (actual time=4460.915..4460.918 rows=1 loops=1)
Output: count(*)
Buffers: shared hit=2806
->  Index Only Scan using cars_type_active_index on public.cars (cost=0.42..23304.23 rows=600590 width=0) (actual time=0.051..2257.832 rows=600000 loops=1)
        Output: type, active
        Index Cond: ((cars.type = 1) AND (cars.active = true))
        Filter: cars.active
        Heap Fetches: 0
        Buffers: shared hit=2806
Planning time: 0.213 ms
Execution time: 4461.002 ms
(11 rows)

Посмотрите на результат объяснения запроса,

  • Используется Index Only Scan, с проверкой только по индексу, в зависимости от visibilities map, PostgresQL иногда нужно выбрать Table Heap для проверки видимости кортежа, но я уже запустил VACUUM ANALYZE, чтобы вы могли видеть Heap fetch = 0, поэтому для ответа на этот запрос достаточно чтения индекса.

  • Размер индекса довольно мал, он может уместиться в буферный кеш (Buffers: shared hit=2806), PostgresQL не нужно извлекать страницы с диска.

Оттуда я не могу понять, почему PostgresQL так долго (4.5s) отвечает на запрос, 1M записей - это не большое количество записей, все уже кэшировано в памяти, и данные в индексе видны, не нужно извлекать кучу.

PostgreSQL 9.5.10 на x86_64-pc-linux-gnu, скомпилированный gcc (код Debian 4 здесь 9.2-10) 4.9.2, 64-битная

Я тестировал его на докере 17.09.1-ce, Macbook pro 2015.

Я все еще новичок в PostgresQL и пытаюсь сопоставить свои знания с реальными случаями. Большое спасибо,

1 Ответ

0 голосов
/ 25 сентября 2018

Кажется, я нашел причину, дело не в проблемах PostgresQL, а в том, что они работают в докере. Когда я запускаю прямо на своем Mac, время будет около 100 мс, что достаточно быстро.

Еще одна вещь, которую я выяснил, это причина, по которой PostgresQL по-прежнему использует seq scan вместо index only scan (вот почему я должен отключить seq_scan и bitmapscan в моем тесте):

  • Размер таблицы не так велик по сравнению с размером индекса, если я добавлю больше столбцов в таблицу, или чем больше длина столбцов, тем больше размер таблицы, тем больше вероятность использования индекса.
  • значение random_page_cost по умолчанию равно 4, мой диск довольно быстрый, поэтому я могу установить его на 1-2, это поможет правильнее оценить стоимость интерпретатора psql.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...