示例说明
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)'
);