Context
I have a table named companies_establishments
that holds ~33M rows.
I created a GIN index with trigrams, so I can make LIKE
queries much faster.
CREATE INDEX companies_establishments_id_index ON companies_establishments USING gin (id gin_trgm_ops);
However, it still takes 150ms and I don't understand why. I know that the index is big because I have a lot of records, but does it really explain these 150ms? Is there really only a 1 to 4 time difference between a full seq scan and an index scan with trigrams? What happens between 00.00 and 157.625 when index scan starts, according to the query plan (see below)? Is there a way to do a string search more efficiently?
EXPLAIN ANALYZE using GIN index
EXPLAIN (ANALYZE, VERBOSE, BUFFERS, COSTS OFF)
SELECT COUNT(*) FROM companies_establishments ce WHERE ce.id LIKE '502217755000%';
Result with the index:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Aggregate (actual time=157.640..157.641 rows=1 loops=1)
Output: count(*)
Buffers: shared hit=16356
-> Bitmap Heap Scan on public.companies_establishments ce (actual time=157.634..157.635 rows=2 loops=1)
Recheck Cond: ((ce.id)::text ~~ '502217755000%'::text)
Heap Blocks: exact=1
Buffers: shared hit=16356
-> Bitmap Index Scan on companies_establishments_id_index (actual time=157.625..157.626 rows=2 loops=1)
Index Cond: ((ce.id)::text ~~ '502217755000%'::text)
Buffers: shared hit=16355
Planning:
Buffers: shared hit=11
Planning Time: 0.446 ms
Execution Time: 157.715 ms
Result without the index:
Finalize Aggregate (actual time=627.485..630.544 rows=1 loops=1)
Output: count(*)
Buffers: shared hit=225 read=353547
I/O Timings: read=309.562
-> Gather (actual time=627.332..630.531 rows=3 loops=1)
Output: (PARTIAL count(*))
Workers Planned: 2
Workers Launched: 2
Buffers: shared hit=225 read=353547
I/O Timings: read=309.562
-> Partial Aggregate (actual time=607.888..607.889 rows=1 loops=3)
Output: PARTIAL count(*)
Buffers: shared hit=225 read=353547
I/O Timings: read=309.562
Worker 0: actual time=598.430..598.430 rows=1 loops=1
Buffers: shared hit=71 read=114427
I/O Timings: read=100.313
Worker 1: actual time=598.529..598.530 rows=1 loops=1
Buffers: shared hit=72 read=114384
I/O Timings: read=99.195
-> Parallel Seq Scan on public.companies_establishments ce (actual time=519.032..607.873 rows=1 loops=3)
" Output: id, siren, name, activity_code, public_name_1, public_name_2, public_name_3, diffusion, active, creation_date, association_id, workforce_size, workforce_size_update_year, can_employ, headquarter"
Filter: ((ce.id)::text ~~ '502217755000%'::text)
Rows Removed by Filter: 11183349
Buffers: shared hit=225 read=353547
I/O Timings: read=309.562
Worker 0: actual time=598.419..598.419 rows=0 loops=1
Buffers: shared hit=71 read=114427
I/O Timings: read=100.313
Worker 1: actual time=598.518..598.518 rows=0 loops=1
Buffers: shared hit=72 read=114384
I/O Timings: read=99.195
Planning:
Buffers: shared hit=6
Planning Time: 0.367 ms
Execution Time: 632.989 ms
EXPLAIN ANALYZE using btree index
I also tried to use normal btree index by querying with a simple =
, which really much faster.
EXPLAIN (ANALYZE, VERBOSE, BUFFERS, COSTS OFF)
SELECT COUNT(*) FROM companies_establishments ce WHERE ce.id = '50221775500011';
Result:
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (actual time=0.070..0.071 rows=1 loops=1)
Output: count(*)
Buffers: shared hit=5
-> Index Only Scan using companies_establishments_pkey on public.companies_establishments ce (actual time=0.058..0.060 rows=1 loops=1)
Output: id
Index Cond: (ce.id = '50221775500011'::text)
Heap Fetches: 1
Buffers: shared hit=5
Planning:
Buffers: shared hit=1
Planning Time: 0.336 ms
Execution Time: 0.130 ms
track_io_timing
is on, however there was no disk IO because I set my shared buffer size to 4GB.