如何解决有什么办法可以使这个 postgres 查询更快?
所以我有这个查询;
SELECT
*
FROM
api_points
WHERE
cat_level_5 = ANY(ARRAY['7223b580c20758c7','3e23543ccb9a2ef4','bbaec92cedb8a3bc',...])
AND
NOT cat_level_5 = ANY(ARRAY['8ccb9d2231a318e4'])
关于地理空间点的记录大约有 2~300 万条。类别是分层的,有 5 列,分别命名为 cat_level_1
、cat_level_2
、cat_level_3
、cat_level_4
和 cat_level_5
。
此查询指定了许多 5 级类别,大约需要 1-2 分钟才能完成。我从使用 IN
运算符切换到使用数组的 =
。
我可以更改类别结构或类别数据类型以加快索引速度。
顺便说一下,当我添加带有 btree(cat_level_1,cat_level_2,cat_level_3,cat_level_4,cat_level_5)
的索引时,它并没有太大区别。
这是result of EXPLAIN(ANALYZE,BUFFERS)
编辑
我将 SQL 查询编辑为此,结果为 this。
EXPLAIN (ANALYZE,BUFFERS)
SELECT *
FROM (
SELECT * FROM unnest(ARRAY['7223b580c20758c7',...]) cat_level_5
WHERE cat_level_5 NOT IN ( SELECT * FROM unnest(ARRAY['8ccb9d2231a318e4']) )
) categories
JOIN api_point
USING (cat_level_5)
表结构由这个django模型定义;
class Point(models.Model):
poi_id = models.TextField(primary_key=True)
poi_name = models.TextField(default = "")
poi_address = models.TextField(default = "")
location = models.TextField(default = "")
location_info = models.TextField(default = "")
history = models.DateField(default = datetime.date.today)
city_code = models.IntegerField()
town_code = models.IntegerField()
quarter_code = models.IntegerField()
icon_url = models.TextField()
cat_level_1 = models.TextField()
cat_level_1_name = models.TextField()
cat_level_2 = models.TextField()
cat_level_2_name = models.TextField()
cat_level_3 = models.TextField()
cat_level_3_name = models.TextField()
cat_level_4 = models.TextField()
cat_level_4_name = models.TextField()
cat_level_5 = models.TextField()
cat_level_5_name = models.TextField()
lat = models.FloatField()
lon = models.FloatField()
point = PointField(default = Point(0.0,0.0))
last_modified = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
SQL表结构如下:
CREATE TABLE public.api_poi
(
poi_id text COLLATE pg_catalog."default" NOT NULL,poi_name text COLLATE pg_catalog."default" NOT NULL,poi_address text COLLATE pg_catalog."default" NOT NULL,location text COLLATE pg_catalog."default" NOT NULL,location_info text COLLATE pg_catalog."default" NOT NULL,history date NOT NULL,city_code integer NOT NULL,town_code integer NOT NULL,quarter_code integer NOT NULL,icon_url text COLLATE pg_catalog."default" NOT NULL,lat double precision NOT NULL,lon double precision NOT NULL,last_modified timestamp with time zone NOT NULL,created_at timestamp with time zone NOT NULL,point geometry(Point,4326) NOT NULL,cat_level_1 text COLLATE pg_catalog."default" NOT NULL,cat_level_1_name text COLLATE pg_catalog."default" NOT NULL,cat_level_2 text COLLATE pg_catalog."default" NOT NULL,cat_level_2_name text COLLATE pg_catalog."default" NOT NULL,cat_level_3 text COLLATE pg_catalog."default" NOT NULL,cat_level_3_name text COLLATE pg_catalog."default" NOT NULL,cat_level_4 text COLLATE pg_catalog."default" NOT NULL,cat_level_4_name text COLLATE pg_catalog."default" NOT NULL,cat_level_5 text COLLATE pg_catalog."default" NOT NULL,cat_level_5_name text COLLATE pg_catalog."default" NOT NULL,CONSTRAINT api_poi_pkey PRIMARY KEY (poi_id)
)
TABLESPACE pg_default;
ALTER TABLE public.api_poi
OWNER to postgres;
-- Index: category_idx
-- DROP INDEX public.category_idx;
CREATE INDEX category_idx
ON public.api_poi USING btree
(cat_level_1 COLLATE pg_catalog."default" ASC NULLS LAST,cat_level_2 COLLATE pg_catalog."default" ASC NULLS LAST,cat_level_3 COLLATE pg_catalog."default" ASC NULLS LAST,cat_level_4 COLLATE pg_catalog."default" ASC NULLS LAST,cat_level_5 COLLATE pg_catalog."default" ASC NULLS LAST)
TABLESPACE pg_default;
-- Index: point_namex
-- DROP INDEX public.point_namex;
CREATE INDEX point_idx
ON public.api_poi USING brin
(point)
TABLESPACE pg_default;
解决方法
示例表 foo 有一个包含数字的文本列 x。
explain analyze select * from foo where x =any('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99}'::TEXT[]);
Gather (cost=1000.00..62144.07 rows=99 width=10) (actual time=0.682..364.252 rows=99 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on foo (cost=0.00..61134.17 rows=41 width=10) (actual time=237.705..358.203 rows=33 loops=3)
Filter: (x = ANY ('{1,99}'::text[]))
Rows Removed by Filter: 333300
Planning Time: 0.219 ms
Execution Time: 364.292 ms
注意 =ANY 运算符在数组中使用线性搜索,如果数组很长,速度会很慢。
explain analyze select * from (select * from unnest('{1,99}'::TEXT[]) x
where x not in ('7','8')) a join foo using (x);
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Gather (cost=1002.45..12146.72 rows=97 width=36) (actual time=0.904..44.894 rows=97 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Hash Join (cost=2.45..11137.02 rows=40 width=36) (actual time=24.904..38.844 rows=32 loops=3)
Hash Cond: (foo.x = x.x)
-> Parallel Seq Scan on foo (cost=0.00..9571.67 rows=416667 width=10) (actual time=0.007..16.592 rows=333333 loops=3)
-> Hash (cost=1.24..1.24 rows=97 width=32) (actual time=0.057..0.057 rows=97 loops=3)
Buckets: 1024 Batches: 1 Memory Usage: 12kB
-> Function Scan on unnest x (cost=0.00..1.24 rows=97 width=32) (actual time=0.020..0.040 rows=97 loops=3)
Filter: (x <> ALL ('{7,8}'::text[]))
Rows Removed by Filter: 2
使用 unnest 会产生更快的散列连接,从而避免在数组中进行线性搜索。此外,我在生成要获取的值列表的子查询中添加了一个带有“否定列表以排除特定用户的黑名单类别”的“NOT IN”子句,因此从列表中删除黑名单值一次开始。
因此,它会生成您想要的值列表,删除列入黑名单的值,对其进行散列,然后扫描表格以查找散列中该列的每一行的值。
这应该会使查询更快一点,但它不会解决主要问题,即它正在读取 3GB 的数据。
当我用 btree(cat_level_1,cat_level_2,cat_level_3,cat_level_4,cat_level_5) 添加索引时,它并没有太大的不同。
这是正常的,你没有在查询中使用 cat_levels_1 到 4,所以它不能使用位于索引列列表末尾的 cat_level_5。也许 cat_level_5 上的索引会有所帮助,但我对此表示怀疑,因为此查询正在选择表的很大一部分。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。