xla*_*ash 8 postgresql performance query full-text-search postgresql-performance
我有一张大桌子,里面entities有大约 1500 万条记录。我想在他们的name.
我在 上有一个全文索引name,用于:gin_ix_entity_full_text_search_name
询问:
SELECT "entities".*,
ts_rank(to_tsvector('english', "entities"."name"::text),
to_tsquery('english', 'hockey'::text)) AS "rank0.48661998202865475"
FROM "entities"
WHERE "entities"."place" = 'f'
AND (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'hockey'::text))
ORDER BY "rank0.48661998202865475" DESC LIMIT 5
Run Code Online (Sandbox Code Playgroud)
持续时间 25,623 毫秒
解释计划
1 限制(成本=12666.89..12666.89 行=5 宽度=3116)
2 -> 排序(成本=12666.89..12670.18 行=6571 宽度=3116)
3 排序键: (ts_rank(to_tsvector('english'::regconfig, (name)::text), '''hockey'''::tsquery))
4 -> 实体上的位图堆扫描(成本 = 124.06..12645.06 行 = 6571 宽度 = 3116)
5 重新检查条件: (to_tsvector('english'::regconfig, (name)::text) @@'''hockey'''::tsquery)
6 过滤器:(不是地方)
7 -> gin_ix_entity_full_text_search_name 上的位图索引扫描(成本=0.00..123.74 行=6625 宽度=0)
8 索引条件: (to_tsvector('english'::regconfig, (name)::text) @@'''hockey'''::tsquery)
我不明白为什么它会两次验证索引条件。(查询计划步骤 4 和 7)。是因为我的布尔条件 ( not place) 吗?如果是这样,我应该将它添加到我的索引中以获得非常快速的查询吗?还是排序条件使它变慢?
EXPLAIN ANALYZE 输出:
限制(成本=4447.28..4447.29行=5宽=3116)(实际时间=18509.274..18509.282行=5循环=1)
-> Sort (cost=4447.28..4448.41 rows=2248 width=3116) (实际时间=18509.271..18509.273 rows=5 loops=1)
排序键:(ts_rank(to_tsvector('english'::regconfig, (name)::text), '''test'''::tsquery))
排序方式:top-N heapsort 内存:19kB
->实体上的位图堆扫描(成本=43.31..4439.82行=2248宽度=3116)(实际时间=119.003..18491.408行=2533循环=1)
复查条件:(to_tsvector('english'::regconfig, (name)::text)@@'''test'''::tsquery)
过滤器:(不是地方)
-> Bitmap Index Scan on gin_ix_entity_full_text_search_name (cost=0.00..43.20 rows=2266 width=0) (actual time=74.093..74.093 rows=2593 loops=1)
索引条件:(to_tsvector('english'::regconfig, (name)::text) @@'''test'''::tsquery)
总运行时间:18509.381 毫秒
这是我的数据库参数。它由 Heroku 在 Amazon 服务上托管。他们将其描述为具有 1.7GB 的 RAM、1 个处理单元和最大 1TB 的 DB。
姓名 | 当前设置 ------------------------------+-------------------- -------------------------------------------------- --------------------- 版本| i486-pc-linux-gnu 上的 PostgreSQL 9.0.7,由 GCC gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5) 4.4.3 编译,32 位 归档命令 | test -f /etc/postgresql/9.0/main/wal-ed/ARCHIVING_OFF || envdir /etc/postgresql/9.0/resource29857_heroku_com/wal-ed/env wal-e wal-push %p 归档模式 | 在 存档超时| 1分钟 checkpoint_completion_target | 0.7 checkpoint_segments | 40 client_min_messages | 注意 cpu_index_tuple_cost | 0.001 cpu_operator_cost | 0.0005 cpu_tuple_cost | 0.003 Effective_cache_size | 1530000kB 热备| 在 lc_collate | en_US.UTF-8 lc_ctype | en_US.UTF-8 listen_addresses | * log_checkpoints | 在 log_destination | 系统日志 log_line_prefix | %u [黄色] log_min_duration_statement | 50ms log_min_messages | 注意 logging_collector | 在 维护工作内存| 64MB max_connections | 500 max_prepared_transactions | 500 max_stack_depth | 2MB max_standby_archive_delay | -1 max_standby_streaming_delay | -1 max_wal_senders | 10 港口| random_page_cost | 2 server_encoding | UTF8 共享缓冲区| 415MB ssl | 在 syslog_ident | resource29857_heroku_com 时区 | 世界标准时间 wal_buffers | 8MB wal_keep_segments | 127 wal_level | 热备 work_mem | 100MB (39 行)
看起来ORDER BY是缓慢的部分:
d6ifslbf0ugpu=> EXPLAIN ANALYZE SELECT "entities"."name",
ts_rank(to_tsvector('english', "entities"."name"::text),
to_tsquery('english', 'banana'::text)) AS "rank0.48661998202865475"
FROM "entities"
WHERE (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'banana'::text))
LIMIT 5;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=43.31..53.07 rows=5 width=24) (actual time=76.583..103.623 rows=5 loops=1)
-> Bitmap Heap Scan on entities (cost=43.31..4467.60 rows=2266 width=24) (actual time=76.581..103.613 rows=5 loops=1)
Recheck Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
-> Bitmap Index Scan on gin_ix_entity_full_text_search_name (cost=0.00..43.20 rows=2266 width=0) (actual time=53.592..53.592 rows=1495 loops=1)
Index Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
Total runtime: 103.680 ms
Run Code Online (Sandbox Code Playgroud)
对比 与ORDER BY:
d6ifslbf0ugpu=> EXPLAIN ANALYZE SELECT "entities"."name",
ts_rank(to_tsvector('english', "entities"."name"::text),
to_tsquery('english', 'banana'::text)) AS "rank0.48661998202865475"
FROM "entities"
WHERE (to_tsvector('english', "entities"."name"::text) @@ to_tsquery('english', 'banana'::text))
ORDER BY "rank0.48661998202865475" DESC
LIMIT 5;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=4475.12..4475.13 rows=5 width=24) (actual time=15013.735..15013.741 rows=5 loops=1)
-> Sort (cost=4475.12..4476.26 rows=2266 width=24) (actual time=15013.732..15013.735 rows=5 loops=1)
Sort Key: (ts_rank(to_tsvector('english'::regconfig, (name)::text), '''banana'''::tsquery))
Sort Method: top-N heapsort Memory: 17kB
-> Bitmap Heap Scan on entities (cost=43.31..4467.60 rows=2266 width=24) (actual time=0.872..15006.763 rows=1495 loops=1)
Recheck Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
-> Bitmap Index Scan on gin_ix_entity_full_text_search_name (cost=0.00..43.20 rows=2266 width=0) (actual time=0.549..0.549 rows=1495 loops=1)
Index Cond: (to_tsvector('english'::regconfig, (name)::text) @@ '''banana'''::tsquery)
Total runtime: 15013.805 ms
Run Code Online (Sandbox Code Playgroud)
有点我仍然不明白为什么这会变慢。看起来它从位图堆扫描中获取相同数量的行,但它需要更长的时间?
我仍然不明白的是,为什么这会变慢。
对行进行排序将花费一些东西是显而易见的。但为什么这么多?
没有ORDER BY rank0...Postgres 就可以
实体上的位图堆扫描...行=5 ...
ts_rank()5 行。实体上的位图堆扫描...行=1495 ...
ts_rank()所有这些。ORDER BY name看看to_tsquery('english', 'hockey'::text))多余行的计算成本以及获取更多行和排序的剩余量。
位图扫描的工作原理如下:扫描索引以查找与索引条件匹配的元组的位置。位图可能会与其他位图扫描的结果进行逻辑组合,对位使用布尔逻辑。然后按页码顺序访问保存数据的页面,以减少磁盘访问并希望将一些随机读取转换为顺序读取。
位图索引扫描可能需要变得“有损”以适应内存——它会降低其从元组级别到页面级别的准确性。如果您增加 work_mem (至少对于这个查询),您可能会避免这种情况。它无法跳过第 4 行的位图堆扫描或第 6 行的过滤器,但它可能能够跳过第 5 行的重新检查。
由于编辑过的问题看起来目标是选择一些最匹配的比赛,而不是所有匹配的列表,我为此发布了一个单独的答案,因为这是一个相当不同的问题。
您可能需要考虑使用KNN - GiST(对于 K 最近邻 - 广义搜索树)索引,它可以按相似性顺序从索引中直接提取 - 因此您不需要随机读取所有这些堆元组并进行排序他们下来找到K个最佳匹配。
迄今为止,我认为没有人实现了对 tsearch 查询的 KNN-GIST 支持(尽管我确信它可以做到,这只是有人花时间去做的问题),但也许是三元组支持(在完成)会为你的应用程序。主要区别在于,trigram 搜索不像 tsearch 那样使用字典来提取词干和同义词;你只会找到完全匹配的单词。
要为您的示例尝试三元组,您可能希望像这样索引“名称”:
CREATE INDEX entities_name_trgm ON entities USING gist (name gist_trgm_ops);
Run Code Online (Sandbox Code Playgroud)
然后你可以这样搜索:
SELECT
*,
name <-> 'banana' AS sim
FROM entities
WHERE name % 'banana'
ORDER BY sim DESC
LIMIT 5;
Run Code Online (Sandbox Code Playgroud)
请注意使用的运算符和ORDER BY使用“相似性”列的别名。尝试时,我不会偏离这种模式太远。tsvector 上的索引不用于此搜索。
除非您当前的配置出现问题(这很容易使您的整个 VM 因内存过量使用而陷入无望的分页),您可能会非常喜欢它的性能。我不知道它是否具有您想要的行为。
| 归档时间: |
|
| 查看次数: |
3891 次 |
| 最近记录: |