Я пишу приложение node.js, чтобы включить поиск по базе данных PostgreSQL. Чтобы включить опережающее ввод текста в поле поиска Twitter, мне нужно перебрать набор ключевых слов из базы данных, чтобы инициализировать Bloodhound перед загрузкой страницы. Это что-то вроде ниже:
SELECT distinct handlerid from lotintro where char_length(lotid)=7;
Так что для большого стола (лотинтро) это дорого; это также глупо, поскольку результат запроса, скорее всего, остается одинаковым для разных посетителей в течение определенного периода времени.
Каков правильный способ справиться с этим? Думаю несколько вариантов:
1) Поместите запрос в хранимую процедуру и вызовите ее из node.js:
SELECT * from getallhandlerid()
Означает ли это, что запрос будет скомпилирован, и база данных автоматически вернет те же наборы результатов без фактического запуска запроса, зная, что результат не изменится?
2) Или создать отдельную таблицу для хранения отдельных handlerid
и обновлять таблицу с помощью триггера, который запускается каждый день? (Я знаю, что в идеале триггер должен запускаться для каждой вставки/обновления таблицы, но это слишком дорого).
3) создайте частичный индекс, как было предложено. Вот что собралось:
Запрос
SELECT distinct handlerid from lotintro where length(lotid) = 7;
Показатель
CREATE INDEX lotid7_idx ON lotintro (handlerid)
WHERE length(lotid) = 7;
С индексом запрос стоит около 250 мс, попробуйте запустить
explain (analyze on, TIMING OFF) SELECT distinct handlerid from lotintro where length(lotid) = 7
"HashAggregate (cost=5542.64..5542.65 rows=1 width=6) (actual rows=151 loops=1)"
" -> Bitmap Heap Scan on lotintro (cost=39.08..5537.50 rows=2056 width=6) (actual rows=298350 loops=1)"
" Recheck Cond: (length(lotid) = 7)"
" Rows Removed by Index Recheck: 55285"
" -> Bitmap Index Scan on lotid7_idx (cost=0.00..38.57 rows=2056 width=0) (actual rows=298350 loops=1)"
"Total runtime: 243.686 ms"
Без индекса запрос стоит около 210 мс, попробуйте запустить
explain (analyze on, TIMING OFF) SELECT distinct handlerid from lotintro where length(lotid) = 7
"HashAggregate (cost=19490.11..19490.12 rows=1 width=6) (actual rows=151 loops=1)"
" -> Seq Scan on lotintro (cost=0.00..19484.97 rows=2056 width=6) (actual rows=298350 loops=1)"
" Filter: (length(lotid) = 7)"
" Rows Removed by Filter: 112915"
"Total runtime: 214.235 ms"
Что я здесь делаю неправильно?
4) Использование предложенного Алексиусом индекса и запроса:
create index on lotintro using btree(char_length(lotid), handlerid);
Но это не оптимальное решение. Поскольку существует всего несколько различных значений, вы можете использовать трюк, называемый свободным сканированием индекса, который в вашем случае должен работать намного быстрее:
explain (analyze on, BUFFERS on, TIMING OFF)
WITH RECURSIVE t AS (
(SELECT handlerid FROM lotintro WHERE char_length(lotid)=7 ORDER BY handlerid LIMIT 1) -- parentheses required
UNION ALL
SELECT (SELECT handlerid FROM lotintro WHERE char_length(lotid)=7 AND handlerid > t.handlerid ORDER BY handlerid LIMIT 1)
FROM t
WHERE t.handlerid IS NOT NULL
)
SELECT handlerid FROM t WHERE handlerid IS NOT NULL;
"CTE Scan on t (cost=444.52..446.54 rows=100 width=32) (actual rows=151 loops=1)"
" Filter: (handlerid IS NOT NULL)"
" Rows Removed by Filter: 1"
" Buffers: shared hit=608"
" CTE t"
" -> Recursive Union (cost=0.42..444.52 rows=101 width=32) (actual rows=152 loops=1)"
" Buffers: shared hit=608"
" -> Limit (cost=0.42..4.17 rows=1 width=6) (actual rows=1 loops=1)"
" Buffers: shared hit=4"
" -> Index Scan using lotid_btree on lotintro lotintro_1 (cost=0.42..7704.41 rows=2056 width=6) (actual rows=1 loops=1)"
" Index Cond: (char_length(lotid) = 7)"
" Buffers: shared hit=4"
" -> WorkTable Scan on t t_1 (cost=0.00..43.83 rows=10 width=32) (actual rows=1 loops=152)"
" Filter: (handlerid IS NOT NULL)"
" Rows Removed by Filter: 0"
" Buffers: shared hit=604"
" SubPlan 1"
" -> Limit (cost=0.42..4.36 rows=1 width=6) (actual rows=1 loops=151)"
" Buffers: shared hit=604"
" -> Index Scan using lotid_btree on lotintro (cost=0.42..2698.13 rows=685 width=6) (actual rows=1 loops=151)"
" Index Cond: ((char_length(lotid) = 7) AND (handlerid > t_1.handlerid))"
" Buffers: shared hit=604"
"Planning time: 1.574 ms"
**"Execution time: 25.476 ms"**
========= больше информации о БД ===========================
dataloggerDB=# \d lotintro Таблица "public.lotintro"
Column | Type | Modifiers
--------------+-----------------------------+--------------
lotstartdt | timestamp without time zone | not null
lotid | text | not null
ftc | text | not null
deviceid | text | not null
packageid | text | not null
testprogname | text | not null
testprogdir | text | not null
testgrade | text | not null
testgroup | text | not null
temperature | smallint | not null
testerid | text | not null
handlerid | text | not null
numofsite | text | not null
masknum | text |
soaktime | text |
xamsqty | smallint |
scd | text |
speedgrade | text |
loginid | text |
operatorid | text | not null
loadboardid | text | not null
checksum | text |
lotenddt | timestamp without time zone | not null
totaltest | integer | default (-1)
totalpass | integer | default (-1)
earnhour | real | default 0
avetesttime | real | default 0
Indexes:
"pkey_lotintro" PRIMARY KEY, btree (lotstartdt, testerid)
"lotid7_idx" btree (handlerid) WHERE length(lotid) = 7
your version of Postgres, [PostgreSQL 9.2] cardinalities (how many rows?), [411K rows for table lotintro] percentage for length(lotid) = 7. [298350/411000= 73%]
============= после переноса всего на PG 9.4 ======================
С индексом:
explain (analyze on, BUFFERS on, TIMING OFF) SELECT distinct handlerid from lotintro where length(lotid) = 7
"HashAggregate (cost=5542.78..5542.79 rows=1 width=6) (actual rows=151 loops=1)"
" Group Key: handlerid"
" Buffers: shared hit=14242"
" -> Bitmap Heap Scan on lotintro (cost=39.22..5537.64 rows=2056 width=6) (actual rows=298350 loops=1)"
" Recheck Cond: (length(lotid) = 7)"
" Heap Blocks: exact=13313"
" Buffers: shared hit=14242"
" -> Bitmap Index Scan on lotid7_idx (cost=0.00..38.70 rows=2056 width=0) (actual rows=298350 loops=1)"
" Buffers: shared hit=929"
"Planning time: 0.256 ms"
"Execution time: 154.657 ms"
Без индекса:
explain (analyze on, BUFFERS on, TIMING OFF) SELECT distinct handlerid from lotintro where length(lotid) = 7
"HashAggregate (cost=19490.11..19490.12 rows=1 width=6) (actual rows=151 loops=1)"
" Group Key: handlerid"
" Buffers: shared hit=13316"
" -> Seq Scan on lotintro (cost=0.00..19484.97 rows=2056 width=6) (actual rows=298350 loops=1)"
" Filter: (length(lotid) = 7)"
" Rows Removed by Filter: 112915"
" Buffers: shared hit=13316"
"Planning time: 0.168 ms"
"Execution time: 176.466 ms"
explain (analyze, timing)
. - person a_horse_with_no_name   schedule 10.05.2015EXPLAIN (ANALYZE, TIMING OFF)
должно быть лучшим (только без учета внутренних деталей). - person Erwin Brandstetter   schedule 10.05.2015\d lotintro
в psql), ваша версия Postgres, кардинальность (сколько строк?), процент дляlength(lotid) = 7
. В целях оптимизации выводEXPLAIN (BUFFERS, ANALYZE)
более полезен. Что произойдет, если вы повторите тест послеANALYZE
, затем послеVACUUM ANALYZE
, затем (только если вы можете позволить себе эксклюзивную блокировку!) послеVACUUM FULL ANALYZE
. Скорее всего, вы увидите сканирование только индекса послеVACUUM FULL
, которое значительно быстрее (но эффект ухудшается со временем, если у вас много записей в таблицу). - person Erwin Brandstetter   schedule 11.05.2015