Vic*_*tor 12 postgresql performance index full-text-search postgresql-performance
我有一个包含超过 20M 元组的 Postgres 表:
first_name | last_name | email
-------------------------------------------
bat | man | batman@wayne.com
arya | vidal | foo@email.com
max | joe | bar@email.com
Run Code Online (Sandbox Code Playgroud)
要过滤我正在使用的记录:
SELECT *
FROM people
WHERE (first_name || '' || last_name) ILIKE '%bat%man%' OR
first_name ILIKE '%bat%man%' OR
last_name ILIKE '%bat%man%' OR
email ILIKE '%bat%man%'
LIMIT 25 OFFSET 0
Run Code Online (Sandbox Code Playgroud)
即使使用索引,搜索也需要将近一分钟才能返回结果。
有索引的(first_name || '' || last_name),first_name,last_name和email。
我可以做些什么来提高此查询的性能?
Erw*_*ter 17
对于您的模式匹配,您最好使用三元组索引。先读这个:
我假设您的 expression 中存在拼写错误(first_name || '' || last_name),这对于空字符串毫无意义,而您确实想要(first_name || ' ' || last_name)- 带有空格字符。
假设任一列都可以为 NULL,您将需要 NULL 安全连接,简单的解决方案是concat_ws():
但该函数不是IMMUTABLE(链接答案中的解释),因此您不能直接在索引表达式中使用它。您可以使用IMMUTABLE函数包装器:
CREATE OR REPLACE FUNCTION f_immutable_concat_ws(s text, t1 text, t2 text)
RETURNS text
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
'SELECT concat_ws(s, t1, t2)';
Run Code Online (Sandbox Code Playgroud)
包装器可以是IMMUTABLE因为它只需要text参数。(但不要试图在调用中强制使用文本表示不是不可变的数据类型!)
无论哪种方式,这都更冗长,但内部开销更少,而且速度要快得多:
CREATE OR REPLACE FUNCTION f_immutable_concat_ws(s text, t1 text, t2 text)
RETURNS text
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
$func$
SELECT CASE
WHEN t1 IS NULL THEN t2
WHEN t2 IS NULL THEN t1
ELSE t1 || s || t2
END
$func$;
Run Code Online (Sandbox Code Playgroud)
或者,使用硬编码的空格字符:
CREATE OR REPLACE FUNCTION f_concat_space(t1 text, t2 text)
RETURNS text AS
LANGUAGE sql IMMUTABLE PARALLEL SAFE
$func$
SELECT CASE
WHEN t1 IS NULL THEN t2
WHEN t2 IS NULL THEN t1
ELSE t1 || ' ' || t2
END
$func$;
Run Code Online (Sandbox Code Playgroud)
使其PARALLEL SAFE(在 Postgres 9.6 或更高版本中)不妨碍并行性。(而且因为它符合条件!)
基于最后一个函数的索引,我建议:
CREATE INDEX people_gin_trgm_idx ON people
USING gin (f_concat_space(first_name, last_name) gin_trgm_ops, email gin_trgm_ops);
Run Code Online (Sandbox Code Playgroud)
我添加email为第二个索引列,因为您似乎同时检查两者。
为 20M 行创建索引需要一段时间,最好不要在最高负载期间,或者可能使用CREATE INDEX CONCURRENTLY .... GIN 索引可能比普通 B 树索引大得多,维护成本也更高。一定要运行最新版本的 Postgres,最近版本对 GIN 索引进行了重大改进。
那么你稍微调整和简化的查询应该是快速和正确的:
SELECT *
FROM people
WHERE f_concat_space(first_name, last_name) ILIKE '%bat%man%'
OR email ILIKE '%bat%man%'
LIMIT 25;
Run Code Online (Sandbox Code Playgroud)
您只需要此查询的一个索引。
有关的:
| 归档时间: |
|
| 查看次数: |
11786 次 |
| 最近记录: |