Postgres pg_trgm - 为什么按相似性排序非常慢

时间:2015-02-13 14:24:53

标签: postgresql postgresql-9.3

我在此列上有表Users,其中包含displayName (text)列和pg_trgm gin index列。

CREATE INDEX "Users-displayName-pg-trgm-index"
  ON "Users"
  USING gin
  ("displayName" COLLATE pg_catalog."default" gin_trgm_ops);

这是我的问题:

SELECT "User"."id"
    ,"User"."displayName"
    ,"User"."firstName"
    ,"User"."lastName"
    ,"User"."email"
    ,"User"."password"
    ,"User"."isVerified"
    ,"User"."isBlocked"
    ,"User"."verificationToken"
    ,"User"."birthDate"
    ,"User"."gender"
    ,"User"."isPrivate"
    ,"User"."role"
    ,"User"."coverImageUrl"
    ,"User"."profileImageUrl"
    ,"User"."facebookId"
    ,"User"."deviceType"
    ,"User"."deviceToken"
    ,"User"."coins"
    ,"User"."LocaleId"
    ,"User"."createdAt"
    ,"User"."updatedAt"
FROM "Users" AS "User"
WHERE (similarity("User"."displayName", 'John') > 0.2)
ORDER BY similarity("User"."displayName", 'John')
    ,"User"."id" ASC LIMIT 25;

上面的查询需要~200ms才能返回结果。当我删除

ORDER BY similarity("User"."displayName", 'John')

并按id排序,然后查询速度最高为30ms

我在50k个用户的桌面上查询。

以下是解释分析:http://explain.depesz.com/s/lXC

出于某种原因,我没有看到任何索引使用情况(gin pg_trgm上的displayName


似乎当我更换行

WHERE (similarity("User"."displayName", 'John') > 0.2)

WHERE ("User"."displayName" % 'John')

查询速度超快 - 有谁能告诉我原因?我认为%运算符只检查相似度(...)是否大于阈值...那么有什么区别?

3 个答案:

答案 0 :(得分:4)

PostgreSQL不使用索引作为函数,它仅为运算符使用索引。

按equality()排序的查询会调用每行的函数,然后对行进行排序。

使用%的查询使用索引并对匹配的函数运行相似性函数(没有索引仅扫描函数)。

如果您想按相似度(如问题中)排序相似度大于0.2的相似度,则应使用distance operator <->

像这样:

WHERE "User"."displayName" <-> 'John' < 0.8
ORDER BY "User"."displayName" <-> 'John' DESC

距离是1-相似性因此0.8

答案 1 :(得分:1)

我无法在Jakub Kania的答案中添加评论。我想以下查询:

WHERE "User"."displayName" <-> 'John' < 0.8
ORDER BY "User"."displayName" <-> 'John' DESC

索引也不会被使用。

您可以使用以下查询:

SELECT set_limit(0.2);
...
WHERE "User"."displayName" % 'John'
ORDER BY "User"."displayName" <-> 'John' DESC

答案 2 :(得分:0)

根据我的经验,GIST索引在相似性排序方面的效果更好/更快。

在这个例子中,我的客户表有大约500k行。

select *,similarity(coalesce(details::text,'') || coalesce(name,''),'9') 
  from customer 
  order by (coalesce(details::text,'') || coalesce(name,'')) <-> '9' 
  asc limit 50;

没有任何索引查询需要大约8,5s的查询计划:

                              QUERY PLAN                                          
-----------------------------------------------------------------------------------
 Limit  (cost=47687.03..47687.16 rows=50 width=1144)
   ->  Sort  (cost=47687.03..49184.52 rows=598995 width=1144)
         Sort Key: (((COALESCE((details)::text, ''::text) ||
                     (COALESCE(name, ''::character varying))::text) <-> '9'::text))
         ->  Seq Scan on customer  (cost=0.00..27788.85 rows=598995 width=1144)
(4 rows)

添加GIN索引时:

CREATE INDEX ON customer USING gin ((coalesce(details::text,'') || coalesce(name,'')) gin_trgm_ops);

什么都没发生。查询计划看起来仍然相同,查询仍需要大约8.5秒才能完成。没有索引用于订购。

创建GIST索引后:

CREATE INDEX ON customer USING gist ((coalesce(details::text,'') || coalesce(name,'')) gist_trgm_ops);

查询大约需要240毫秒,查询计划会显示正在使用的索引

                     QUERY PLAN                         
--------------------------------------------------------------------------
 Limit  (cost=0.42..10.19 rows=50 width=1144)
   ->  Index Scan using customer_expr_idx1 on customer  (cost=0.42..117106.73 rows=598995 width=1144)
     Order By: ((COALESCE((details)::text, ''::text) || 
                (COALESCE(name, ''::character varying))::text) <-> '9'::text)
(3 rows) 

为了好奇,返回的行看起来像这样:

   id   |           name           |        details         | similarity 
--------+--------------------------+------------------------+------------
     25 | Generic Company (9) Inc. |                        |  0.0909091
    125 | Generic Company (9) Inc. |                        |  0.0909091
 268649 | 9bg1ubTCYo7mMcDaHmCC     | { "fatty": "McDaddy" } |  0.0294118
 470217 | 9hSXtDmW9cXvKk4Q6McD     | { "fatty": "McDaddy" } |  0.0285714
 180775 | 9pRPi1w9nqV9999g2ceo     | { "fatty": "McDaddy" } |  0.0285714
 162931 | 9qMyYbWNJLZdv7uYYbOl     | { "fatty": "McDaddy" } |  0.0285714
 176961 | 9ow1NcTjAmCDyRsapDl4     | { "fatty": "McDaddy" } |  0.0285714
   ... etc ...