我试图加快查询存储在PostgreSQL数据库中的一些JSON数据。我继承了一个应用程序,该应用程序查询名为data
的PostgreSQL表,该表具有名为value
的字段,其中value是jsonb
类型的JSON的blob。
它大约有300行,但是从5个json元素中选择这些数据需要12秒。json blob有点大,但是如果有帮助的话,我需要的数据都在json嵌套的顶层。
我试着添加一个CREATE INDEX idx_tbl_data ON data USING gin (value);
的索引,但没有帮助。我应该使用不同的索引吗?长期的愿景是重写应用程序,将数据从json中删除,但由于应用程序其他部分的复杂性,这至少需要30-40个工作日,所以我希望看看我是否可以在短期内加快速度。
不知道这是否有帮助,但是组成这个结果集的底层数据并不经常变化,经常变化的是json blob中更底层的数据。
SELECT
value::json ->> 'name' AS name,
value::json ->> 'mnemonic' AS mnemonic,
value::json ->> 'urlName' AS "urlName",
value::json ->> 'countryCode' AS "countryCode",
value::json #>>'{team}' AS team
FROM
data;
字符串
2条答案
按热度按时间z0qdvdin1#
就像你自己提到的,正确的解决办法是将这些属性提取到单独的列中,这在一定程度上规范了你的设计。
索引能帮上忙吗?
很遗憾,没有(截至Postgres 14)。
它在理论上可以工作。因为你的值很大,一个只有一些小属性的表达式索引可以被Postgres在一个只扫描索引的扫描中拾取,即使在检索所有行时(否则它会忽略索引)。
The manual:
然而,PostgreSQL的规划器目前对这种情况不是很聪明,它认为只有当查询所需的所有列都可以从索引中获得时,查询才可能通过仅索引扫描执行。
所以你必须在索引中包含
value
本身,即使只是作为INCLUDE
列-完全破坏了整个想法。在短期内,你可能仍然可以做一些**事情。两个关键的报价:
我期待着看看我是否可以使这在短期内更快
json blob有点大
数据类型
从查询中删除
json
的强制转换。每次强制转换都会增加无意义的成本。压缩
一个主要的成本因素将是压缩。Postgres必须"de-toast"整个大列,只是为了提取一些小属性。从Postgres 14开始,您可以切换压缩算法(如果您的版本中启用了支持!)。默认值由配置设置
default_toast_compression
定义,默认为pglz
。目前唯一可用的替代方案是**lz4
**。您可以随时按列设置。LZ 4(
lz4
)速度快得多,但压缩速度通常稍低。大约是两倍快,但存储量增加了10%左右(取决于!)。如果性能不是问题,最好坚持使用默认LZ算法(pglz
)的更强压缩。将来可能会有更多压缩算法可供选择。执行:
字符串
为列设置一个新的
COMPRESSION
不会自动重新压缩。Postgres会记住压缩方法,只有在强制解压缩的情况下才会重新压缩。你可能想强制重新压缩现有的值。你可以检查:型
相关博客文章:
GENERATED
列当你被这种糟糕的设计所困时,你可以添加一些(小的!)生成列来覆盖你的查询:
型
然后只针对那些生成的列,根本不涉及大的
value
。型
这将绕过主要的性能问题。
参见:
但是,你提到:
json blob中更底层的数据经常发生变化。
对
value
的每一次更改都会强制重新检查生成的列,因此会增加写入成本。23c0lvtd2#
我也有类似的性能问题,不幸的是我没有运行
PostgreSQL 12+
,所以不能使用generated
列。你有两个解决方案:
1.)表中的每一列都应该是硬编码的,解析器应该插入到这些列中。(理想的解决方案)
2.)创建查询的
materialized view
,并将索引添加到此视图的主键列。然后,您可以使用refresh materialized view concurrently
刷新此视图,而不会对其他用户/应用程序查询此视图时的性能产生任何影响。