使用物化视图改进 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_id
和 name
上有默认索引 (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 的延迟有时长达一个小时,没有超时但返回数据可能具有挑战性,这对我们的报告团队来说绝对是一个巨大的打击。
我正在寻找的问题和知识是,我可以在哪里创建(或应该创建)索引以改善读取延迟;目前正在讨论两个发现。
- 在实际的 JSONB 列 (
metadata
) 上没有意义,因此也许解决方案是根据每个事件名称的特定模式开始创建索引,例如
- 创建
materialized views
并支付刷新数据副本(每晚)的代价,同时在物化视图上创建索引,类似于this answer
- 也许保留
views
并在这些 上创建索引
您可以尝试将索引与以下表达式一起使用:
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);
您也可以为这些列创建索引。
有一个 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_id
和 name
上有默认索引 (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 的延迟有时长达一个小时,没有超时但返回数据可能具有挑战性,这对我们的报告团队来说绝对是一个巨大的打击。
我正在寻找的问题和知识是,我可以在哪里创建(或应该创建)索引以改善读取延迟;目前正在讨论两个发现。
- 在实际的 JSONB 列 (
metadata
) 上没有意义,因此也许解决方案是根据每个事件名称的特定模式开始创建索引,例如 - 创建
materialized views
并支付刷新数据副本(每晚)的代价,同时在物化视图上创建索引,类似于this answer - 也许保留
views
并在这些 上创建索引
您可以尝试将索引与以下表达式一起使用:
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);
您也可以为这些列创建索引。