Як пришвидшити вибір чітких?


16

У деяких даних про часовий ряд у мене є простий вибір:

SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';

І це займає 112 секунд. Ось план запитів:

http://explain.depesz.com/s/NTyA

У моїй програмі потрібно зробити багато різних операцій і вважається таким. Чи є більш швидкий спосіб отримати такий вид даних?

Відповіді:


19

Напевно, ви не хочете цього чути, але найкращий варіант для прискорення - SELECT DISTINCTце уникати DISTINCT для початку. У багатьох випадках (не у всіх!) Цього можна уникнути за допомогою кращого дизайну баз даних або кращих запитів.

Іноді, GROUP BYшвидше, тому що він займає інший шлях коду.

У вашому конкретному випадку не здається, що ви можете позбутися DISTINCT. Але ви можете підтримати запит спеціалізованим індексом, якщо у вас є багато запитів такого типу:

CREATE INDEX foo ON events (project_id, "time", user_id);

Додавання user_idкорисно лише в тому випадку, якщо з цього ви отримаєте сканування , призначені лише для покажчиків . Докладніше перейдіть за посиланням. Видалить з плану запитів дорогий Bitmap Heap Scan , який забирає 90% часу запиту.

Ваша EXPLAIN висновок підказує мені, що запит має стиснути 2491 різних користувачів із півмільйона відповідних рядків. Це не стане надшвидким, що б ви не робили, але це може бути значно швидше.

Якщо інтервали часу у ваших запитах завжди однакові, MATERIALIIZED VIEWскладанняuser_id на (project_id, <fixed time intervall>)певний шлях пройде досить довго. Немає жодного шансу з різними інтервалами часу. Можливо, ви могли б принаймні скласти користувачів на годину чи якусь мінімальну одиницю часу, і це дозволить придбати достатню продуктивність, щоб гарантувати значні витрати.

Нітпік:
Скоріш за все, предикати "time"повинні бути:

AND "time" >= '2015-01-11 8:00:00'
AND "time" <  '2015-02-10 8:00:00';

Убік:
Не використовуйте timeяк ідентифікатор. Це зарезервоване слово у стандартному SQL та базовий тип у Postgres.


Я читав трохи про сканування лише з індексом, я дам йому знімок.
Сем

На жаль, інтервал часу не фіксований.
Сем

@Sam: Отже, наскільки швидше отримали ваш приклад запиту із запропонованим індексом?
Ервін Брандстеттер

3
@edwin: Ще не пробували на виробництві. Однак я запустив оригінальний запит на мій локальний (з тими ж даними), і він зайняв 3678,780 мс. Потім я додав індекс, і він перевищив швидкість до 170.156 мс. План тепер містить "Сканувати лише за допомогою покажчика за допомогою foo на події".
Сем

1
@Sam: Приємно! Саме на це я і прагнув.
Erwin Brandstetter

2

Ось мій тест на випадок Сема та відповідь Ервіна

drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;

insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date 
limit 10000000

-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;

CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"         

-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;

Ервін сказав: "Ви, мабуть, не хочете цього чути, але найкращий варіант пришвидшити SELECT DISTINCT - це уникнути DISTINCT для початку. У багатьох випадках (не всі!) Цього можна уникнути за допомогою кращого дизайну баз даних або кращих запитів. ". Я думаю, він має рацію, нам слід уникати використання "чіткого, групування, упорядкування" (якщо він є).

Я зіткнувся з ситуацією, як у випадку Сама, і думаю, що Сем може використовувати розділ на столі подій за місяцем. Це зменшить розмір ваших даних при запиті, але вам потрібна функція (pl / pgsql), щоб виконати замість запиту вище. Функція знайде відповідні розділи (залежать від умов) для виконання запиту.


2
> Я думаю, що він має рацію, нам слід уникати використання "чіткого, групування за порядком", - а також SELECT, INSERT та UPDATE. Якщо ми уникатимемо цих конструкцій, наша база даних буде дуже швидкою!
greatvovan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.