【发布时间】:2018-03-15 10:27:27
【问题描述】:
我有一个类似这样的查询
SELECT DISTINCT
COALESCE(fa.id, fb.id) AS id,
COALESCE(fa.d_id, fb.d_id) AS d_id,
COALESCE(fa.name, fb.name) AS name,
COALESCE(fa.disabled, fb.disabled) AS disabled,
COALESCE(fa.deleted, fb.deleted) AS deleted
FROM (
SELECT * from table WHERE name LIKE '%'
AND d_id IS NULL AND deleted = false
) fa
FULL JOIN (
SELECT * from table WHERE name LIKE '%'
AND d_id = 1 AND deleted = false
) fb ON fa.name = fb.name
ORDER BY name;
其中id 是表的主键,name 是实际值。 d_id 是用户的id。
基本上,该表有一个巨大的名称列表(大约 400k+),如果它没有d_id,则表示它是由系统自动生成的。如果它有d_id,则表示它是用户生成的。
查询应该返回的是整个默认系统名称列表加上某个用户添加的名称(在这种情况下,所有名称由用户生成,d_id 为 1)。这就是它对自身执行完全连接的原因。
我的问题是运行查询花费的时间太长(在我的本地 psql shell 上大约需要 30000~40000 毫秒,而在实时运行大约需要 15000 毫秒)。我已经运行了 EXPLAIN ANALYZE 并得到了这个
Unique (cost=8240.78..8272.13 rows=2090 width=42) (actual time=27591.662..28742.062 rows=418018 loops=1)
-> Sort (cost=8240.78..8246.01 rows=2090 width=42) (actual time=27591.659..28504.606 rows=418018 loops=1)
Sort Key: (COALESCE(table.name, table_1.name)), (COALESCE(table.id, table_1.id)), (COALESCE(table.d_id, table_1.d_id)), (COALESCE(table.disabled, table_1.disabled)), (COALESCE(table.deleted, table_1.deleted))
Sort Method: external merge Disk: 13680kB
-> Hash Full Join (cost=8.45..8125.53 rows=2090 width=42) (actual time=11.037..1479.053 rows=418018 loops=1)
Hash Cond: (table.name = table_1.name)
-> Seq Scan on table (cost=0.00..8109.23 rows=2090 width=27) (actual time=0.048..799.822 rows=418018 loops=1)
Filter: ((d_id IS NULL) AND (NOT deleted) AND (name ~~ '%'::citext))
-> Hash (cost=8.44..8.44 rows=1 width=27) (actual time=10.970..10.970 rows=0 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 8kB
-> Index Scan using table__d_id__name__idx on table table_1 (cost=0.42..8.44 rows=1 width=27) (actual time=10.970..10.970 rows=0 loops=1)
Index Cond: (d_id = 1)
Filter: ((NOT deleted) AND (name ~~ '%'::citext))
虽然我不能完全理解它,但我可以说它需要太长时间的大部分原因在于排序 (ORDER BY) 函数。
我的索引如下:
Indexes:
"table_pkey" PRIMARY KEY, btree (id)
"table__d_id__name__idx" UNIQUE, btree (d_id, name)
"table__name__idx" gist (name gist_trgm_ops)
"table__id__idx" btree (id)
我尝试过使用不同的索引、重构查询并使用代码,但仍然需要同样长的时间。我已经尝试删除除主键索引之外的所有索引,并且查询以某种方式加速到了 ~23000 毫秒。
此外,在应用程序中,用户可以选择一个字母,该字母将返回以该字母开头的所有结果,查询类似于WHERE name LIKE 'a%'。尽管也有数以万计的结果,但指定一个起始字母会大大减少加载时间到大约 1000-2000 毫秒。
我的目标是将查询加载时间缩短到 5000 到 10000 毫秒。任何帮助或建议将不胜感激!
【问题讨论】:
-
不相关,但是:
WHERE name LIKE '%'可以简化为where name is not null
标签: sql database postgresql performance query-optimization