On Monday 25 July 2005 15:43, Michael Fuhr pondered: > Whatever the results of your experiments, could you post the settings > you tried and the corresponding EXPLAIN ANALYZE outputs? I did lots of tests now that you pointed me to a useful guide, also taking what's in the documentation into account. In the attached file I have documented my results. There are three sections to the file, each separated by '====' markers. The first section deals in detail with the EXPLAIN ANALYZE info relating to the troublesome queries. The second is probably of least interest, just showing that I could implement my problem differently to improve performance. But the last section is the most important, where I varied effective_cache_size, random_page_cost, shared_buffers and cpu_tuple_costs, each on its own with the other ones assuming default values(unless indicated). To summarise, increasing effective_cache_size and decreasing random_page_cost both yield in lower index scan cost estimates while not changing the seqscan ones. As expected, increasing shared_buffers makes no difference whatsoever in the query cost estimates or the actual query times. A higher cpu_tuple cost penalises the seqscans significantly while only slightly increasing the index scan estimates. Also note that these are all related to the query planner only, they do NOT change the actual query time which explains why I did not include EXPLAIN ANALYZE outputs, only plain EXPLAIN ones. In order to make PostgreSQL choose the index scans when I need them (other than by setting enable_seq_scans to off), I ended up choosing effective_cache_size 40000 random_page_cost 2.5 cpu_tuple_cost 0.08 as only a combination yielded the desired results. Hardly optimal, but the real problem seems to lie with the correlation of the indexed columns (see other post in this thread). If I encounter trouble with these somewhere down the line, I'll post again. Hope this helps someone out there. -- Kilian Hagemann Climate Systems Analysis Group University of Cape Town Republic of South Africa Tel(w): ++27 21 650 2748
It all began with the most basic of queries on a clean database (first batch 5min data only, vacuum full analyze'd), namely station_data=# EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------- Seq Scan on speed (cost=0.00..242637.38 rows=1073843 width=8) (actual time=98080.848..104617.800 rows=1094400 loops=1) Filter: (set_id = 25::smallint) Total runtime: 109957.981 ms (3 rows) which chooses a seqscan by default . Disabling seqscan manually causes an index scan strategy which takes only ~ 12 sec: station_data=# EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Index Scan using speed_pkey on speed (cost=0.00..3194177.02 rows=1073843 width=8) (actual time=90.544..6881.291 rows=1094400 loops=1) Index Cond: (set_id = 25::smallint) Total runtime: 12243.179 ms (3 rows) Making the query a bit more complex yields the same picture(default configuration is 9x slower): seqscans disabled: station_data=# EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint AND rec_time >= '1999/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- Index Scan using speed_pkey on speed (cost=0.00..1932444.35 rows=649115 width=8) (actual time=0.169..6520.960 rows=652345 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) Total runtime: 11664.710 ms (3 rows) default (seqscans enabled): station_data=# EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint AND rec_time >= '1999/01/01'; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- Seq Scan on speed (cost=0.00..277537.25 rows=649115 width=8) (actual time=94546.374..98789.401 rows=652345 loops=1) Filter: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) Total runtime: 101833.815 ms (3 rows) Only when adding more conditions does the index scan seem attractive for the planner, and it chooses the index no matter what: station_data=# EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint AND rec_time BETWEEN '1999/01/01'AND '2000/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using speed_pkey on speed (cost=0.00..311175.90 rows=104431 width=8) (actual time=83.423..821.657 rows=105121 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) Total runtime: 1310.723 ms (3 rows) Now we'll use the same systematic approach, just with the combined speed/direction queries of the type we're after: station_data=# EXPLAIN SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE station_data-# s.set_id = 25::smallint AND d.set_id = 26::smallint station_data-# AND s.rec_time=d.rec_time; QUERY PLAN ---------------------------------------------------------------------------------- Merge Join (cost=776310.23..806022.76 rows=1623364 width=8) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=388154.12..390838.72 rows=1073843 width=6) Sort Key: d.rec_time -> Seq Scan on direction d (cost=0.00..242635.38 rows=1073843 width=6) Filter: (set_id = 26::smallint) -> Sort (cost=388156.12..390840.72 rows=1073843 width=6) Sort Key: s.rec_time -> Seq Scan on speed s (cost=0.00..242637.38 rows=1073843 width=6) Filter: (set_id = 25::smallint) (10 rows) station_data=# SET enable_seqscan TO OFF; station_data=# EXPLAIN ANALYZE SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE station_data-# s.set_id = 25::smallint AND d.set_id = 26::smallint station_data-# AND s.rec_time=d.rec_time; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Join (cost=6679390.14..6709102.67 rows=1623364 width=8) (actual time=58160.045..87179.399 rows=1094400 loops=1) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=3339694.38..3342378.99 rows=1073843 width=6) (actual time=33347.556..38652.736 rows=1094400 loops=1) Sort Key: d.rec_time -> Index Scan using direction_pkey on direction d (cost=0.00..3194175.64 rows=1073843 width=6) (actual time=112.973..16233.524 rows=1094400 loops=1) Index Cond: (set_id = 26::smallint) -> Sort (cost=3339695.77..3342380.37 rows=1073843 width=6) (actual time=24812.447..29836.246 rows=1094400 loops=1) Sort Key: s.rec_time -> Index Scan using speed_pkey on speed s (cost=0.00..3194177.02 rows=1073843 width=6) (actual time=80.720..13457.008 rows=1094400 loops=1) Index Cond: (set_id = 25::smallint) Total runtime: 81613.626 ms (11 rows) Next level of complexity: station_data=# EXPLAIN SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE station_data-# s.set_id = 25::smallint AND d.set_id = 26::smallint station_data-# AND s.rec_time=d.rec_time AND s.rec_time>='1999/01/01'; QUERY PLAN ----------------------------------------------------------------------------------------------------- Merge Join (cost=749717.30..768142.12 rows=981289 width=8) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=361563.18..363185.97 rows=649115 width=6) Sort Key: s.rec_time -> Seq Scan on speed s (cost=0.00..277537.25 rows=649115 width=6) Filter: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) -> Sort (cost=388154.12..390838.72 rows=1073843 width=6) Sort Key: d.rec_time -> Seq Scan on direction d (cost=0.00..242635.38 rows=1073843 width=6) Filter: (set_id = 26::smallint) (10 rows) station_data=# SET enable_seqscan TO OFF; SET station_data=# EXPLAIN ANALYZE SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE station_data-# s.set_id = 25::smallint AND d.set_id = 26::smallint station_data-# AND s.rec_time=d.rec_time AND s.rec_time>='1999/01/01'; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Join (cost=5356164.66..5374589.48 rows=981289 width=8) (actual time=38426.142..54574.311 rows=652345 loops=1) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=2016470.28..2018093.07 rows=649115 width=6) (actual time=13332.304..16505.141 rows=652345 loops=1) Sort Key: s.rec_time -> Index Scan using speed_pkey on speed s (cost=0.00..1932444.35 rows=649115 width=6) (actual time=15.370..4180.006 rows=652345 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) -> Sort (cost=3339694.38..3342378.99 rows=1073843 width=6) (actual time=20656.563..26500.827 rows=1094400 loops=1) Sort Key: d.rec_time -> Index Scan using direction_pkey on direction d (cost=0.00..3194175.64 rows=1073843 width=6) (actual time=44.112..9969.454 rows=1094400 loops=1) Index Cond: (set_id = 26::smallint) Total runtime: 57320.424 ms (11 rows) station_data=# SET enable_seqscan TO ON; SET station_data=# EXPLAIN SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE station_data-# s.set_id = 25::smallint AND d.set_id = 26::smallint station_data-# AND s.rec_time=d.rec_time AND s.rec_time BETWEEN '1999/01/01' AND '2000/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Join (cost=708919.40..716389.03 rows=157872 width=8) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=320765.28..321026.36 rows=104431 width=6) Sort Key: s.rec_time -> Index Scan using speed_pkey on speed s (cost=0.00..311175.90 rows=104431 width=6) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) -> Sort (cost=388154.12..390838.72 rows=1073843 width=6) Sort Key: d.rec_time -> Seq Scan on direction d (cost=0.00..242635.38 rows=1073843 width=6) Filter: (set_id = 26::smallint) (10 rows) station_data=# SET enable_seqscan TO OFF; SET EXPLAIN SELECT s.rec_time, wind_speed, wind_direction FROM speed s, direction d WHERE s.set_id = 25::smallint AND d.set_id = 26::smallint AND s.rec_time=d.rec_time AND s.rec_time BETWEEN '1999/01/01'AND '2000/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop (cost=0.00..1024658.27 rows=157872 width=8) (actual time=0.395..7387.471 rows=105121 loops=1) -> Index Scan using speed_pkey on speed s (cost=0.00..311175.90 rows=104431 width=6) (actual time=0.218..858.831 rows=105121 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) -> Index Scan using direction_pkey on direction d (cost=0.00..6.81 rows=2 width=6) (actual time=0.030..0.039 rows=1 loops=105121) Index Cond: ((d.set_id = 26::smallint) AND ("outer".rec_time = d.rec_time)) Total runtime: 7947.613 ms (6 rows) 5151.794 11198.190 9849.849 5148.811 5064.850 average 7.394 sec (2.5 times slower than wind_data query) An example of the performance of an inefficient search query (condition OUTSIDE the join): station_data=# EXPLAIN ANALYZE SELECT s.rec_time, wind_speed, wind_direction station_data-# FROM speed s JOIN direction d ON ( station_data(# s.rec_time=d.rec_time station_data(# AND station_data(# s.set_id=25::smallint station_data(# AND station_data(# d.set_id=26::smallint station_data(# ) station_data-# WHERE s.rec_time BETWEEN '1999/01/01' AND '2000/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Join (cost=709092.45..715560.75 rows=57718 width=8) (actual time=111723.482..114421.974 rows=105121 loops=1) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=320938.34..321199.55 rows=104487 width=6) (actual time=1599.714..2187.360 rows=105121 loops=1) Sort Key: s.rec_time -> Index Scan using speed_pkey on speed s (cost=0.00..311341.38 rows=104487 width=6) (actual time=0.151..553.042 rows=105121 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) -> Sort (cost=388154.12..390838.72 rows=1073843 width=6) (actual time=105542.478..108179.999 rows=547177 loops=1) Sort Key: d.rec_time -> Seq Scan on direction d (cost=0.00..242635.38 rows=1073843 width=6) (actual time=86182.694..94456.869 rows=1094400 loops=1) Filter: (set_id = 26::smallint) Total runtime: 114833.711 ms (11 rows) A desperate and final attempt (yields a different query plan, BUT chooses index scans by default!!!): station_data=# EXPLAIN ANALYZE SELECT s.rec_time, wind_speed, wind_direction FROM ( SELECT * FROM speed WHERE set_id=25::smallint AND speed.rec_time BETWEEN '1999/01/01'AND '2000/01/01' ) AS s JOIN ( SELECT * FROM direction WHERE set_id=26::smallint AND direction.rec_time BETWEEN '1999/01/01'AND '2000/01/01') AS d ON s.rec_time=d.rec_time; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Join (cost=627397.55..628565.24 rows=14679 width=8) (actual time=8532.723..10929.575 rows=105121 loops=1) Merge Cond: ("outer".rec_time = "inner".rec_time) -> Sort (cost=320765.28..321026.36 rows=104431 width=6) (actual time=4195.636..4643.069 rows=105121 loops=1) Sort Key: speed.rec_time -> Index Scan using speed_pkey on speed (cost=0.00..311175.90 rows=104431 width=6) (actual time=0.229..1890.093 rows=105121 loops=1) Index Cond: ((set_id = 25::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) -> Sort (cost=306632.27..306881.88 rows=99841 width=6) (actual time=4337.044..4845.900 rows=105121 loops=1) Sort Key: direction.rec_time -> Index Scan using direction_pkey on direction (cost=0.00..297500.03 rows=99841 width=6) (actual time=0.167..1857.420 rows=105121 loops=1) Index Cond: ((set_id = 26::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) Total runtime: 11350.495 ms (11 rows) 7583.417 6528.144 6288.434 12450.558 7546.104 average 8.624 sec (~3 times slower than combined wind_data) ====================================================================================================================================================================================================================================================================================================== for new wind_data table, had to cancel: UPDATE wind_data SET direction = d.wind_direction FROM wind_data w, direction d WHERE w.rec_time=d.rec_time AND w.set_id=d.set_id/2; It takes longer than 7 hours to complete, the update was hence abandoned altogether since hopes of optimisation are minimal (maybe through increased memory usage) Using the new database layout with only one table for speed and direction, we get station_data=# EXPLAIN SELECT rec_time, speed, direction FROM wind_data WHERE set_id=39::smallint; QUERY PLAN -------------------------------------------------------------------- Seq Scan on wind_data (cost=0.00..242671.38 rows=1073843 width=8) Filter: (set_id = 39::smallint) (2 rows) station_data=# SET enable_seqscan TO OFF; station_data=# EXPLAIN ANALYZE SELECT rec_time, speed, direction FROM wind_data WHERE set_id=39::smallint; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using wind_data_pkey on wind_data (cost=0.00..3230761.68 rows=1073843 width=8) (actual time=219.563..41598.457 rows=1094400 loops=1) Index Cond: (set_id = 39::smallint) Total runtime: 46862.019 ms (3 rows) station_data=# SET enable_seqscan TO ON; station_data=# EXPLAIN SELECT rec_time, speed, direction FROM wind_data WHERE set_id=39::smallint AND rec_time>='1999/01/01'; QUERY PLAN ----------------------------------------------------------------------------------------- Seq Scan on wind_data (cost=0.00..277571.25 rows=631929 width=8) Filter: ((set_id = 39::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) (2 rows) station_data=# SET enable_seqscan TO OFF; station_data=# EXPLAIN ANALYZE SELECT rec_time, speed, direction FROM wind_data WHERE set_id=39::smallint AND rec_time>='1999/01/01'; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using wind_data_pkey on wind_data (cost=0.00..1902808.95 rows=631929 width=8) (actual time=58.051..18961.633 rows=652345 loops=1) Index Cond: ((set_id = 39::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime)) Total runtime: 22351.893 ms (3 rows) Finally, with the most complex query the planner chooses a sensible plan again no matter what: station_data=# SET enable_seqscan TO ON; station_data=# EXPLAIN ANALYZE SELECT rec_time, speed, direction FROM wind_data WHERE set_id=39::smallint AND rec_time BETWEEN '1999/01/01'AND '2000/01/01'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using wind_data_pkey on wind_data (cost=0.00..302862.80 rows=100492 width=8) (actual time=0.218..2135.167 rows=105121 loops=1) Index Cond: ((set_id = 39::smallint) AND (rec_time >= '1999-01-01 00:00:00+02'::abstime) AND (rec_time <= '2000-01-01 00:00:00+02'::abstime)) Total runtime: 2646.776 ms (3 rows) 5636.407 1085.729 2764.653 1073.631 2589.617 average 2.802 sec (fastest of all) ======================================================================================================================================================================================= Tuning the configuration parameters(all testing performed on 7.4.8, but also seems applicable to 8.0.3): Base case query - EXPLAIN ANALYZE SELECT * FROM speed WHERE set_id=25::smallint AND rec_time >= '1999/01/01'; Chooses seqscan by default, estimate (cost=0.00..277537.25 rows=649115 width=8) Reference estimate of the index scan (cost=0.00..1932444.35 rows=649115 width=8) By tuning the following parameters, the index scan cost estimates become effective_cache_size (available disk cache for objects during queries, in 8KB blocks), default 1000(8MB): 10000 - 0.00..1677583.14 40000 - 0.00..867681.46 100000 - 0.00..218071.09 -> first time smaller than seqscan, but unrealistic memory requirement random_page_cost (cost of fetching disk page non-sequentially, in multiples of seq fetch cost), default 4: 3 - 0.00..1452747.53 2 - 0.00..973050.72 it shouldn't be allowed to dip below 2 (unrealistic) shared_buffers (shared memory), default 1000(8MB): 3000 - identical (no change) 5000 - identical cpu_tuple_cost - Sets the query planner's estimate of the cost of processing each row during a query. This is measured as a fraction of the cost of a sequential page fetch. The default is 0.01 0.005 - seq 0.00..207737.50, index 0.00..1929198.77 0.02 - seq 0.00..417136.75, index 0.00..1938935.50 0.05 - seq 0.00..835935.25, index 0.00..1958408.94 0.1 - seq 0.00..1533932.75, index 0.00..1990864.68 random_page_cost of 3: 0.005 - seq 0.00..207737.50, index 0.00..1449501.96 0.02 - seq 0.00..417136.75, index 0.00..1459238.68 0.05 - seq 0.00..835935.25, index 0.00..1478712.13 0.1 - seq 0.00..1533932.75, index 0.00..1511167.87 -> index better cpu_index_tuple_cost - Sets the query planner's estimate of the cost of processing each index row during an index scan. This is measured as a fraction of the cost of a sequential page fetch. The default is 0.001. 0.0005 - 0.00..1932119.79 0.0002 - 0.00..1931925.06 0.0001 - 0.00..1931860.14 0.00001 - 0.00..1931801.72 -> no major impact here... Final settings: effective_cache_size 40000 random_page_cost 2.5 cpu_tuple_cost 0.08
---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives? http://archives.postgresql.org