Skip to content

Instantly share code, notes, and snippets.

@ariejan
Last active December 12, 2015 10:09
Show Gist options
  • Save ariejan/4756958 to your computer and use it in GitHub Desktop.
Save ariejan/4756958 to your computer and use it in GitHub Desktop.

Consider the following query

SELECT "orders".* FROM "orders" 
WHERE "orders"."bid" = 't' 
AND "orders"."region_id" = 10000002 
AND "orders"."type_id" = 30259 
ORDER BY price DESC
LIMIT 1

From a table with about 2.5million rows, it returns this one result in about 1075ms.

Running the same query, but with a LIMIT 2 increased the speed of the query to about 12ms.

Here are the EXPLAIN ANALYZE for both queries, LIMIT 1 first:

Limit  (cost=0.00..4187.72 rows=1 width=140) (actual time=1062.737..1062.737 rows=1 loops=1)
  ->  Index Scan Backward using index_orders_on_price on orders  (cost=0.00..6662655.21 rows=1591 width=140) (actual time=1062.736..1062.736 rows=1 loops=1)
        Filter: (bid AND (region_id = 10000002) AND (type_id = 30259))
        Rows Removed by Filter: 598168

And for LIMIT 2:

Limit  (cost=5288.02..5288.02 rows=2 width=140) (actual time=0.922..0.922 rows=2 loops=1)
  ->  Sort  (cost=5288.02..5291.99 rows=1591 width=140) (actual time=0.922..0.922 rows=2 loops=1)
        Sort Key: price
        Sort Method: top-N heapsort  Memory: 25kB
        ->  Index Scan using index_orders_on_bid_and_region_id_and_type_id on orders  (cost=0.00..5272.11 rows=1591 width=140) (actual time=0.044..0.673 rows=1429 loops=1)
              Index Cond: ((bid = true) AND (region_id = 10000002) AND (type_id = 30259))
              Filter: bid

Clearly, using the LIMIT 2 makes Postgres decide to use a sorting algoritm and use the index_orders_on_bid_and_region_id_and_type_id index. LIMIT 1 does no such thing.

So, what's happening here? Why does Postgres make this bad query plan decision and how do I beat Postgres back into submission by running that LIMIT 1 query fast as well?

Some more table info:

CREATE TABLE orders
(
  id serial NOT NULL,
  result_type character varying(8),
  price numeric(19,2),
  range integer,
  vol_entered bigint,
  vol_remaining bigint,
  min_volume bigint,
  bid boolean,
  issued_at timestamp without time zone,
  duration integer,
  type_id integer,
  order_id bigint,
  station_id integer,
  solar_system_id integer,
  region_id integer,
  emdr_hash character varying(40),
  generated_at timestamp without time zone,
  created_at timestamp without time zone,
  CONSTRAINT orders_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

Indices:

CREATE INDEX index_orders_on_bid
  ON orders
  USING btree
  (bid);

CREATE INDEX index_orders_on_bid_and_region_id_and_type_id
  ON orders
  USING btree
  (bid, region_id, type_id);

CREATE INDEX index_orders_on_generated_at
  ON orders
  USING btree
  (generated_at);

CREATE INDEX index_orders_on_price
  ON orders
  USING btree
  (price);

CREATE INDEX index_orders_on_solar_system_id
  ON orders
  USING btree
  (solar_system_id);

CREATE INDEX index_orders_on_type_id
  ON orders
  USING btree
  (type_id);

I also ran VACUUM ANALYZE FULL on the table.

Noteworth might be that if I change bid to false in the query, I get slow queries upto LIMIT 4, where LIMIT 5 is using the composite key again - and thus fast.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment