Optimizing Postgres JSONB query with not null constraint
我有一个包含170万条记录的Postgres 9.4.4数据库,其中以下信息存储在名为
1 2 3 4 5 6 7 8 | DATA: { "lastUpdatedTime":"2016-12-26T12:09:43.901Z", "UID":"2c5bb7fd-1a00-4988-8d92-ffaa52ebc20d", "data": { "country":"UK", "verified_at":"2017-01-01T23:49:10.217Z" } } |
由于这是遗留信息,因此无法更改数据格式。
我需要获取国家/地区
到目前为止,我有以下查询:
1 2 3 4 5 6 | SELECT * FROM"accounts" WHERE (DATA @> '{"data": {"country":"UK" } }') AND (data->'data' ? 'verified_at') AND ((data->'data' ->> 'verified_at') IS NOT NULL) AND (DATA ->>'lastUpdatedTime' > '2016-02-28T05:49:08.511846') ORDER BY DATA ->>'lastUpdatedTime' LIMIT 100 OFFSET 0; |
以下索引:
1 2 | "accounts_idxgin" gin (DATA) "accounts_idxgin_on_data" gin ((DATA -> 'data'::text)) |
我设法将查询时间缩短到大约1000到4000毫秒
以下是查询分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Bitmap Heap Scan ON accounts (cost=41.31..6934.50 ROWS=9 width=1719) (actual TIME=7.273..1067.657 ROWS=23190 loops=1) Recheck Cond: ((DATA -> 'data'::text) ? 'verified_at'::text) FILTER: ((((DATA -> 'data'::text) ->> 'verified_at'::text) IS NOT NULL) AND ((DATA ->> 'lastUpdatedTime'::text) > '2016-02-01 05:49:08.511846'::text) AND (((DATA -> 'data'::text) ->> 'country'::text) = 'UK'::text)) ROWS Removed BY FILTER: 4 Heap Blocks: exact=16039 -> Bitmap INDEX Scan ON accounts_idxgin_on_data (cost=0.00..41.30 ROWS=1773 width=0) (actual TIME=4.618..4.618 ROWS=23194 loops=1) INDEX Cond: ((DATA -> 'data'::text) ? 'verified_at'::text) Planning TIME: 0.448 ms Execution TIME: 1069.344 ms (9 ROWS) |
我有以下问题
3:不是。这个案例在文档中明确提到。
如果列
2:通常的
1:不多。可能会有一些改进,但不要指望有巨大的性能优势。所以他们去了:
您可以使用
你有一个单一的,索引不友好的
所以,你的索引应该是这样的:
1 2 3 | CREATE INDEX accounts_idxgin_on_data ON accounts USING gin ((DATA -> 'data') jsonb_path_ops) WHERE (DATA -> 'data' ->> 'verified_at') IS NOT NULL; |
使用此索引,您可以使用以下查询:
1 2 3 4 5 6 | SELECT * FROM accounts WHERE (DATA -> 'data') @> '{"country":"UK"}' AND (DATA -> 'data' ->> 'verified_at') IS NOT NULL AND (DATA ->> 'lastUpdatedTime') > '2016-02-28T05:49:08.511Z' ORDER BY DATA ->>'lastUpdatedTime'; |
注意:对于正确的
http://rextester.com/QWUW41874
在玩了一下之后,我设法通过创建以下部分索引将查询时间从大约1000ms减少到350ms:
1 2 3 4 5 | CREATE INDEX index_accounts_partial_on_verified_at ON accounts ((data->'data'->'verified_at')) WHERE (data->'data'->>'verified_at') IS NOT NULL AND (data->'data' ? 'verified_at') AND (data->'data'->>'country' = 'UK'); |
我能够对此索引中的某些值进行硬编码,例如
对于任何感兴趣的人,我从这里找到了构建部分JSONB索引的详细信息
使用路径访问运算符可以更快地访问较低级别的对象:
1 2 3 4 5 | SELECT * FROM"accounts" WHERE DATA #>> '{data, country}' = 'UK' AND DATA #>> '{data, verified_at}' IS NOT NULL AND DATA ->> 'lastUpdatedTime' > '2016-02-28T05:49:08.511846' ORDER BY DATA ->> 'lastUpdatedTime' LIMIT 100 OFFSET 0; |
索引仅适用于顶级键。因此,对于列
还有两点:
-
我不认为有必要测试
verified_at 的存在。如果它不在那里它只是出现为NULL,所以它被相同的测试捕获。 -
如果JSON值正确且一致地格式化,则比较
timestamp 值的字符串表示可能有效。转换为timestamp 以确保安全。