Better index with leading bucket_id
You want one row per bucket. An index with leading bucket_id
will be much more useful.
CREATE INDEX events_bucket_id_created_at_idx ON events (bucket_id, created_at DESC);
Related:
Since you have a very small number of distinct values in bucket_id
("rows=979"), this query technique should give you dramatically faster results, based on my suggested index:
WITH RECURSIVE cte AS (
( -- parentheses required
SELECT bucket_id, created_at
FROM events
ORDER BY bucket_id, created_at DESC
LIMIT 1
)
UNION ALL
SELECT e.*
FROM cte c
CROSS JOIN LATERAL (
SELECT e.bucket_id, e.created_at
FROM events e
WHERE e.bucket_id > c.bucket_id
ORDER BY e.bucket_id, e.created_at DESC
LIMIT 1
) e
WHERE c.bucket_id IS NOT NULL
)
SELECT * FROM cte
WHERE bucket_id IS NOT NULL;
It emulates a "loose index scan", only picking the "first" row for every distinct bucket_id
- exactly what you are looking for.
Note how the sort order in the query meticulously matches the index.
If the visibility map of the table is up to date (i.e. the table is vacuum'ed enough), you get index-only scans. Should apply, since the slow query you demonstrated got an index-only scan, too. (Though that's scanning the whole index instead of just leading entries per bucket). Related:
This assumes both columns of interest to be NOT NULL
. Else you have to do more.
If you also have a table bucket
with one row per relevant bucket_id
, this is even a bit faster, yet:
SELECT b.bucket_id, e.created_at
FROM bucket b
CROSS JOIN LATERAL (
SELECT e.created_at
FROM events e
WHERE e.bucket_id = b.bucket_id
ORDER BY e.created_at DESC
LIMIT 1
) e
ORDER BY b.bucket_id;
See:
Stuck with index on (created_at DESC, bucket_id ASC)
We can work with the additional meta information from your comments:
I know all the buckets I care about have recent events
You can enhance the queries above, but a different angle based on that should perform better:
SELECT DISTINCT ON (bucket_id)
bucket_id, created_at
FROM events
WHERE created_at > now() - interval '15 minutes' -- adapt as needed
ORDER BY bucket_id, created_at DESC;
Should be faster when limited to the tiny (?) fraction of the most recent rows. Postgres can read top rows from the index and feed that to DISTINCT ON
. About DISTINCT ON
: