使用Postgres 9.5.3,我有一个包含~10k文本文档的表。其中一个字段content
是JSONB,其条目看起来像这样:
{'title' : 'Short Document Title', 'text' : 'Some loooooong text......'}
一个简单的select *和sort查询工作正常:
EXPLAIN ANALYZE
SELECT *
FROM documents
ORDER BY date DESC LIMIT 25;
Limit (cost=2224.60..2224.67 rows=25 width=1393) (actual time=30.733..30.741 rows=25 loops=1)
-> Sort (cost=2224.60..2246.16 rows=8624 width=1393) (actual time=30.731..30.733 rows=25 loops=1)
Sort Key: release_date DESC
Sort Method: top-N heapsort Memory: 65kB
-> Seq Scan on document (cost=0.00..1981.24 rows=8624 width=1393) (actual time=0.025..26.463 rows=8624 loops=1)
Planning time: 0.388 ms
Execution time: 30.840 ms
但是,使用单个JSONB字段查找,执行时间增加超过30x:
EXPLAIN ANALYZE
SELECT content->'title'
FROM documents
ORDER BY date DESC LIMIT 25;
Limit (cost=2246.16..2246.23 rows=25 width=59) (actual time=972.382..972.389 rows=25 loops=1)
-> Sort (cost=2246.16..2267.72 rows=8624 width=59) (actual time=972.380..972.382 rows=25 loops=1)
Sort Key: release_date DESC
Sort Method: top-N heapsort Memory: 28kB
-> Seq Scan on document (cost=0.00..2002.80 rows=8624 width=59) (actual time=0.582..964.898 rows=8624 loops=1)
Planning time: 0.114 ms
Execution time: 972.434 ms
为什么第二个查询要慢得多/应该采取哪些措施来提高性能(最好不要更改架构)?
更新:我已从9.5.3>升级10.1期望看到更好的表现。事实上,10.1上的表现相当糟糕:
EXPLAIN ANALYZE
SELECT content->'title'
FROM documents
ORDER BY date DESC LIMIT 25;
Limit (cost=2009.16..2009.23 rows=25 width=36) (actual time=1011.282..1011.288 rows=25 loops=1)
-> Sort (cost=2009.16..2030.72 rows=8624 width=36) (actual time=1011.281..1011.285 rows=25 loops=1)
Sort Key: release_date DESC
Sort Method: top-N heapsort Memory: 30kB
-> Seq Scan on documents (cost=0.00..1765.80 rows=8624 width=36) (actual time=0.157..1005.157 rows=8624 loops=1)
Planning time: 0.090 ms
Execution time: 1011.315 ms
答案 0 :(得分:2)
我认为这是因为您使用的是早于9.6的PostgreSQL版本。
在9.6 release notes中,您可以看到相关条目:
在适当的情况下,将
SELECT
输出表达式的评估推迟到ORDER BY
排序后(Konstantin Knizhnik)此更改可确保输出列表中的易失性或昂贵函数按
ORDER BY
建议的顺序执行,并且当存在LIMIT
子句时,它们的评估次数不会超过要求。以前,如果排序是通过索引扫描或预合并连接排序执行的,则会保留这些属性,但如果是通过顶级排序执行排序则不会。
jsonb
值不存储在主表中,而是存储在属于它的 TOAST表中。如果访问该值,则必须将其“解除”,即从TOAST表加载并解压缩。
在您的第一个查询中,这只发生在返回给客户端的25行中,但在第二个查询中,所有8624 jsonb
值都被解除了。