【发布时间】:2016-04-27 22:22:55
【问题描述】:
我继承了一个在 django 1.5 中运行的大型遗留代码库,我当前的任务是加速网站的某个部分,该部分需要 ~1 分钟 才能加载。
我做了一个应用程序的配置文件并得到了这个:
罪魁祸首是以下查询(为简洁起见):
SELECT COUNT(*) FROM "entities_entity" WHERE (
"entities_entity"."date_filed" <= '2016-01-21' AND (
UPPER("entities_entity"."entity_city_state_zip"::text) LIKE UPPER('%Atherton%') OR
UPPER("entities_entity"."entity_city_state_zip"::text) LIKE UPPER('%Berkeley%') OR
-- 34 more of these
UPPER("entities_entity"."agent_city_state_zip"::text) LIKE UPPER('%Atherton%') OR
UPPER("entities_entity"."agent_city_state_zip"::text) LIKE UPPER('%Berkeley%') OR
-- 34 more of these
)
)
这基本上包括对两个字段entity_city_state_zip和agent_city_state_zip的大相似查询,它们是character varying(200) | not null字段。
该查询被执行 两次 (!),每次花费 18814.02ms,再一次将 COUNT 替换为 SELECT 占用额外的时间20216.49(我要缓存COUNT的结果)
解释如下:
Aggregate (cost=175867.33..175867.34 rows=1 width=0) (actual time=17841.502..17841.502 rows=1 loops=1)
-> Seq Scan on entities_entity (cost=0.00..175858.95 rows=3351 width=0) (actual time=0.849..17818.551 rows=145075 loops=1)
Filter: ((date_filed <= '2016-01-21'::date) AND ((upper((entity_city_state_zip)::text) ~~ '%ATHERTON%'::text) OR (upper((entity_city_state_zip)::text) ~~ '%BERKELEY%'::text) (..skipped..) OR (upper((agent_city_state_zip)::text) ~~ '%ATHERTON%'::text) OR (upper((agent_city_state_zip)::text) ~~ '%BERKELEY%'::text) OR (upper((agent_city_state_zip)::text) ~~ '%BURLINGAME%'::text) ))
Rows Removed by Filter: 310249
Planning time: 2.110 ms
Execution time: 17841.944 ms
我尝试过在entity_city_state_zip 和agent_city_state_zip 上使用索引,使用各种组合,例如:
CREATE INDEX ON entities_entity (upper(entity_city_state_zip));
CREATE INDEX ON entities_entity (upper(agent_city_state_zip));
或使用varchar_pattern_ops,没有运气。
服务器正在使用这样的东西:
qs = queryset.filter(Q(entity_city_state_zip__icontains = all_city_list) |
Q(agent_city_state_zip__icontains = all_city_list))
生成该查询。
我不知道还能尝试什么,
谢谢!
【问题讨论】:
-
LIKE以'%...'开头的查询不会使用任何btree 索引(包括xxx_pattern_ops)。如果模式在开始时匹配,则仅选择这些索引。 (例如col LIKE 'XXX%'或col ~ '^XXX')。你可以试试pg_trgmmodule、which provides a suitable index for you。 (您可以使用ilike代替like和lower()/upper()调用)。 -
@pozs 我不知道!我试试看
-
我至少想知道
Seq Scan有什么影响,以及是否可以替换索引扫描。看看set enable_seqscan=false对计划有什么影响。数据库是否在 SSD 上运行? -
@AndrewRegan 是的,测试是在我的具有 SSD(生产中)的 Mac 上进行的。将
enable_seqscan设置为 false 产生:Aggregate (cost=175867.33..175867.34 rows=1 width=0) (actual time=20916.498..20916.498 rows=1 loops=1) -> Seq Scan on entities_entity (cost=0.00..175858.95 rows=3351 width=0) (actual time=0.192..20871.984 rows=145075 loops=1) -
嗯,好的,这没有效果,这表明规划器没有它可能使用的替代索引。我想这就是我的下一个建议 - 将
random_page_cost降低到 1.1 左右,以告诉规划者它足够快以随机访问快速磁盘上的潜在索引而不是 seq 扫描 - 冗余。
标签: django performance postgresql