#sql #postgresql #оптимизация
Выбираю данные между двумя датами с количеством строк более миллиона таким запросом:
SELECT
time AT TIME ZONE 'UTC' AT TIME ZONE 'MSK' AS time,
type,
CASE
WHEN (duration > (extract(epoch from (time - '2015-08-29 12:36:50'))) )
THEN extract(epoch from (time - '2015-08-29 12:36:50'))
ELSE duration
END AS trim_duration
FROM clamps
WHERE device_id = 27 AND time BETWEEN '2015-08-29 12:36:50' AND '2016-03-15 12:36:50'
ORDER BY time ASC
Структура таблицы:
CREATE TABLE clamps
(
id serial NOT NULL,
"time" timestamp without time zone,
duration numeric,
type character varying(255),
device_id integer,
packet_id integer,
dur_float double precision DEFAULT 0,
created_at timestamp without time zone DEFAULT now(),
updated_at timestamp without time zone DEFAULT now(),
CONSTRAINT clamps_pkey PRIMARY KEY (id)
)
Имеется индекс:
CREATE UNIQUE INDEX index_clamps_on_device_id_time
ON clamps
USING btree (device_id, "time");
Explaine Analyze:
Sort (cost=149213.28..150770.01 rows=622695 width=21) (actual time=4263.385..4713.566
rows=1469514 loops=1)
Sort Key: "time"
Sort Method: external merge Disk: 71816kB
-> Bitmap Heap Scan on clamps (cost=17535.79..89284.59 rows=622695 width=21)
(actual time=190.593..2692.161 rows=1469514 loops=1)
Recheck Cond: ((device_id = 27) AND ("time" >= '2015-08-29 12:36:50'::timestamp
without time zone) AND ("time" <= '2016-03-15 12:36:50'::timestamp without time zone))
Heap Blocks: exact=17528
-> Bitmap Index Scan on index_clamps_on_device_id_time (cost=0.00..17380.12
rows=622695 width=0) (actual time=186.667..186.667 rows=1469514 loops=1)
Index Cond: ((device_id = 27) AND ("time" >= '2015-08-29 12:36:50'::timestamp
without time zone) AND ("time" <= '2016-03-15 12:36:50'::timestamp without time zone))
Planning time: 0.291 ms
Execution time: 5414.691 ms
Запрос выполняется секунд 9-13 (хоть explain и показывает 5.5). Можно как-то запрос
оптимизировать?
Ответы
Ответ 1
Есть ощущение, что потенциальная проблема у вас в условии WHERE с неявным преобразованием типов данных: time BETWEEN '2015-08-29 12:36:50' AND '2016-03-15 12:36:50', попробуйте переписать с CAST и добавить индекс на time (кстати, называть поля базы данных зарезервированными словами - плохая практика), такая же проблема возможно присутствует и в extract(epoch from (time - '2015-08-29 12:36:50'))Ответ 2
Разберем вывод Explaine Analyze Индекс задействуется, причем, индекс очень хорош, ибо задействует все поля выборки и реальное сканирование индекса составляет всего 186.667 ms. Тут оптимизировать нечего. Выборка по индексу занимает от 190.593 до 2692.161 ms. Можно ли что-то с этим сделать? Если версия базы >= 9.2, то можно построить индекс, который бы задействовал не только поля выборки, но и поля вывода, тогда вся выборка будет делаться исключительно по индексу и к таблице обращения вообще не будет, т.е. этот пункт будет отсутствовать. Если база постарее, то увы, only index scan появился лишь в 9.2. Соответсвенно, новый индекс: CREATE INDEX index_clamps_on_device_id_time_type_duration ON clamps USING btree (device_id, "time", type, duration); Сортировка однопроходная, выполняется от 4263.385 до 4713.566 ms. Вот он, "добрый" кусок потери времени. К сожалению, единственное, что с этим можно сделать - отказаться от сортировки. Задумайтесь, так ли она нужна? Несостыковка времени выполнения запроса с реально наблюдаемым. Напрашивается вопрос, а как производился замер реально наблюдаемого времени выполнения? Не уж-то от момента запуска запроса до появления данных в клиенте? Ну так извините, как на доставку 1.5 млн строк данных до клиента, так и на отображение их непостредственно в клиенте тоже время требуется. Время выполнения первого пункта можно сократить лишь уменьшением передаваемых данных (ничего лишнего не тащить) либо увеличением пропускной способности канала между клиентом и сервером. Второй - сменой клиента на более шустрый аналог. Если же выборка делается в коде своей программы/скрипта, то сменить подход к получению данных с сервера, не ждать получения всех данных, а забирать небольшими порциями, тогда их в параллельном потоке можно будет уже начать обрабатывать не дожидаясь получения всех данных. Еще одна причина несостыковки возможна при обращении к таблице с большим количеством наследованных таблиц. Но думается, не тот случай, при столь подробно составленном вопросе, врядли бы об этом умолчалось, так что эту тему не затрагиваю.
Комментариев нет:
Отправить комментарий