查询 json / jsonb 列超级慢。我可以使用索引吗?

问题描述 投票:0回答:2

我正在尝试加快对 PostgreSQL 数据库中存储的一些 json 数据的查询速度。我继承了一个应用程序,该应用程序查询名为

data
的 PostgreSQL 表,其中包含名为
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;
json postgresql jsonb postgresql-performance
2个回答
13
投票

就像您自己提到的,正确的解决方法是将这些属性提取到单独的列中,从而在某种程度上规范化您的设计。

索引有帮助吗?

遗憾的是,没有(从 Postgres 14 开始)。

理论上它

可以工作。由于您的值是big,Postgres 可以在仅索引扫描中拾取仅包含一些小属性的表达式索引,即使在检索所有行时(否则它将忽略索引)。

说明书:

然而,PostgreSQL 的规划器目前对于此类情况还不是很聪明。仅当查询所需的所有列均可从索引中获取时,它才认为查询可以通过仅索引扫描来执行。

因此,您必须将

value

 本身包含在索引中,即使就像 
INCLUDE
 列一样 - 完全破坏了整个想法。不许去。

短期内您可能仍然可以做

某事。两句重要的引言:

我想看看是否可以在短期内加快速度

json blob 有点大

数据类型

将查询中的转换删除至

json

。每次选角都会增加无意义的成本。

压缩

一个主要的成本因素是压缩。 Postgres 必须“de-toast”整个大列,只是为了提取一些小属性。从

Postgres 14 开始,您可以切换压缩算法(如果您的版本启用了支持!)。默认值由配置设置 default_toast_compression 定义,默认情况下为

pglz
。目前唯一可用的替代方案是
lz4
。您可以对每列进行设置。任何时间。
LZ4 (
lz4

) 速度相当快,但压缩量通常要小一些。速度大约是原来的两倍,但存储空间增加了大约 10%(具体取决于!)。如果性能不是问题,最好坚持使用默认 LZ 算法的更强压缩 (

pglz

)。未来可能会有更多的压缩算法可供选择。
实施:

ALTER TABLE data ALTER COLUMN value SET COMPRESSION lz4;

为列设置新的
COMPRESSION
不会自动重新压缩。 Postgres 会记住压缩方法,并且仅在强制解压缩时才重新压缩。您可能想要强制重新压缩现有值。您可以检查:

SELECT pg_column_compression(value) FROM data LIMIT 10;

相关博文:

https://www.postgresql.fastware.com/blog/what-is-the-new-lz4-toast-compression-in-postgresql-14
  • GENERATED
栏目

虽然坚持破损的设计,您可能只需添加一些(小!)生成的列来覆盖您的查询:

ALTER TABLE data ADD COLUMN name text GENERATED ALWAYS AS (value::json ->> 'name') STORED , ADD COLUMN mnemonic text GENERATED ALWAYS AS (value::json ->> 'mnemonic') STORED ...

然后仅针对那些生成的列,
根本不涉及大
value

SELECT name, mnemonic, ... FROM data;

这将绕过主要的性能问题。
参见:

PostgreSQL 中的计算/计算/虚拟/派生/生成列
  • 但是,您提到:

经常变化的是 json blob 中更靠下的数据。

value
的每次更改都会强制重新检查生成的列,从而增加写入成本。


我也有类似的性能问题,不幸的是我没有运行

0
投票
所以无法使用

generated

列。
您有两种解决方案:

1.) 您的表应该对每一列进行硬编码,并且解析器应该插入到这些列中。 (理想的解决方案)

2.) 创建查询的

materialized view

并将索引添加到该视图的主键列。然后,您可以使用

refresh materialized view concurrently

 刷新此视图,而不会在其他用户/应用程序查询该视图时产生任何性能影响。

© www.soinside.com 2019 - 2024. All rights reserved.