April 5th, 2015 by depesz | Tags: , , , , , , | 1 comment »
Did it help? If yes - maybe you can help me?

On 26th of March, Heikki Linnakangas committed patch:

Add support for index-only scans in GiST.
 
This adds a new GiST opclass method, 'fetch', which is used to reconstruct
the original Datum from the value stored in the index. Also, the 'canreturn'
index AM interface function gains a new 'attno' argument. That makes it
possible to use index-only scans on a multi-column index where some of the
opclasses support index-only scans but some do not.
 
This patch adds support in the box and point opclasses. Other opclasses
can added later as follow-on patches (btree_gist would be particularly
interesting).
 
Anastasia Lubennikova, with additional fixes and modifications by me.

After this commit there was also a bunch of others that add necessary logic so that index only gist scans can be used on other datatypes too, but since I'm just writing about new functionality, I figured I can use points:

$ create table test (id serial primary key, some_point point);
$ insert into test (some_point) select point(random() * 5000, random() * 5000) from generate_series(1,100000) i;
$ create index tst_idx on test using gist (some_point);

And now, let's try to use index only scan to find 10 points nearest to given location:

$ explain analyze select some_point, some_point <-> point(500,500) from test order by some_point <-> point(500,500) limit 10;
                                                            QUERY PLAN                                                             
-----------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..1.09 rows=10 width=16) (actual time=0.169..0.284 rows=10 loops=1)
   ->  Index Only Scan using tst_idx on test  (cost=0.28..8136.28 rows=100000 width=16) (actual time=0.168..0.280 rows=10 loops=1)
         Order By: (some_point <-> '(500,500)'::point)
         Heap Fetches: 10
 Planning time: 0.129 ms
 Execution time: 0.332 ms
(6 rows)

Of course, for sanity checking, the values:

$ select some_point, some_point <-> point(500,500) from test order by some_point <-> point(500,500) limit 10;
             some_point              |     ?column?     
-------------------------------------+------------------
 (496.12135393545,491.772019304335)  | 9.09634880720175
 (479.593751952052,492.762844078243) | 21.6515908244684
 (478.387083858252,508.600422181189) | 23.2612425688083
 (476.656502578408,504.556254018098) | 23.7839929900202
 (494.28480444476,526.699291076511)  | 27.3041316328299
 (474.246060475707,515.573592856526) | 30.0965478997474
 (490.935954730958,529.630510136485) |  30.985868514334
 (530.101526528597,485.602258704603) |  33.367601858105
 (466.621380764991,489.233953412622) | 35.0719258261832
 (526.196013670415,523.584464099258) | 35.2485188209342
(10 rows)

Of course, if I wanted to add id column, it would switch to normal index scan, because index doesn't contain values for id column:

$ explain analyze select id, some_point, some_point <-> point(500,500) from test order by some_point <-> point(500,500) limit 10;
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..1.09 rows=10 width=20) (actual time=0.128..0.205 rows=10 loops=1)
   ->  Index Scan using tst_idx on test  (cost=0.28..8136.28 rows=100000 width=20) (actual time=0.126..0.200 rows=10 loops=1)
         Order By: (some_point <-> '(500,500)'::point)
 Planning time: 0.168 ms
 Execution time: 0.254 ms
(5 rows)

Which, of course, can be fixed:

$ drop index tst_idx ;
$ create extension btree_gist ;
$ create index tst_idx on test using gist (some_point, id);

And now:

$ explain analyze select id, some_point, some_point <-> point(500,500) from test order by some_point <-> point(500,500) limit 10;
                                                            QUERY PLAN                                                             
-----------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..1.17 rows=10 width=20) (actual time=0.066..0.107 rows=10 loops=1)
   ->  Index Only Scan using tst_idx on test  (cost=0.28..8908.28 rows=100000 width=20) (actual time=0.065..0.106 rows=10 loops=1)
         Order By: (some_point <-> '(500,500)'::point)
         Heap Fetches: 10
 Planning time: 0.114 ms
 Execution time: 0.130 ms
(6 rows)

Nice. That's definitely going to be useful. Thanks Anastasia and Heikki.

  1. One comment

  2. # Andreas
    Apr 6, 2015

    Heikki, with some help from me, added index-only scan support for many of the btree_gist types and ranges and inet/cidr.

Leave a comment