我正在尝试加快对 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;
就像您自己提到的,正确的解决方法是将这些属性提取到单独的列中,从而在某种程度上规范化您的设计。
遗憾的是,没有(从 Postgres 14 开始)。
理论上它可以工作。由于您的值是big,Postgres 可以在仅索引扫描中拾取仅包含一些小属性的表达式索引,即使在检索所有行时(否则它将忽略索引)。
然而,PostgreSQL 的规划器目前对于此类情况还不是很聪明。仅当查询所需的所有列均可从索引中获取时,它才认为查询可以通过仅索引扫描来执行。因此,您必须将
value
本身包含在索引中,即使就像
INCLUDE
列一样 - 完全破坏了整个想法。不许去。短期内您可能仍然可以做
某事。两句重要的引言:
我想看看是否可以在短期内加快速度
json blob 有点大数据类型
json
。每次选角都会增加无意义的成本。压缩
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栏目虽然坚持破损的设计,您可能只需添加一些(小!)生成的列来覆盖您的查询:
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
我也有类似的性能问题,不幸的是我没有运行
generated
列。
您有两种解决方案:1.) 您的表应该对每一列进行硬编码,并且解析器应该插入到这些列中。 (理想的解决方案)
2.) 创建查询的
materialized view
refresh materialized view concurrently
刷新此视图,而不会在其他用户/应用程序查询该视图时产生任何性能影响。