【发布时间】:2019-12-28 21:22:16
【问题描述】:
我有三个构建层次结构的表:
- 表
customer主键id - 表
portfolio,主键id,外键fk_customer(索引) - 表
position,主键id,外键fk_portfolio(索引)
客户有投资组合(但有些没有),投资组合有头寸(但有些没有)。
我有一个视图,它基本上选择了这个层次结构,通常使用客户 ID、投资组合 ID 或职位 ID 的子句进行查询。 使用此视图进行选择的性能出奇地差,并且在我希望执行时间低于 10 毫秒的情况下需要超过一秒钟。
为了分析性能,我将查询隔离并简化如下:
SELECT bp.id, ptf.id, pos.id FROM customer bp
left outer join portfolio ptf on ptf.fk_customer = bp.id
left outer join position pos on pos.fk_portfolio = ptf.id
WHERE ptf.id IN (1, 2)
OR pos.id IN (3, 4)
在具体设置中(70k 客户、100k 投资组合、600k 头寸),此查询需要将近一秒钟(返回大约 10 行)。我在 Oracle 和 Postgres 上重建了这个设置(相同的数据,相同数量的记录),都显示出相同的性能问题。
当我稍微改变一下视图时(WHERE pos.fk_portfolio IN (1, 2)),执行时间大约是 0.1ms,但是没有返回没有仓位的投资组合。
Postgres 上的执行计划:
Gather (cost=22125.87..27689.07 rows=13 width=24) (actual time=703.717..782.415 rows=9 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Hash Left Join (cost=21125.87..26687.77 rows=5 width=24) (actual time=700.739..751.123 rows=3 loops=3)
Hash Cond: (ptf.id = pos.fk_portfolio)
Filter: ((ptf.id = ANY ('{1,2}'::bigint[])) OR (pos.id = ANY ('{3,4}'::bigint[])))
Rows Removed by Filter: 202202
-> Parallel Hash Left Join (cost=3057.84..5195.48 rows=42990 width=16) (actual time=70.319..171.940 rows=39930 loops=3)
Hash Cond: (bp.id = ptf.fk_customer)
-> Parallel Index Only Scan using sys_c0011416 on customer bp (cost=0.29..1440.43 rows=29642 width=8) (actual time=0.026..20.169 rows=23714 loops=3)
Heap Fetches: 0
-> Parallel Hash (cost=2298.91..2298.91 rows=60691 width=16) (actual time=69.626..69.627 rows=34392 loops=3)
Buckets: 131072 Batches: 1 Memory Usage: 5920kB
-> Parallel Seq Scan on portfolio ptf (cost=0.00..2298.91 rows=60691 width=16) (actual time=0.027..38.559 rows=34392 loops=3)
-> Parallel Hash (cost=13796.90..13796.90 rows=245690 width=16) (actual time=415.120..415.121 rows=196552 loops=3)
Buckets: 131072 Batches: 16 Memory Usage: 2816kB
-> Parallel Seq Scan on "position" pos (cost=0.00..13796.90 rows=245690 width=16) (actual time=0.009..222.681 rows=196552 loops=3)
Planning Time: 1.280 ms
Execution Time: 782.808 ms
禁用序列扫描 (set enable_seqscan = false) 也无济于事。
我也
- 确保索引(在外键约束上)存在并且处于活动状态
- 更新统计信息并压缩表(对所有 3 个表进行 VACUUM ANALYZE)
- 重新索引表(所有 3 个表上的 REINDEX)
我发现表达查询的其他方法(使用两个单独的选择和 id 过滤,然后将两者联合)显示出出色的性能,但 没有一种方法可以让我创建视图 之后我可以按客户/投资组合/职位 ID 进行过滤。 我束手无策 - 我本来预计查询会非常快,因为: 我希望你们中的任何人都可以了解为什么性能如此糟糕(在 Postgres 和 Oracle 上),并就如何解决这个问题提出建议。 编辑:
联合示例(执行时间
(select bp.id, ptf.id, pos.id from customer bp
left outer join portfolio ptf on ptf.fk_customer = bp.id
left outer join position pos on pos.fk_portfolio = ptf.id
where ptf.id IN (1, 2))
UNION
(select bp.id, ptf.id, pos.id from customer bp
left outer join portfolio ptf on ptf.fk_customer = bp.id
left outer join position pos on pos.fk_portfolio = ptf.id
where pos.id IN (3, 4))
我正在通过不支持联合的 JPA(Java Persistence API)查询数据。但是,我可以将联合用作视图定义的一部分,因为我只需要通过 JPA 传递标准(在子句中)。
【问题讨论】:
-
关于返回没有匹配投资组合的客户,您必须将这些包含在 where 子句中,即添加
OR ptf.id IS NULL和OR pos.id IS NULL -
@Adder 在我对投资组合和/或头寸有限制的情况下,我特别想排除没有投资组合和头寸的客户,因为他们既不符合投资组合也不符合 id 标准。
-
条件
pos.id IN (3, 4)将外部连接变为position回到内部连接 - 这真的是您想要的吗?
标签: postgresql performance jpa