Postgres выбрать по префиксу в течение определенного периода, а затем отсортировать .. низкая производительность - PullRequest
0 голосов
/ 17 октября 2018

Я хочу выбрать записи, у которых геохэш (строка) начинается с b в течение определенного периода и упорядочить результаты по количеству фотографий, но это медленно.

Таблица:

create table test (
    geohash varchar(20),
    num_pics integer,
    dt date,
    body varchar(1000)
)

Фиктивные данные (запуск 5 раз для вставки 10-метровых записей)

insert into test 
select g, v, d, b from (
    select generate_series(1, 2000000) as id, 
    left(md5(random()::text),9) as g, 
    floor(random() * 100000 + 1)::int as v, 
    timestamp '2014-01-10 20:00:00' + random() * (timestamp '2020-01-20 20:00:00' - timestamp '2014-01-10 10:00:00') as d,
    md5(random()::text) as b) a

Плюс 1 м. Данные с geohash начинаются с b

insert into test 
select g, v, d, b from (
    select generate_series(1, 1000000) as id, 
    'b' || left(md5(random()::text),9) as g, 
    floor(random() * 100000 + 1)::int as v, 
    timestamp '2014-01-10 20:00:00' + random() * (timestamp '2020-01-20 20:00:00' - timestamp '2014-01-10 10:00:00') as d,
    md5(random()::text) as b) a    

Индекс

create index idx on test(geohash, dt desc , num_pics desc)

Мой запрос

explain analyze 
  select * 
  from test 
  where geohash like 'b%' 
    and dt between timestamp '2014-02-21 00:00:00' 
               and timestamp '2014-02-22 00:00:00' 
  order by num_pics desc limit 1000

План запроса (https://explain.depesz.com/s/XNZ)

'Limit  (cost=75956.07..75958.10 rows=814 width=51) (actual time=1743.841..1744.141 rows=1000 loops=1)'
'  ->  Sort  (cost=75956.07..75958.10 rows=814 width=51) (actual time=1743.839..1744.019 rows=1000 loops=1)'
'        Sort Key: num_pics DESC'
'        Sort Method: quicksort  Memory: 254kB'
'        ->  Index Scan using idx on test  (cost=0.56..75916.71 rows=814 width=51) (actual time=2.943..1741.071 rows=1464 loops=1)'
'              Index Cond: (((geohash)::text >= 'b'::text) AND ((geohash)::text < 'c'::text) AND (dt >= '2014-02-21 00:00:00'::timestamp without time zone) AND (dt <= '2014-02-22 00:00:00'::timestamp without time zone))'
'              Filter: ((geohash)::text ~~ 'b%'::text)'
'Planning time: 279.249 ms'
'Execution time: 1744.194 ms'

Вопрос :

Похоже, что это такудар по индексу, но производительность по-прежнему низкая. Это проблема Filter: 'b%'? Если он был переведен в geohash >= 'b' and geohash <'c' в оптимизаторе, то почему он должен фильтровать его снова?

Кроме того, это так?правильный способ использовать многостолбцовый индекс дерева B? Потому что я прочитал, что лучше использовать оператор equality(=) в первом индексированном столбце, вместо оператора range в этом случае.

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Если вы всегда хотите спросить после первого знака, лучше создать индекс для этого не общего индекса;

 create index on test using btree(substr(geohash,1,1)); 
 create index on test using btree(dt desc); analyze test;

  explain analyze 
  select 
  from test 
  where substr(geohash,1,1) ='b' 
    and dt between timestamp '2014-02-21 00:00:00' and timestamp '2014-02-22 00:00:00' 
  order by num_pics desc limit 1000

План выполнения

 Limit  (cost=15057.49..15059.14 rows=660 width=4) (actual time=29.433..29.644 rows=1000 loops=1)
  ->  Sort  (cost=15057.49..15059.14 rows=660 width=4) (actual time=29.431..29.564 rows=1000 loops=1)
        Sort Key: num_pics DESC
        Sort Method: quicksort  Memory: 117kB
        ->  Bitmap Heap Scan on test  (cost=96.93..15026.58 rows=660 width=4) (actual time=10.782..28.708 rows=1469 loops=1)
              Recheck Cond: ((dt >= '2014-02-21 00:00:00'::timestamp without time zone) AND (dt <= '2014-02-22 00:00:00'::timestamp without time zone))
              Filter: (substr((geohash)::text, 1, 1) = 'b'::text)
              Rows Removed by Filter: 8470
              Heap Blocks: exact=9481
              ->  Bitmap Index Scan on test_dt_idx  (cost=0.00..96.77 rows=4433 width=0) (actual time=5.541..5.541 rows=9939 loops=1)
                    Index Cond: ((dt >= '2014-02-21 00:00:00'::timestamp without time zone) AND (dt <= '2014-02-22 00:00:00'::timestamp without time zone))
Planning time: 0.325 ms
Execution time: 30.065 ms

С составным индексом этоеще лучше

create index on test using btree(substr(geohash,1,1), dt desc); 

Limit  (cost=2546.25..2547.90 rows=660 width=51) (actual time=6.188..6.679 rows=1000 loops=1)
  ->  Sort  (cost=2546.25..2547.90 rows=660 width=51) (actual time=6.186..6.528 rows=1000 loops=1)
        Sort Key: num_pics DESC
        Sort Method: quicksort  Memory: 255kB
        ->  Bitmap Heap Scan on test  (cost=16.85..2515.34 rows=660 width=51) (actual time=1.896..4.740 rows=1469 loops=1)
              Recheck Cond: ((substr((geohash)::text, 1, 1) = 'b'::text) AND (dt >= '2014-02-21 00:00:00'::timestamp without time zone) AND (dt <= '2014-02-22 00:00:00'::timestamp without time zone))
              Heap Blocks: exact=1430
              ->  Bitmap Index Scan on test_substr_dt_idx  (cost=0.00..16.68 rows=660 width=0) (actual time=1.266..1.266 rows=1469 loops=1)
                    Index Cond: ((substr((geohash)::text, 1, 1) = 'b'::text) AND (dt >= '2014-02-21 00:00:00'::timestamp without time zone) AND (dt <= '2014-02-22 00:00:00'::timestamp without time zone))
Planning time: 0.389 ms
Execution time: 7.052 ms
0 голосов
/ 17 октября 2018

Это всего лишь предположение, поскольку я его не проверял.Запрос «доступ» выполняется по неправильному столбцу.

Правило большого пальца:

  1. Доступ к по наиболее селективному столбцу.
  2. Фильтр с использованием менее селективного столбца.

В этом случае geohash не очень избирателен, поскольку шаблон имеет только одну букву.Если бы он имел больше, скажем, 3 или более букв, то он был бы более избирательным.Избирательность: одна буква из 26 (может быть, только 16?) Равна 1 / 26 = 3.84%.Скорее плохо.

Кажется, что dt в этом случае более избирателен, поскольку охватывает один день (из 2000 дней?).Селективность: 1 / 2000 = 0.05%.Гораздо лучше.

Попробуйте следующий индекс, чтобы увидеть, если вы получите более быстрое время выполнения:

create index idx2 on test(dt, geohash, num_pics);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...