I have a production database which is replicated to a another host using londist. The table looks like
# \d+ usermessage
Table "public.usermessage"
Column | Type | Modifiers | Description
-------------------+-------------------+-----------+-------------
id | bigint | not null |
subject | character varying | |
message | character varying | |
read | boolean | |
timestamp | bigint | |
owner | bigint | |
sender | bigint | |
recipient | bigint | |
dao_created | bigint | |
dao_updated | bigint | |
type | integer | |
replymessageid | character varying | |
originalmessageid | character varying | |
replied | boolean | |
mheader | boolean | |
mbody | boolean | |
Indexes:
"usermessage_pkey" PRIMARY KEY, btree (id)
"usermessage_owner_key" btree (owner)
"usermessage_recipient_key" btree (recipient)
"usermessage_timestamp_key" btree ("timestamp")
"usermessage_type_key" btree (type)
Has OIDs: no
If executed on the replicated database, the select is fast as expected, if executed on the production host it's horrible slow. To make things more strange, not all timestamps are slow, some of them are fast on both hosts. The filesystem and the storage behind the production host is fine and not under heavy usage. Any ideas?
replication# explain analyse SELECT COUNT(id) FROM usermessage WHERE owner = 1234567 AND timestamp > 1362077127010;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=263.37..263.38 rows=1 width=8) (actual time=0.059..0.060 rows=1 loops=1)
-> Bitmap Heap Scan on usermessage (cost=259.35..263.36 rows=1 width=8) (actual time=0.055..0.055 rows=0 loops=1)
Recheck Cond: ((owner = 1234567) AND ("timestamp" > 1362077127010::bigint))
-> BitmapAnd (cost=259.35..259.35 rows=1 width=0) (actual time=0.054..0.054 rows=0 loops=1)
-> Bitmap Index Scan on usermessage_owner_key (cost=0.00..19.27 rows=241 width=0) (actual time=0.032..0.032 rows=33 loops=1)
Index Cond: (owner = 1234567)
-> Bitmap Index Scan on usermessage_timestamp_key (cost=0.00..239.82 rows=12048 width=0) (actual time=0.013..0.013 rows=0 loops=1)
Index Cond: ("timestamp" > 1362077127010::bigint)
Total runtime: 0.103 ms
(9 rows)
production# explain analyse SELECT COUNT(id) FROM usermessage WHERE owner = 1234567 AND timestamp > 1362077127010;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=267.39..267.40 rows=1 width=8) (actual time=47536.590..47536.590 rows=1 loops=1)
-> Bitmap Heap Scan on usermessage (cost=263.37..267.38 rows=1 width=8) (actual time=47532.520..47536.579 rows=3 loops=1)
Recheck Cond: ((owner = 1234567) AND ("timestamp" > 1362077127010::bigint))
-> BitmapAnd (cost=263.37..263.37 rows=1 width=0) (actual time=47532.334..47532.334 rows=0 loops=1)
-> Bitmap Index Scan on usermessage_owner_key (cost=0.00..21.90 rows=168 width=0) (actual time=0.123..0.123 rows=46 loops=1)
Index Cond: (owner = 1234567)
-> Bitmap Index Scan on usermessage_timestamp_key (cost=0.00..241.22 rows=12209 width=0) (actual time=47530.255..47530.255 rows=5255617 loops=1)
Index Cond: ("timestamp" > 1362077127010::bigint)
Total runtime: 47536.668 ms
(9 rows)
I am less familiar with postgresql than mysql but
(actual time=0.013..0.013 rows=0 loops=1)
and
(actual time=47530.255..47530.255 rows=5255617 loops=1)
Suggests to me that your production db has more data given that the rows are drastically different.
Related
I am fairly new to Postgres and I am trying to debug an issue I found recently in which adding an Index has slowed down my query. To begin with, here is the bare bones table that I have. There are more indexes and columns but I am only showing the ones that are relevant to the issue:
> \d+ classes
Table "public.classes"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------+-----------------------------+-----------+----------+--------------------------+----------+--------------+-------------
classid | bigint | | not null | gen_random_js_safe_int() | plain | |
schoolid | bigint | | | | plain | |
classname | character varying(255) | | not null | | extended | |
Indexes:
"classes_classname_schoolid_idx" UNIQUE, btree (classname, schoolid) WHERE schoolid IS NOT NULL
Foreign-key constraints:
"classes_schoolid_fkey" FOREIGN KEY (schoolid) REFERENCES schools(schoolid)
Referenced by:
TABLE "classtoschool" CONSTRAINT "classtoschool_classid_fkey" FOREIGN KEY (classid) REFERENCES classes(classid) ON DELETE CASCADE
> \d+ classtoschool
Table "public.classtoschool"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-------------------+-----------------------------+-----------+----------+-----------------------------+----------+--------------+-------------
classid | bigint | | | | plain | |
schoolid | bigint | | not null | | plain | |
Foreign-key constraints:
"classtoschool_classid_fkey" FOREIGN KEY (classid) REFERENCES classes(classid) ON DELETE CASCADE
"classtoschool_schoolid_fkey" FOREIGN KEY (schoolid) REFERENCES schools(schoolid) ON DELETE CASCADE
> \d+ schools
Table "public.schools"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-------------------------------------+-----------------------------+-----------+----------+--------------------------+----------+--------------+-------------
schoolid | bigint | | not null | gen_random_js_safe_int() | plain | |
schoolStatus | character varying(100) | | not null | | extended | |
Referenced by:
TABLE "classes" CONSTRAINT "classes_schoolid_fkey" FOREIGN KEY (schoolid) REFERENCES schools(schoolid)
TABLE "classtoschool" CONSTRAINT "classtoschool_schoolid_fkey" FOREIGN KEY (schoolid) REFERENCES school(schoolid) ON DELETE CASCADE
The index in question is:
"classes_classname_schoolid_idx" UNIQUE, btree (classname, schoolid) WHERE schoolid IS NOT NULL
Before adding this index, the query execution times are in single digit milliseconds, however, after adding this index, it is triple digits milliseconds sometimes even in seconds under heavy load.
The funniest thing is, that when I run the EXPLAIN, it doesn't even include the index. But for whatever reason, adding the index impacts the query execution time. Another awkward thing is that I have different databases for different regions, and this increase in latency is only observed in this one database. All Postgres versions and settings are identical.
The query that I run is:
EXPLAIN ANALYZE VERBOSE SELECT
schools.ommitedColumnA,
classes.classId,
classes.classname,
classes.omittedColumnB,
classes.omittedColumnC,
classes.omittedColumnD,
classes.omittedColumnE
FROM
classes
LEFT JOIN classtoschool ON (classes.classId = classtoschool.classid)
LEFT JOIN schools ON (classtoschool.schoolid = schools.schoolid)
WHERE
(classes.classname = 'maths') AND
(schools.schoolid = '12345678') AND
((schools.banned = false OR schools.banned IS NULL) AND schools.schoolStatus = 'RUNNING') AND
(schools.schoolStatus != 'BREAK' OR schools.schoolStatus IS NULL);
Explain Results:
Nested Loop (cost=1834.56..7368.73 rows=2704 width=104) (actual time=3165.232..3191.356 rows=1 loops=1)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classesomittedColumnD, classes.omittedColumnE
-> Index Scan using schools_pkey on public.schools (cost=0.42..2.64 rows=1 width=16) (actual time=30.811..30.815 rows=1 loops=1)
Output: schools.orgid, schools.schoolid
Index Cond: (schools.schoolid = '12345678'::bigint)
Filter: (((NOT schools.banned) OR (schools.banned IS NULL)) AND (((schools.schoolstatus)::text <> 'BREAK'::text) OR (schools.schoolstatus IS NULL)) AND ((schools.schoolstatus)::text = 'RUNNING'::text))
-> Gather (cost=1834.15..7339.05 rows=2704 width=104) (actual time=3134.409..3160.528 rows=1 loops=1)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classesomittedColumnD, classes.omittedColumnE, classtoschool.schoolid
Workers Planned: 2
Workers Launched: 2
-> Hash Join (cost=834.15..6068.65 rows=1127 width=104) (actual time=2890.802..3034.482 rows=0 loops=3)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classesomittedColumnD, classes.omittedColumnE, classtoschool.schoolid
Inner Unique: true
Hash Cond: (classes.classid = classtoschool.classid)
Worker 0: actual time=2555.322..2986.361 rows=1 loops=1
Worker 1: actual time=2986.457..2986.459 rows=0 loops=1
-> Parallel Seq Scan on public.classes (cost=0.00..5108.60 rows=47960 width=96) (actual time=3.194..1527.764 rows=38171 loops=3)
Output: classes.classid, classes.schoolid, classes.omittedColumnC, classesomittedColumnD, classes.classname, classes.omittedColumnB, classes.omittedColumnE
Filter: ((classes.classname)::text = 'maths'::text)
Rows Removed by Filter: 60282
Worker 0: actual time=0.229..1524.734 rows=39144 loops=1
Worker 1: actual time=0.255..1524.984 rows=39508 loops=1
-> Hash (cost=747.56..747.56 rows=6927 width=16) (actual time=1502.768..1502.769 rows=2134 loops=3)
Output: classtoschool.classid, classtoschool.schoolid
Buckets: 8192 Batches: 1 Memory Usage: 165kB
Worker 0: actual time=1457.649..1457.649 rows=2134 loops=1
Worker 1: actual time=1457.576..1457.576 rows=2134 loops=1
-> Index Only Scan using classtoschool_schoolid_classid_idx on public.classtoschool (cost=0.42..747.56 rows=6927 width=16) (actual time=12.409..1501.892 rows=2134 loops=3)
Output: classtoschool.classid, classtoschool.schoolid
Index Cond: (classtoschool.schoolid = '12345678'::bigint)
Heap Fetches: 1512
Worker 0: actual time=0.134..1456.763 rows=2134 loops=1
Worker 1: actual time=0.135..1456.757 rows=2134 loops=1
Planning Time: 307.276 ms
Execution Time: 3191.475 ms
As you can see, the index classes_classname_schoolid_idx is nowhere used.
Below is the EXPLAIN with the index removed:
Nested Loop (cost=1834.56..7368.73 rows=2704 width=104) (actual time=41.516..190.198 rows=1 loops=1)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classes.omittedColumnD, classes.omittedColumnE
-> Index Scan using schools_pkey on public.schools (cost=0.42..2.64 rows=1 width=16) (actual time=0.034..0.042 rows=1 loops=1)
Output: schools.schoolid
Index Cond: (schools.schoolid = '12345678'::bigint)
Filter: (((NOT schools.banned) OR (schools.banned IS NULL)) AND (((schools.schoolstatus)::text <> 'BREAK'::text) OR (schools.schoolstatus IS NULL)) AND ((schools.schoolstatus)::text = 'RUNNING'::text))
-> Gather (cost=1834.15..7339.05 rows=2704 width=104) (actual time=41.477..190.150 rows=1 loops=1)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classes.omittedColumnD, classes.omittedColumnE, classtoschool.schoolid
Workers Planned: 2
Workers Launched: 2
-> Hash Join (cost=834.15..6068.65 rows=1127 width=104) (actual time=12.321..17.334 rows=0 loops=3)
Output: classes.classid, classes.classname, classes.omittedColumnB, classes.omittedColumnC, classes.omittedColumnD, classes.omittedColumnE, classtoschool.schoolid
Inner Unique: true
Hash Cond: (classes.classid = classtoschool.classid)
Worker 0: actual time=0.006..0.007 rows=0 loops=1
Worker 1: actual time=0.006..0.006 rows=0 loops=1
-> Parallel Seq Scan on public.classes (cost=0.00..5108.60 rows=47960 width=96) (actual time=0.006..13.902 rows=38171 loops=3)
Output: classes.classid, classes.schoolid, classes.omittedColumnC, classes.omittedColumnD, classes.classname, classes.omittedColumnB, classes.omittedColumnE
Filter: ((classes.classname)::text = 'maths'::text)
Rows Removed by Filter: 60282
Worker 0: actual time=0.004..0.004 rows=0 loops=1
Worker 1: actual time=0.004..0.004 rows=0 loops=1
-> Hash (cost=747.56..747.56 rows=6927 width=16) (actual time=1.358..1.359 rows=2134 loops=1)
Output: classtoschool.classid, classtoschool.schoolid
Buckets: 8192 Batches: 1 Memory Usage: 165kB
-> Index Only Scan using classtoschool_schoolid_classid_idx on public.classtoschool (cost=0.42..747.56 rows=6927 width=16) (actual time=0.246..1.060 rows=2134 loops=1)
Output: classtoschool.classid, classtoschool.schoolid
Index Cond: (classtoschool.schoolid = '12345678'::bigint)
Heap Fetches: 504
Planning Time: 5.019 ms
Execution Time: 190.426 ms
Someone suggested I look at the pg_stat_user_tables where I found that the value of column idx_tup_fetch is vastly different in the database with the index (3 billion) vs without the index (300K).
I have 2 questions:
Why & how does the index impact the query execution even though EXPLAIN does not show it?
Why does the index not impact the query execution in other databases with more rows & data, but only impact this one database?
create extension btree_gist
CREATE INDEX poi_timestamp_midx ON spatio using gist(poi, timestamp);
explain analyze
select count(*) from spatio as a
where ST_DWithin(ST_GeomFromText('Point(30.391324 114.117508)',4326),a.poi,0.01)
and timestamp > 1645727066-2*86400
and timestamp < 1645727066+86400
;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Aggregate (cost=250.00..250.02 rows=1 width=8) (actual time=13.561..13.563 rows=1 loops=1) |
-> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=100000 width=8) (actual time=13.541..13.545 rows=32 loops=1) |
Task Count: 32 |
Tuple data received from nodes: 32 bytes |
Tasks Shown: One of 32 |
-> Task |
Tuple data received from node: 1 bytes |
Node: host=10.174.250.40 port=5432 dbname=postgres |
-> Aggregate (cost=33.30..33.31 rows=1 width=8) (actual time=0.087..0.087 rows=1 loops=1) |
-> Index Scan using poi_timestamp_midx_102039 on spatio_102039 a (cost=0.27..33.30 rows=1 width=0) (actual time=0.086..0.086 rows=0 loops=1) |
Index Cond: (poi && st_expand('0101000020E6100000D12346CF2D643E402D41464085875C40'::geometry, '0.01'::double precision)) |
Filter: (("timestamp" > 1645554266) AND ("timestamp" < 1645813466) AND st_dwithin('0101000020E6100000D12346CF2D643E402D41464085875C40'::geometry, poi, '0.01'::double precision))|
Planning Time: 0.133 ms |
Execution Time: 0.102 ms |
Planning Time: 1.730 ms |
Execution Time: 13.630 ms |`
I'm trying to figure out if my GIST index on my cube column for my table is working for my nearest neighbors query (metric = Euclidean). My cube values are 75 dimensional vectors.
Table:
\d+ reduced_features
Table "public.reduced_features"
Column | Type | Modifiers | Storage | Stats target | Description
----------+--------+-----------+---------+--------------+-------------
id | bigint | not null | plain | |
features | cube | not null | plain | |
Indexes:
"reduced_features_id_idx" UNIQUE, btree (id)
"reduced_features_features_idx" gist (features)
Here is my query:
explain analyze select id from reduced_features order by features <-> (select features from reduced_features where id = 198990) limit 10;
Results:
QUERY PLAN
---------------
Limit (cost=8.58..18.53 rows=10 width=16) (actual time=0.821..35.987 rows=10 loops=1)
InitPlan 1 (returns $0)
-> Index Scan using reduced_features_id_idx on reduced_features reduced_features_1 (cost=0.29..8.31 rows=1 width=608) (actual time=0.014..0.015 rows=1 loops=1)
Index Cond: (id = 198990)
-> Index Scan using reduced_features_features_idx on reduced_features (cost=0.28..36482.06 rows=36689 width=16) (actual time=0.819..35.984 rows=10 loops=1)
Order By: (features <-> $0)
Planning time: 0.117 ms
Execution time: 36.232 ms
I have 36689 total records in my table. Is my index working?
I have a query that is running too slowly.
select c.vm_name,
round(sum(bytes_sent)*1.8/power(10,9)) gb_sent,
round(sum(bytes_received)*1.8/power(10,9)) gb_received
from groups b,
vms c,
vm_ip_address_histories d,
ip_address_usage_histories e
where b.group_id = c.group_id
and c.vm_id = d.vm_id
and d.ip_address = e.ip_address
and e.datetime >= firstday()
and d.allocation_date <= last_day(sysdate()) and (d.deallocation_date is null or d.deallocation_date > last_day(sysdate()))
and b.customer_id = 29
group by c.vm_name
order by 1;
The function sysdate() returns the current system timestamp without a time zone, last_day() returns the timestamp that represents the last day of the month. I created these because Hibernate doesn't like the Postgres casting notation.
The issue is that the planner is doing full table scans where there are indexes in place. Here is the explain plan for the above query:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=1326387.13..1326391.38 rows=1698 width=24) (actual time=13221.041..13221.042 rows=7 loops=1)
Sort Key: c.vm_name
Sort Method: quicksort Memory: 25kB
-> HashAggregate (cost=1326236.61..1326296.04 rows=1698 width=24) (actual time=13221.008..13221.026 rows=7 loops=1)
Group Key: c.vm_name
-> Hash Join (cost=1309056.97..1325972.10 rows=35268 width=24) (actual time=13131.323..13211.612 rows=13631 loops=1)
Hash Cond: (d.ip_address = e.ip_address)
-> Nested Loop (cost=2.97..6942.24 rows=79 width=15) (actual time=0.249..56.904 rows=192 loops=1)
-> Hash Join (cost=2.69..41.02 rows=98 width=16) (actual time=0.066..0.638 rows=61 loops=1)
Hash Cond: (c.group_id = b.group_id)
-> Seq Scan on vms c (cost=0.00..30.98 rows=1698 width=24) (actual time=0.009..0.281 rows=1698 loops=1)
-> Hash (cost=2.65..2.65 rows=3 width=8) (actual time=0.014..0.014 rows=4 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on groups b (cost=0.00..2.65 rows=3 width=8) (actual time=0.004..0.011 rows=4 loops=1)
Filter: (customer_id = 29)
Rows Removed by Filter: 49
-> Index Scan using xif1vm_ip_address_histories on vm_ip_address_histories d (cost=0.29..70.34 rows=8 width=15) (actual time=0.011..0.921 rows=3 loops=61)
Index Cond: (vm_id = c.vm_id)
Filter: ((allocation_date <= last_day(sysdate())) AND ((deallocation_date IS NULL) OR (deallocation_date > last_day(sysdate()))))
Rows Removed by Filter: 84
-> Hash (cost=1280129.06..1280129.06 rows=1575435 width=23) (actual time=13130.223..13130.223 rows=203702 loops=1)
Buckets: 8192 Batches: 32 Memory Usage: 422kB
-> Seq Scan on ip_address_usage_histories e (cost=0.00..1280129.06 rows=1575435 width=23) (actual time=0.205..13002.776 rows=203702 loops=1)
Filter: (datetime >= firstday())
Rows Removed by Filter: 4522813
Planning time: 0.804 ms
Execution time: 13221.155 ms
(27 rows)
Notice that the planner is choosing to perform a very expensive full table scans on the largest tables - ip_address_usage_histories and vm_ip_address_histories. I have tried changing the configuration parameter enable_seqscan to off, but that made the problem worse, total execution time went to 63 seconds.
Here are the describes of the aforementioned tables:
Table "ip_address_usage_histories"
Column | Type | Modifiers
-----------------------------+-----------------------------+-----------
ip_address_usage_history_id | bigint | not null
datetime | timestamp without time zone | not null
ip_address | inet | not null
bytes_sent | bigint | not null
bytes_received | bigint | not null
Indexes:
"ip_address_usage_histories_pkey" PRIMARY KEY, btree (ip_address_usage_history_id)
"ip_address_usage_histories_datetime_ip_address_key" UNIQUE CONSTRAINT, btree (datetime, ip_address)
"uk_mit6tbiu8k62vdae4tmtnwb3f" UNIQUE CONSTRAINT, btree (datetime, ip_address)
Table "vm_ip_address_histories"
Column | Type | Modifiers
--------------------------+-----------------------------+--------------------------------------------------------------------------------------------
vm_ip_address_history_id | bigint | not null default nextval('vm_ip_address_histories_vm_ip_address_history_id_seq'::regclass)
ip_address | inet | not null
allocation_date | timestamp without time zone | not null
deallocation_date | timestamp without time zone |
vm_id | bigint | not null
Indexes:
"vm_ip_address_histories_pkey" PRIMARY KEY, btree (vm_ip_address_history_id)
"xie1vm_ip_address_histories" btree (replicate_date)
"xif1vm_ip_address_histories" btree (vm_id)
Foreign-key constraints:
"vm_ip_address_histories_vm_id_fkey" FOREIGN KEY (vm_id) REFERENCES vms(vm_id) ON DELETE RESTRICT
It appears that Postgres does not have query hints to direct the planner. I also tried the from clause inner join ... on ... syntax, but that did not improve things either.
Update 1
create or replace function firstday() returns timestamp without time zone as $$
begin
return date_trunc('month',now()::timestamp without time zone)::timestamp without time zone;
end; $$
language plpgsql;
I have not tried to replace this function with a standard function because Postgres doesn't have a function that returns the first day of the month to my knowledge.
The following was embedded in the question, but it reads as an answer.
After changing the all of my functions to immutable, the query now runs in 200ms! All the right things are happening.
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=51865.24..51914.88 rows=1103 width=24) (actual time=178.793..188.223 rows=7 loops=1)
Group Key: c.vm_name
-> Sort (cost=51865.24..51868.00 rows=1103 width=24) (actual time=178.517..180.541 rows=13823 loops=1)
Sort Key: c.vm_name
Sort Method: quicksort Memory: 1464kB
-> Hash Join (cost=50289.49..51809.50 rows=1103 width=24) (actual time=131.278..155.971 rows=13823 loops=1)
Hash Cond: (d.ip_address = e.ip_address)
-> Nested Loop (cost=2.97..272.36 rows=23 width=15) (actual time=0.149..2.310 rows=192 loops=1)
-> Hash Join (cost=2.69..41.02 rows=98 width=16) (actual time=0.046..0.590 rows=61 loops=1)
Hash Cond: (c.group_id = b.group_id)
-> Seq Scan on vms c (cost=0.00..30.98 rows=1698 width=24) (actual time=0.006..0.250 rows=1698 loops=1)
-> Hash (cost=2.65..2.65 rows=3 width=8) (actual time=0.014..0.014 rows=4 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on groups b (cost=0.00..2.65 rows=3 width=8) (actual time=0.004..0.012 rows=4 loops=1)
Filter: (customer_id = 29)
Rows Removed by Filter: 49
-> Index Scan using xif1vm_ip_address_histories on vm_ip_address_histories d (cost=0.29..2.34 rows=2 width=15) (actual time=0.002..0.027 rows=3 loops=61)
Index Cond: (vm_id = c.vm_id)
Filter: ((allocation_date <= '2015-03-31 00:00:00'::timestamp without time zone) AND ((deallocation_date IS NULL) OR (deallocation_date > '2015-03-31 00:00:00'::timestamp without time zone)))
Rows Removed by Filter: 84
-> Hash (cost=46621.83..46621.83 rows=199575 width=23) (actual time=130.762..130.762 rows=206266 loops=1)
Buckets: 8192 Batches: 4 Memory Usage: 2818kB
-> Bitmap Heap Scan on ip_address_usage_histories e (cost=4627.14..46621.83 rows=199575 width=23) (actual time=18.335..69.763 rows=206266 loops=1)
Recheck Cond: (datetime >= '2015-03-01 00:00:00'::timestamp without time zone)
Heap Blocks: exact=3684
-> Bitmap Index Scan on uk_mit6tbiu8k62vdae4tmtnwb3f (cost=0.00..4577.24 rows=199575 width=0) (actual time=17.797..17.797 rows=206935 loops=1)
Index Cond: (datetime >= '2015-03-01 00:00:00'::timestamp without time zone)
Planning time: 0.837 ms
Execution time: 188.301 ms
(29 rows)
I now see the planner is executing the functions, and using their values to insert into the where clause, which causes the indexes to be used.
I have a large table (star catalog), of which I have a subset. I implement the subset
as a union of two tables, where I make use of a cross index.
The issue is that a query from the view doesn't seem to be using the index, the time takes the same as a scan through the table.
A query against the large table goes quickly:
select count(*) from ucac4 where rnm in (select ucac4_rnm from grid_catalog limit 5);
count
-------
5
(1 row)
Time: 12.132 ms
A query against the view does not go quickly, even though I would expect it to.
select count(*) from grid_catalog_view where ident in (select ucac4_rnm from grid_catalog limit 5);
count
-------
5
(1 row)
Time: 1056237.045 ms
An explain of this query yeilds:
Aggregate (cost=23175810.51..23175810.52 rows=1 width=0)
-> Hash Join (cost=23081888.41..23172893.67 rows=1166734 width=0)
Hash Cond: (ucac4.rnm = public.grid_catalog.ucac4_rnm)
-> Unique (cost=23081888.17..23140224.87 rows=2333468 width=44)
-> Sort (cost=23081888.17..23087721.84 rows=2333468 width=44)
Sort Key: ucac4.ra, ucac4."dec", ucac4.pmrac, ucac4.pmdc, ucac4.rnm, ucac4.nest4, ucac4.nest6, ucac4.nest7, public.grid_catalog.subset
-> Append (cost=63349.87..22763295.24 rows=2333468 width=44)
-> Hash Join (cost=63349.87..22738772.75 rows=2333467 width=44)
Hash Cond: (ucac4.rnm = public.grid_catalog.ucac4_rnm)
-> Seq Scan on ucac4 (cost=0.00..16394129.04 rows=455124304 width=40)
-> Hash (cost=34048.69..34048.69 rows=2344094 width=8)
-> Seq Scan on grid_catalog (cost=0.00..34048.69 rows=2344094 width=8)
Filter: (petrov_prikey IS NULL)
-> Hash Join (cost=415.51..1187.80 rows=1 width=36)
Hash Cond: (petrov.prikey = public.grid_catalog.petrov_prikey)
-> Seq Scan on petrov (cost=0.00..709.15 rows=7215 width=32)
-> Hash (cost=282.08..282.08 rows=10675 width=8)
-> Index Scan using grid_catalog_petrov_prikey_idx on grid_catalog (cost=0.00..282.08 row
s=10675 width=8)
-> Hash (cost=0.18..0.18 rows=5 width=4)
-> HashAggregate (cost=0.13..0.18 rows=5 width=4)
-> Limit (cost=0.00..0.07 rows=5 width=4)
-> Seq Scan on grid_catalog (cost=0.00..34048.69 rows=2354769 width=4)
(22 rows)
The explain analyze (request in a comment) is:
Aggregate (cost=23175810.51..23175810.52 rows=1 width=0) (actual time=1625067.627..1625067.628 rows=1 loops=1)
-> Hash Join (cost=23081888.41..23172893.67 rows=1166734 width=0) (actual time=1621395.200..1625067.618 rows=5 loops=1)
Hash Cond: (ucac4.rnm = public.grid_catalog.ucac4_rnm)
-> Unique (cost=23081888.17..23140224.87 rows=2333468 width=44) (actual time=1620897.932..1624102.849 rows=1597359 loops
=1)
-> Sort (cost=23081888.17..23087721.84 rows=2333468 width=44) (actual time=1620897.928..1622191.358 rows=1597359 l
oops=1)
Sort Key: ucac4.ra, ucac4."dec", ucac4.pmrac, ucac4.pmdc, ucac4.rnm, ucac4.nest4, ucac4.nest6, ucac4.nest7, pu
blic.grid_catalog.subset
Sort Method: external merge Disk: 87536kB
-> Append (cost=63349.87..22763295.24 rows=2333468 width=44) (actual time=890293.619..1613769.160 rows=15973
59 loops=1)
-> Hash Join (cost=63349.87..22738772.75 rows=2333467 width=44) (actual time=890293.617..1611550.313 r
ows=1590144 loops=1)
Hash Cond: (ucac4.rnm = public.grid_catalog.ucac4_rnm)
-> Seq Scan on ucac4 (cost=0.00..16394129.04 rows=455124304 width=40) (actual time=886086.630..1
359934.589 rows=113780093 loops=1)
-> Hash (cost=34048.69..34048.69 rows=2344094 width=8) (actual time=4203.785..4203.785 rows=1590
144 loops=1)
-> Seq Scan on grid_catalog (cost=0.00..34048.69 rows=2344094 width=8) (actual time=0.014.
.2813.031 rows=1590144 loops=1)
Filter: (petrov_prikey IS NULL)
-> Hash Join (cost=415.51..1187.80 rows=1 width=36) (actual time=101.604..165.749 rows=7215 loops=1)
Hash Cond: (petrov.prikey = public.grid_catalog.petrov_prikey)
-> Seq Scan on petrov (cost=0.00..709.15 rows=7215 width=32) (actual time=58.280..108.043 rows=7
215 loops=1)
-> Hash (cost=282.08..282.08 rows=10675 width=8) (actual time=43.276..43.276 rows=7215 loops=1)
-> Index Scan using grid_catalog_petrov_prikey_idx on grid_catalog (cost=0.00..282.08 rows
=10675 width=8) (actual time=19.387..37.533 rows=7215 loops=1)
-> Hash (cost=0.18..0.18 rows=5 width=4) (actual time=0.035..0.035 rows=5 loops=1)
-> HashAggregate (cost=0.13..0.18 rows=5 width=4) (actual time=0.026..0.030 rows=5 loops=1)
-> Limit (cost=0.00..0.07 rows=5 width=4) (actual time=0.009..0.017 rows=5 loops=1)
-> Seq Scan on grid_catalog (cost=0.00..34048.69 rows=2354769 width=4) (actual time=0.007..0.009 rows=
5 loops=1)
Total runtime: 1625108.504 ms
(24 rows)
Time: 1625466.830 ms
To see the time to scan through the view:
select count(*) from grid_catalog_view;
count
---------
1597359
(1 row)
Time: 1033732.786 ms
My view is defined as:
PS1=# \d grid_catalog_view
View "public.grid_catalog_view"
Column | Type | Modifiers
--------+------------------+-----------
ra | double precision |
dec | double precision |
pmrac | integer |
pmdc | integer |
ident | integer |
nest4 | integer |
nest6 | integer |
nest7 | integer |
subset | integer |
View definition:
SELECT ucac4.ra, ucac4."dec", ucac4.pmrac, ucac4.pmdc, ucac4.rnm AS ident, ucac4.nest4, ucac4.nest6, ucac4.nest7, grid_catalog.subset
FROM ucac4, grid_catalog
WHERE ucac4.rnm = grid_catalog.ucac4_rnm AND grid_catalog.petrov_prikey IS NULL
UNION
SELECT petrov.ra, petrov."dec", 0 AS pmrac, 0 AS pmdc, grid_catalog.petrov_prikey AS ident, petrov.nest4, petrov.nest6, petrov.nest7, grid_catalog.subset
FROM petrov, grid_catalog
WHERE petrov.prikey = grid_catalog.petrov_prikey AND grid_catalog.ucac4_rnm IS NULL;
The large table is defined as:
PS1=# \d ucac4
Table "public.ucac4"
Column | Type | Modifiers
----------+------------------+-----------
radi | bigint |
spdi | bigint |
magm | smallint |
maga | smallint |
sigmag | smallint |
objt | smallint |
cdf | smallint |
... deleted entries not of relavance ...
ra | double precision |
dec | double precision |
x | double precision |
y | double precision |
z | double precision |
nest4 | integer |
nest6 | integer |
nest7 | integer |
Indexes:
"ucac4_pkey" PRIMARY KEY, btree (rnm)
"q3c_ucac4_idx" btree (q3c_ang2ipix(ra, "dec")) CLUSTER
"ucac4_nest4_idx" btree (nest4)
"ucac4_nest6_idx" btree (nest6)
"ucac4_nest7_idx" btree (nest7)
Referenced by:
TABLE "grid_catalog" CONSTRAINT "grid_catalog_ucac4_rnm_fkey" FOREIGN KEY (ucac4_rnm) REFERENCES ucac4(rnm)
Any idea why my index doesn't seem to be used?
As far as I can see it's a limitation in postgres - it's hard to make it avoid scanning the whole table on a union in this way.
See:
https://www.postgresql-archive.org/Poor-plan-when-joining-against-a-union-containing-a-join-td5747690.html
and
https://www.postgresql-archive.org/Pushing-IN-subquery-down-through-UNION-ALL-td3398684.html
and also maybe related
https://dba.stackexchange.com/questions/47572/in-postgresql-9-3-union-view-with-where-clause-not-taken-into-account
Basically - I guess you need to revisit your view definition! Sorry for no definitive solution.