- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我定义了以下索引:
CREATE INDEX
users_search_idx
ON
auth_user
USING
gin(
username gin_trgm_ops,
first_name gin_trgm_ops,
last_name gin_trgm_ops
);
我正在执行以下查询:
PREPARE user_search (TEXT, INT) AS
SELECT
username,
email,
first_name,
last_name,
( -- would probably do per-field weightings here
s_username + s_first_name + s_last_name
) rank
FROM
auth_user,
similarity(username, $1) s_username,
similarity(first_name, $1) s_first_name,
similarity(last_name, $1) s_last_name
WHERE
username % $1 OR
first_name % $1 OR
last_name % $1
ORDER BY
rank DESC
LIMIT $2;
auth_user
表有 620 万行。
查询速度似乎在很大程度上取决于similarity
查询可能返回的结果数量。
通过 set_limit
增加相似性阈值会有所帮助,但会通过消除部分匹配降低结果的有用性。
一些搜索在 200 毫秒内返回,其他搜索大约需要 10 秒。
我们已经使用 Elasticsearch 实现了此功能,可以在 < 200 毫秒内返回任何查询,同时进行更复杂(更好)的排名。
我想知道是否有任何方法可以改进这一点以获得更一致的性能?
据我了解,GIN 索引(倒排索引)与 Elasticsearch 使用的基本方法相同,因此我认为可以进行一些优化。
EXPLAIN ANALYZE EXECUTE user_search('mel', 20)
显示:
Limit (cost=54099.81..54099.86 rows=20 width=52) (actual time=10302.092..10302.104 rows=20 loops=1)
-> Sort (cost=54099.81..54146.66 rows=18739 width=52) (actual time=10302.091..10302.095 rows=20 loops=1)
Sort Key: (((s_username.s_username + s_first_name.s_first_name) + s_last_name.s_last_name)) DESC
Sort Method: top-N heapsort Memory: 26kB
-> Nested Loop (cost=382.74..53601.17 rows=18739 width=52) (actual time=118.164..10293.765 rows=8380 loops=1)
-> Nested Loop (cost=382.74..53132.69 rows=18739 width=56) (actual time=118.150..10262.804 rows=8380 loops=1)
-> Nested Loop (cost=382.74..52757.91 rows=18739 width=52) (actual time=118.142..10233.990 rows=8380 loops=1)
-> Bitmap Heap Scan on auth_user (cost=382.74..52383.13 rows=18739 width=48) (actual time=118.128..10186.816 rows=8380loops=1)"
Recheck Cond: (((username)::text % 'mel'::text) OR ((first_name)::text % 'mel'::text) OR ((last_name)::text %'mel'::text))"
Rows Removed by Index Recheck: 2434523
Heap Blocks: exact=49337 lossy=53104
-> BitmapOr (cost=382.74..382.74 rows=18757 width=0) (actual time=107.436..107.436 rows=0 loops=1)
-> Bitmap Index Scan on users_search_idx (cost=0.00..122.89 rows=6252 width=0) (actual time=40.200..40.200rows=88908 loops=1)"
Index Cond: ((username)::text % 'mel'::text)
-> Bitmap Index Scan on users_search_idx (cost=0.00..122.89 rows=6252 width=0) (actual time=43.847..43.847rows=102028 loops=1)"
Index Cond: ((first_name)::text % 'mel'::text)
-> Bitmap Index Scan on users_search_idx (cost=0.00..122.89 rows=6252 width=0) (actual time=23.387..23.387rows=58740 loops=1)"
Index Cond: ((last_name)::text % 'mel'::text)
-> Function Scan on similarity s_username (cost=0.00..0.01 rows=1 width=4) (actual time=0.004..0.004 rows=1 loops=8380)
-> Function Scan on similarity s_first_name (cost=0.00..0.01 rows=1 width=4) (actual time=0.002..0.002 rows=1 loops=8380)
-> Function Scan on similarity s_last_name (cost=0.00..0.01 rows=1 width=4) (actual time=0.002..0.002 rows=1 loops=8380)
Execution time: 10302.559 ms
服务器是在 Amazon RDS 上运行的 Postgres 9.6.1
发布问题后不久,我发现了以下信息:https://www.postgresql.org/message-id/464F3C5D.2000700@enterprisedb.com
所以我试过了
-> SHOW work_mem;
4MB
-> SET work_mem='12MB';
-> EXECUTE user_search('mel', 20);
(results returned in ~1.5s)
这有了很大的改进(之前 > 10 秒)!
对于类似的查询,1.5s 仍然比 ES 慢很多,所以我仍然想听听任何优化查询的建议。
2.作为对评论的回应,在看到这个问题(Postgresql GIN index slower than GIST for pg_trgm)后,我尝试了完全相同的设置,使用 GIST 索引代替 GIN 索引。
使用默认 work_mem='4MB'
尝试上面的相同搜索,它在 ~3.5 秒内返回。增加 work_mem
没有任何区别。
由此我得出结论,GIST 索引的内存效率更高(没有像 GIN 那样遇到病理情况)但是当 GIN 正常工作时比 GIN 慢。这与推荐 GIN 索引的文档中描述的内容一致。
3.我还是不明白为什么要花这么多时间:
-> Bitmap Heap Scan on auth_user (cost=382.74..52383.13 rows=18739 width=48) (actual time=118.128..10186.816 rows=8380loops=1)"
Recheck Cond: (((username)::text % 'mel'::text) OR ((first_name)::text % 'mel'::text) OR ((last_name)::text %'mel'::text))"
Rows Removed by Index Recheck: 2434523
Heap Blocks: exact=49337 lossy=53104
我不明白为什么需要这一步或它在做什么。
对于每个 username % $1
子句,在它下面有三个 Bitmap Index Scan
...然后这些结果与 BitmapOr
步骤。这些部分都非常快。
但即使在我们没有用完工作内存的情况下,我们仍然会在 Bitmap Heap Scan
上花费将近整整一秒。
最佳答案
我希望使用这种方法很多 更快的结果:
创建一个 GiST 索引,其中 1 列包含连接的值:
CREATE INDEX users_search_idx ON auth_user
USING gist((username || ' ' || first_name || ' ' || last_name) gist_trgm_ops);
这假设所有 3 列都被定义为 NOT NULL
(您没有指定)。否则你需要做更多。
为什么不使用 concat_ws()
进行简化?
使用适当的 nearest-neighbor查询,匹配上面的索引:
SELECT username, email, first_name, last_name
, similarity(username , $1) AS s_username
, similarity(first_name, $1) AS s_first_name
, similarity(last_name , $1) AS s_last_name
, row_number() OVER () AS rank -- greatest similarity first
FROM auth_user
WHERE (username || ' ' || first_name || ' ' || last_name) % $1 -- !!
ORDER BY (username || ' ' || first_name || ' ' || last_name) <-> $1 -- !!
LIMIT $2;
WHERE
和ORDER BY
中的表达式必须匹配索引表达式!
特别是 ORDER BY rank
(就像你有的那样)对于从更大的合格行池中挑选的小 LIMIT
总是表现不佳,因为它不能使用直接索引:rank
背后的复杂表达式必须计算每个 符合条件的行,然后在返回最佳匹配的小选择之前对所有行进行排序。这比真正的最近邻查询昂贵得多,后者可以直接从索引中选择最佳结果,甚至无需查看其余部分。
row_number()
仅反射(reflect)由同一 SELECT
的 ORDER BY
产生的排序。
相关回答:
至于你的项目3.
,我添加了你提到的问题的答案,应该解释一下:
关于postgresql - 优化 postgres 相似性查询(pg_trgm + gin 索引),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43867449/
pg_trgm 在这两个比较中都给我 0.4 分: SELECT similarity('Noemie','Noémie'); 0.4 SELECT similarity('Noemie','NoXm
创建表: CREATE TABLE public.personal ( id bigserial NOT NULL PRIMARY KEY, first_name ch
将 pg_trgm.word_similarity_threshold 设置为 0.2;降低当前 session 的阈值但不为数据库做。我需要降低支持拼写错误的阈值。 最佳答案 赞the docume
我在玩 pg_trgm 扩展,我有点困惑。这是 session : postgres=# create table t(i int, x text); CREATE TABLE postgres=#
SELECT col1, max(date) as max_date FROM table WHERE col1 ILIKE 'name' GROUP BY col1 这里的 col1 是 varch
我有下表 id error - ---------------------------------------- 1 Error 1234eee5, can not write to disk 2
谁能给我解释一下在 Postgres pg_trgm 模块中相似度函数是如何计算的。 例如相似度('sage', 'message') = 0.3 1) " s"," sa",age,"ge ",s
我有表 Users,该列上有列 displayName (text) 和 pg_trgm gin index。 CREATE INDEX "Users-displayName-pg-trgm-inde
所以我有一个简单的场景。我有一张 field 表(事件 field 等)。我的查询看起来像: SELECT * FROM venues WHERE venues.name % 'Philips Are
这似乎是一个非常基本的问题,但我如何更改 pg_trgm 扩展的默认限制?目前是 0.3。我做了: select set_limit(0.5) select show_limit() => 0.5 关
我有一张 table item具有多个文本字段,例如 name , unique_attr , category等,所有这些我都使用 GIN (gin_trgm_ops) 索引更快地编制索引 ilik
我有我的 pg_trgm已安装模块。 pg_trgm | 1.0 | extensions | text similarity measurement and index ... 模式集是ex
从源代码成功安装 postgresql 9.2.2 后(在 OpenSUSE 11.4 上,存储库中没有此版本),我正在尝试恢复使用 pg_trgm 扩展的数据库。这会导致错误,因为找不到 pg_tr
我在一个表中有一个包含 100.000 个句子的列表,使用 pg_trgm 我可以使用 GIN/GIST 非常快速地获得与我的字符串最接近的"super cool"指数。看官方例子: https://
我正在尝试使用 Postgres 中的相似度函数来进行一些模糊文本匹配,但是每当我尝试使用它时,我都会收到错误消息: function similarity(character varying, un
我有一个 Django 应用程序和一个 Postgresql 数据库(在生产中)。现在我想为 Postgres 安装 pg_trgm 扩展。但我找不到任何从 Django 应用程序安装它的分步说明。我
我有一个 Django 应用程序和一个 Postgresql 数据库(在生产中)。现在我想为 Postgres 安装 pg_trgm 扩展。但我找不到任何从 Django 应用程序安装它的分步说明。我
这可能是一个非常愚蠢的问题。 psyco2pg 或 postgres 都不喜欢相似性运算符。这有效: sql = 'Select * from movie where title = %s' data
我的 PostgreSQL 9.3 数据库中有 3 亿个地址,我想使用 pg_trgm 来模糊搜索这些行。最终目的是实现一个类似于谷歌地图搜索的搜索功能。 当我使用pg_trgm 搜索这些地址时,大约
我定义了以下索引: CREATE INDEX users_search_idx ON auth_user USING gin( username gin_trg
我是一名优秀的程序员,十分优秀!