J'ai un tableau qui contient des données extraites de documents texte. Les données sont stockées dans une colonne appelée "CONTENT"
pour laquelle j'ai créé cet index à l'aide de GIN:
CREATE INDEX "File_contentIndex"
ON "File"
USING gin
(setweight(to_tsvector('english'::regconfig
, COALESCE("CONTENT", ''::character varying)::text), 'C'::"char"));
J'utilise la requête suivante pour effectuer une recherche en texte intégral sur la table:
SELECT "ITEMID",
ts_rank(setweight(to_tsvector('english', coalesce("CONTENT",'')), 'C') ,
plainto_tsquery('english', 'searchTerm')) AS "RANK"
FROM "File"
WHERE setweight(to_tsvector('english', coalesce("CONTENT",'')), 'C')
@@ plainto_tsquery('english', 'searchTerm')
ORDER BY "RANK" DESC
LIMIT 5;
La table File contient 250 000 lignes et chaque "CONTENT"
entrée se compose d'un mot aléatoire et d'une chaîne de texte identique pour toutes les lignes.
Maintenant, lorsque je recherche un mot aléatoire (1 hit dans tout le tableau), la requête s'exécute très rapidement (<100 ms). Cependant, lorsque je recherche un mot qui est présent dans toutes les lignes, la requête s'exécute extrêmement lentement (10 minutes ou plus).
EXPLAIN ANALYZE
montre que pour la recherche en une touche, un scan d'index bitmap suivi d'un scan de tas bitmap est effectué. Pour la recherche lente, une analyse Seq est effectuée à la place, ce qui prend si longtemps.
Certes, il n'est pas réaliste d'avoir les mêmes données dans toutes les lignes. Mais comme je ne peux pas contrôler les documents texte téléchargés par les utilisateurs, ni les recherches qu'ils effectuent, il est possible qu'un scénario similaire se produise (recherche sur des termes avec une occurrence très élevée dans la base de données). Comment puis-je augmenter les performances de ma requête de recherche pour un tel scénario?
Exécution de PostgreSQL 9.3.4
Plans de requête de EXPLAIN ANALYZE
:
Recherche rapide (1 hit dans DB)
"Limit (cost=2802.89..2802.90 rows=5 width=26) (actual time=0.037..0.037 rows=1 loops=1)"
" -> Sort (cost=2802.89..2806.15 rows=1305 width=26) (actual time=0.037..0.037 rows=1 loops=1)"
" Sort Key: (ts_rank(setweight(to_tsvector('english'::regconfig, (COALESCE("CONTENT", ''::character varying))::text), 'C'::"char"), '''wfecg'''::tsquery))"
" Sort Method: quicksort Memory: 25kB"
" -> Bitmap Heap Scan on "File" (cost=38.12..2781.21 rows=1305 width=26) (actual time=0.030..0.031 rows=1 loops=1)"
" Recheck Cond: (setweight(to_tsvector('english'::regconfig, (COALESCE("CONTENT", ''::character varying))::text), 'C'::"char") @@ '''wfecg'''::tsquery)"
" -> Bitmap Index Scan on "File_contentIndex" (cost=0.00..37.79 rows=1305 width=0) (actual time=0.012..0.012 rows=1 loops=1)"
" Index Cond: (setweight(to_tsvector('english'::regconfig, (COALESCE("CONTENT", ''::character varying))::text), 'C'::"char") @@ '''wfecg'''::tsquery)"
"Total runtime: 0.069 ms"
Recherche lente (250k hits dans DB)
"Limit (cost=14876.82..14876.84 rows=5 width=26) (actual time=519667.404..519667.405 rows=5 loops=1)"
" -> Sort (cost=14876.82..15529.37 rows=261017 width=26) (actual time=519667.402..519667.402 rows=5 loops=1)"
" Sort Key: (ts_rank(setweight(to_tsvector('english'::regconfig, (COALESCE("CONTENT", ''::character varying))::text), 'C'::"char"), '''cyberspace'''::tsquery))"
" Sort Method: top-N heapsort Memory: 25kB"
" -> Seq Scan on "File" (cost=0.00..10541.43 rows=261017 width=26) (actual time=2.097..519465.953 rows=261011 loops=1)"
" Filter: (setweight(to_tsvector('english'::regconfig, (COALESCE("CONTENT", ''::character varying))::text), 'C'::"char") @@ '''cyberspace'''::tsquery)"
" Rows Removed by Filter: 6"
"Total runtime: 519667.429 ms"
explain (analyze, buffers)
, de préférence avec track_io_timing défini sur ON
? Il n'y a aucun moyen que cela prenne 520 secondes pour seq analyser cette table, sauf si vous l'avez stockée sur un RAID de disquettes. Quelque chose y est définitivement pathologique. En outre, quel est votre réglage random_page_cost
et les autres paramètres de coût?
ORDER BY "RANK" DESC
. J'enquêtepg_trgm
avec l' index GiST et les opérateurs similarité / distance , comme alternative. Considérez: dba.stackexchange.com/questions/56224/… . Peut même produire de "meilleurs" résultats (en plus d'être plus rapide).