我需要为多个模型构建建议程序(自动完成)。我有一个特殊的表,其中包含需要建议的所有模型的搜索ts_vector记录。
要显示每个searchable_type
列值的最大N个结果。因此,例如,如果我们有四个类型为['Artist', 'Artwork', 'Category', 'Nation']
的值,并且具有与搜索查询匹配的类型的记录,则结果必须类似于示例。
N = 3的示例,查询='titl':
id title search searchable_type searchable_id
1 Title 1 'title':1A 'Artist' 121
1 Titlimbo 'titlimbo':1A 'Artist' 122
1 Titlover 'titlover':1A 'Artist' 123
1 Titleart 'titleart':1A 'Artwork' 124
1 Titless 'titless':1A 'Artwork' 125
1 Titlecat 'titlecat':1A 'Category' 126
1 Titledog 'titledog':1A 'Category' 127
1 TitleNation 1 'titlenation':1A 'Nation' 128
我有一个执行此任务的查询,并且在具有约2000条记录的表中做得很好。但是,当我决定在具有15万条记录的表中测试该查询时,我感到非常惊讶。查询的执行时间最多上升24分钟!对于实时建议者来说,这是一个相当大的时间。
事实是,我做错了。我正在寻求帮助,以实现这种查询和解释。
创建表查询
CREATE TABLE pg_search_documents (
id bigint NOT NULL CONSTRAINT pg_search_documents_pkey PRIMARY KEY,
title character varying,
search tsvector,
searchable_type character varying,
searchable_id bigint,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE INDEX index_pg_search_documents_on_search ON pg_search_documents USING gin (search);
CREATE INDEX index_pg_search_documents_on_searchable_type_and_searchable_id ON pg_search_documents USING btree (searchable_type, searchable_id);
查询(限制= 5,搜索=“冷杉”)
SELECT DISTINCT t_outer.searchable_type, t_top.id, t_top.title, t_top.searchable_id, t_top.updated_at FROM pg_search_documents t_outer
JOIN LATERAL (
SELECT * FROM pg_search_documents t_inner
WHERE t_inner.searchable_type = t_outer.searchable_type AND ((t_inner.search) @@ (to_tsquery('simple', ''' ' || 'fir' || ' ''' || ':*')))
ORDER BY t_inner.updated_at DESC
LIMIT 5
) t_top ON true
ORDER BY t_top.updated_at DESC
在15万条记录上的执行时间最多为25分钟。
EXPLAIN (ANALYZE, BUFFERS) SELECT DISTINCT t_top.id, t_top.title, t_top.searchable_id, t_top.updated_at FROM pg_search_documents AS t_outer
JOIN LATERAL (
SELECT * FROM pg_search_documents AS t_inner
WHERE t_inner.searchable_type = t_outer.searchable_type AND ((t_inner.search) @@ (to_tsquery('simple', ''' ' || 'fir' || ' ''' || ':*')))
ORDER BY t_inner.updated_at DESC
LIMIT 5
) AS t_top ON true
ORDER BY t_top.updated_at DESC;
Unique (cost=301102555.51..301111909.01 rows=5 width=60) (actual time=161305.761..161796.379 rows=10 loops=1)
Buffers: shared hit=74382891, temp read=19008 written=19046
-> Sort (cost=301102555.51..301104426.21 rows=748280 width=60) (actual time=161305.759..161616.199 rows=748065 loops=1)
Sort Key: t_inner.updated_at DESC, t_inner.id, t_inner.title, t_inner.searchable_id
Sort Method: external merge Disk: 90312kB
Buffers: shared hit=74382891, temp read=19008 written=19046
-> Nested Loop (cost=2010.95..300973275.75 rows=748280 width=60) (actual time=0.904..160242.631 rows=748065 loops=1)
Buffers: shared hit=74382891
-> Seq Scan on pg_search_documents t_outer (cost=0.00..5355.56 rows=149656 width=7) (actual time=0.008..49.066 rows=149656 loops=1)
Buffers: shared hit=3859
-> Limit (cost=2010.95..2010.96 rows=5 width=132) (actual time=1.067..1.068 rows=5 loops=149656)
Buffers: shared hit=74379032
-> Sort (cost=2010.95..2011.45 rows=201 width=132) (actual time=1.065..1.066 rows=5 loops=149656)
Sort Key: t_inner.updated_at DESC
Sort Method: top-N heapsort Memory: 26kB
Buffers: shared hit=74379032
-> Bitmap Heap Scan on pg_search_documents t_inner (cost=30.09..2007.61 rows=201 width=132) (actual time=0.338..0.803 rows=795 loops=149656)
Recheck Cond: (search @@ '''fir'':*'::tsquery)
Filter: ((searchable_type)::text = (t_outer.searchable_type)::text)
Rows Removed by Filter: 98
Heap Blocks: exact=73780408
Buffers: shared hit=74379032
-> Bitmap Index Scan on index_pg_search_documents_on_search (cost=0.00..30.04 rows=805 width=0) (actual time=0.277..0.277 rows=893 loops=149656)
Index Cond: (search @@ '''fir'':*'::tsquery)
Buffers: shared hit=598624
Planning time: 0.220 ms
Execution time: 161893.484 ms
如果将JOIN LATERAL中的WHERE从t_inner.searchable_type = t_outer.searchable_type
更改为t_inner.searchable_type = 'Artist'
,执行时间将为464毫秒(正常),但输出结果将错误(不稳定)。
说明
Sort (cost=26369.27..26369.32 rows=20 width=67)
Sort Key: t_top.updated_at DESC
-> HashAggregate (cost=26368.64..26368.84 rows=20 width=67)
Group Key: t_top.updated_at, t_outer.searchable_type, t_top.id, t_top.title, t_top.searchable_id
-> Nested Loop (cost=2273.74..17000.20 rows=749475 width=67)
-> Seq Scan on pg_search_documents t_outer (cost=0.00..5357.95 rows=149895 width=7)
-> Materialize (cost=2273.74..2273.82 rows=5 width=60)
-> Subquery Scan on t_top (cost=2273.74..2273.80 rows=5 width=60)
-> Limit (cost=2273.74..2273.75 rows=5 width=132)
-> Sort (cost=2273.74..2275.53 rows=718 width=132)
Sort Key: t_inner.updated_at DESC
-> Bitmap Heap Scan on pg_search_documents t_inner (cost=282.23..2261.81 rows=718 width=132)
Recheck Cond: (search @@ '''fir'':*'::tsquery)
Filter: ((searchable_type)::text = 'Artwork'::text)
-> Bitmap Index Scan on index_pg_search_documents_on_search (cost=0.00..282.05 rows=806 width=0)
Index Cond: (search @@ '''fir'':*'::tsquery)
据我所知,问题在于检查此类是否相等。
更新
查询速度慢的原因之一-表中搜索列的GIN索引可能损坏。 Postgres建议在大数据迁移之前将其关闭(我没有)。因此,删除并创建索引后,我的搜索查询开始变得更快-每个请求2.5分钟-但这也是一个巨大的时间。
答案 0 :(得分:0)
a_horse_with_no_name推荐的具有窗口功能的解决方案。
此解决方案在具有15万行的表中的执行时间没有问题,我将用该解决方案替换LATERAL查询。
SELECT rank_filter.* FROM (
SELECT pg_search_documents.*,
rank() OVER (
PARTITION BY searchable_type
ORDER BY created_at DESC
)
FROM pg_search_documents
WHERE ((search) @@ (to_tsquery('simple', ''' ' || '#{query}' || ' ''' || ':*')))
) rank_filter WHERE RANK <= 5
EXPLAIN (ANALYZE, BUFFERS) SELECT rank_filter.* FROM (
SELECT pg_search_documents.*,
rank() OVER (
PARTITION BY searchable_type
ORDER BY created_at DESC
)
FROM pg_search_documents
WHERE ((search) @@ (to_tsquery('simple', ''' ' || 'fir' || ' ''' || ':*')))
) rank_filter WHERE RANK <= 5;
Subquery Scan on rank_filter (cost=2044.61..2070.77 rows=268 width=184) (actual time=1.628..2.275 rows=10 loops=1)
Filter: (rank_filter.rank <= 5)
Rows Removed by Filter: 883
Buffers: shared hit=497
-> WindowAgg (cost=2044.61..2060.71 rows=805 width=184) (actual time=1.627..2.206 rows=893 loops=1)
Buffers: shared hit=497
-> Sort (cost=2044.61..2046.62 rows=805 width=176) (actual time=1.622..1.684 rows=893 loops=1)
Sort Key: pg_search_documents.searchable_type, pg_search_documents.created_at DESC
Sort Method: quicksort Memory: 417kB
Buffers: shared hit=497
-> Bitmap Heap Scan on pg_search_documents (cost=30.24..2005.75 rows=805 width=176) (actual time=0.300..1.007 rows=893 loops=1)
Recheck Cond: (search @@ '''fir'':*'::tsquery)
Heap Blocks: exact=493
Buffers: shared hit=497
-> Bitmap Index Scan on index_pg_search_documents_on_search (cost=0.00..30.04 rows=805 width=0) (actual time=0.251..0.251 rows=893 loops=1)
Index Cond: (search @@ '''fir'':*'::tsquery)
Buffers: shared hit=4
Planning time: 0.180 ms
Execution time: 2.317 ms