使用物化视图改进 jsonb 索引和查询

Using materialized views to improve jsonb indexing and querying

有一个 RDS Postgresql 12.5 并处理一个 table (app_events) 和一个 JSONB 列 (metadata),JSON 数据可能会因事件名称而异,请参阅下面的结构。

CREATE TABLE IF NOT EXISTS "public".app_events (
    id uuid DEFAULT uuid_generate_v4() NOT NULL,
    event_id text NOT NULL,
    name text NOT NULL,
    creation_time timestamp without time zone NOT NULL,
    creation_time_in_milliseconds bigint NOT NULL,
    metadata jsonb NOT NULL,
    PRIMARY KEY(id)
);

event_idname 上有默认索引 (btree)。

基于 name 列,我们创建视图以将 JSON 中的数据规范化为表格格式,示例如下。

CREATE OR REPLACE VIEW "public".charity_created AS
 SELECT app_events.id,
    app_events.event_id,
    app_events.name,
    app_events.creation_time,
    date(app_events.creation_time) AS created_date,
    (app_events.metadata ->> 'aggregateId'::text) AS user_id,
    (app_events.metadata ->> 'url'::text) AS url,
    (app_events.metadata ->> 'name'::text) AS charity_name,
    (app_events.metadata ->> 'about'::text) AS charity_about,
    (app_events.metadata ->> 'country'::text) AS country,
    (app_events.metadata ->> 'category'::text) AS category,
    (app_events.metadata ->> 'currencyCode'::text) AS currencycode,
    (app_events.metadata ->> 'isPayItForwardPartner'::text) AS is_pay_it_forward_partner,
    (app_events.metadata ->> 'isCampaignDonationPartner'::text) AS is_campaign_donation_partner
   FROM public.app_events
  WHERE (app_events.name = 'CharityCreated'::text)
  ORDER BY (date(app_events.creation_time)) DESC;

现在您可以 运行 如下查询。

SELECT * 
FROM "public".charity_created
WHERE charity_name == 'some_charity_name'

此外,我们在视图之间创建联接或并集,并开始注意到 reads/queries 的延迟有时长达一个小时,没有超时但返回数据可能具有挑战性,这对我们的报告团队来说绝对是一个巨大的打击。

我正在寻找的问题和知识是,我可以在哪里创建(或应该创建)索引以改善读取延迟;目前正在讨论两个发现。

  1. 在实际的 JSONB 列 (metadata) 上没有意义,因此也许解决方案是根据每个事件名称的特定模式开始创建索引,例如
  2. 创建materialized views并支付刷新数据副本(每晚)的代价,同时在物化视图上创建索引,类似于this answer
  3. 也许保留 views 并在这些
  4. 上创建索引

您可以尝试将索引与以下表达式一起使用:

CREATE INDEX app_events_charity_name_idx ON app_events USING(metadata ->> 'name'::text);

当在查询中(准确地)使用表达式时,可能会使用此索引,例如:

SELECT * FROM app_events WHERE metadata ->> 'name'::text = 'some charity';

这也适用于您的观点。但是你应该用 EXPLAIN.

检查一下

在较新版本的 Postgres 中也有可能 generated columns。与物化视图相比,它们的优点是您不必担心更新它们。

ALTER TABLE app_events ADD charity_name TEXT GENERATED ALWAYS AS (metadata ->> 'name'::text);

您也可以为这些列创建索引。