← 中文资源

简体中文版本

PostgreSQL JSONB GIN 包含查询

用 GIN 索引加速 JSONB 包含查询:@> 结构包含、数组值包含、键存在三种运算,复杂谓词交给 jsonpath 结构化表达。

阅读英文原文

示例说明

JSONB 的查询性能分水岭在索引:@> 包含运算符可以走 jsonb_path_ops 的 GIN 索引——文档包含给定子结构即命中,等价于一组字段的 AND 匹配,索引直接定位无需逐行解析。jsonb_path_ops 比默认 GIN 更小更快,代价是不支持存在性运算符——所以 ? 键存在查询需要普通 GIN 或表达式索引。

技术要点

  • @> 包含查询命中 GIN 索引
  • 数组值的包含语义直接可用
  • jsonpath 表达嵌套复杂谓词

代码解读

索引建在 payload 的 path_ops 变体上;第一个查询的 @> 条件表示 context.region 等于 eu 的文档,箭头运算符取值展示。第二个演示数组包含与键存在的组合。jsonpath 查询用路径表达式在 metrics 数组里找延迟超五百的项——path_exists 做过滤、query_first 取值两步配合。

使用建议

查询模式固定时表达式索引更精准。JSONB 写放大高于列存储,高频更新字段外置。jsonpath 条件放 WHERE 前先 EXPLAIN 验证索引利用。

代码示例

-- PostgreSQL GIN 索引让 @> 包含查询走索引
CREATE INDEX idx_events_payload
  ON events USING gin (payload jsonb_path_ops);

-- 包含运算符:文档包含给定结构即命中
SELECT
  event_id,
  payload->>'kind' AS kind,
  payload->'context'->>'region' AS region
FROM events
WHERE payload @> '{"context": {"region": "eu"}}'
ORDER BY created_at DESC
LIMIT 20;

-- 顶层数组包含:tags 数组含指定值
SELECT event_id, payload->'tags' AS tags
FROM events
WHERE payload->'tags' @> '["checkout"]'
  AND payload ? 'priority';  -- 顶层键存在性

-- jsonpath:复杂条件的结构化表达
SELECT
  event_id,
  jsonb_path_query_first(
    payload,
    '$.metrics[*] ? (@.name == "latency_ms" && @.value > 500).value'
  ) AS slow_latency
FROM events
WHERE jsonb_path_exists(
  payload,
  '$.metrics[*] ? (@.name == "latency_ms" && @.value > 500)'
);