Postgresql 与时间戳列查询相比,PostgresJSONB时间戳查询速度非常慢
我有一个Postgres 9.4.4数据库,其中有170万条记录,以下信息存储在一个名为Postgresql 与时间戳列查询相比,PostgresJSONB时间戳查询速度非常慢,postgresql,jsonb,Postgresql,Jsonb,我有一个Postgres 9.4.4数据库,其中有170万条记录,以下信息存储在一个名为data的JSONB列中,该列位于一个名为accounts的表中: data: { "lastUpdated": "2016-12-26T12:09:43.901Z", "lastUpdatedTimestamp": "1482754183" } } 实际的JSONB列存储了更多的信息,但我省略了不相关的数据。无法更改数据格式,因为这是旧信息 我正在尝试有效地获取lastUpdated值大于或等
data
的JSONB列中,该列位于一个名为accounts
的表中:
data: {
"lastUpdated": "2016-12-26T12:09:43.901Z",
"lastUpdatedTimestamp": "1482754183"
}
}
实际的JSONB列存储了更多的信息,但我省略了不相关的数据。无法更改数据格式,因为这是旧信息
我正在尝试有效地获取lastUpdated
值大于或等于某个参考时间的所有记录的计数(在以下示例中,我将使用2015-12-01T10:10:10Z
):
这需要超过22秒:
Aggregate (cost=843795.05..843795.06 rows=1 width=0) (actual time=22292.584..22292.584 rows=1 loops=1)
-> Seq Scan on accounts (cost=0.00..842317.05 rows=591201 width=0)
(actual time=1.410..22142.046 rows=1773603 loops=1)
Filter: ((data ->> 'lastUpdated'::text) >= '2015-12-01T10:10:10Z'::text)
Planning time: 1.234 ms
Execution time: 22292.671 ms
我已尝试添加以下文本索引:
CREATE INDEX accounts_last_updated ON accounts ((data->>'lastUpdated'));
但查询速度仍然相当慢,超过17秒:
Aggregate (cost=815548.64..815548.65 rows=1 width=0) (actual time=17172.844..17172.845 rows=1 loops=1)
-> Bitmap Heap Scan on accounts (cost=18942.24..814070.64 rows=591201 width=0)
(actual time=1605.454..17036.081 rows=1773603 loops=1)
Recheck Cond: ((data ->> 'lastUpdated'::text) >= '2015-12-01T10:10:10Z'::text)
Heap Blocks: exact=28955 lossy=397518
-> Bitmap Index Scan on accounts_last_updated (cost=0.00..18794.44 rows=591201 width=0)
(actual time=1596.645..1596.645 rows=1773603 loops=1)
Index Cond: ((data ->> 'lastUpdated'::text) >= '2015-12-01T10:10:10Z'::text)
Planning time: 1.373 ms
Execution time: 17172.974 ms
我还尝试按照中的说明进行操作,并尝试创建以下函数和索引:
CREATE OR REPLACE FUNCTION text_to_timestamp(text)
RETURNS timestamp AS
$$SELECT to_timestamp($1, 'YYYY-MM-DD HH24:MI:SS.MS')::timestamp; $$
LANGUAGE sql IMMUTABLE;
CREATE INDEX accounts_last_updated ON accounts
(text_to_timestamp(data->>'lastUpdated'));
但这并没有给我带来任何改进,事实上它速度较慢,查询需要24秒以上,而未编制索引的版本需要22秒:
explain analyze SELECT count(*) FROM "accounts"
WHERE text_to_timestamp(data->>'lastUpdated') >= '2015-12-01T10:10:10Z';
Aggregate (cost=1287195.80..1287195.81 rows=1 width=0) (actual time=24143.150..24143.150 rows=1 loops=1)
-> Seq Scan on accounts (cost=0.00..1285717.79 rows=591201 width=0)
(actual time=4.044..23971.723 rows=1773603 loops=1)
Filter: (text_to_timestamp((data ->> 'lastUpdated'::text)) >= '2015-12-01 10:10:10'::timestamp without time zone)
Planning time: 1.107 ms
Execution time: 24143.183 ms
在最后一次绝望中,我决定添加另一个timestamp列,并对其进行更新,使其包含与数据->'lastUpdated'
相同的值:
alter table accounts add column updated_at timestamp;
update accounts set updated_at = text_to_timestamp(data->>'lastUpdated');
create index accounts_updated_at on accounts(updated_at);
这给了我迄今为止最好的表现:
explain analyze SELECT count(*) FROM "accounts" where updated_at >= '2015-12-01T10:10:10Z';
Aggregate (cost=54936.49..54936.50 rows=1 width=0) (actual time=676.955..676.955 rows=1 loops=1)
-> Index Only Scan using accounts_updated_at on accounts
(cost=0.43..50502.48 rows=1773603 width=0) (actual time=0.026..552.442 rows=1773603 loops=1)
Index Cond: (updated_at >= '2015-12-01 10:10:10'::timestamp without time zone)
Heap Fetches: 0
Planning time: 4.643 ms
Execution time: 678.962 ms
然而,我非常希望避免仅仅为了提高一个查询的速度而添加另一列
这就给我留下了一个问题:是否有任何方法可以提高我的JSONB查询的性能,使其与单个列查询(我在
使用updated\u而不是数据->'lastUpdated'
的最后一个查询)一样高效?目前,我使用数据->>'lastUpdated'
查询JSONB数据需要17秒到24秒,而在列查询更新只需要678毫秒。JSONB查询会慢得多,这是没有意义的。我希望通过使用text\u to\u timestamp
函数可以提高性能,但事实并非如此(或者我做错了什么)。在第一次和第二次尝试中,大部分执行时间都花在索引复查或过滤上,必须读取每个json字段的索引命中,读取json是昂贵的。若索引命中几百行,查询速度会很快,但若索引命中数千行或数十万行,过滤/重新检查json字段将花费一些时间。在第二次尝试中,另外使用另一个函数会使情况变得更糟。
JSON字段用于存储数据,但不用于分析查询(如摘要、统计数据)中,以及在where条件中使用JSON对象的不良做法,至少作为您案例中的主要过滤条件。
你沮丧的最后一幕才是正确的选择:)
为了提高查询性能,您必须添加一个或多个带有键值的列,这些键值将在where条件下使用得最多。在第一次和第二次尝试中,大多数执行时间都花在索引重新检查或筛选上,这必须读取每个json字段的索引命中,读取json非常昂贵。若索引命中几百行,查询速度会很快,但若索引命中数千行或数十万行,过滤/重新检查json字段将花费一些时间。在第二次尝试中,另外使用另一个函数会使情况变得更糟。
JSON字段用于存储数据,但不用于分析查询(如摘要、统计数据)中,以及在where条件中使用JSON对象的不良做法,至少作为您案例中的主要过滤条件。
你沮丧的最后一幕才是正确的选择:)
为了提高查询性能,您必须添加一个或多个带有键值的列,这些键值将在where条件下使用得最多。为了让查询在JSON列上使用索引,您的查询必须使用与索引完全相同的表达式,因此您应该使用where text\u To\u timestamp(数据->“LastUpdate”)>…
)实际上,我使用了与您建议的相同的查询,只是由于错误,我没有将其包含在原始问题中。我已经更新了这个问题,以包含我在analyze命令中使用的精确查询,该命令产生24143.183毫秒的执行时间。我还缺少什么吗?为了让查询使用JSON列上的索引,您的查询必须使用与索引完全相同的表达式,因此您应该使用where text_to_timestamp(数据->>'lastUpdated')>…
)我确实使用了与您建议的相同的查询,只是我错误地没有将其包含在原始问题中。我已经更新了这个问题,以包含我在analyze命令中使用的确切查询,该命令产生24143.183毫秒的执行时间。还有什么我遗漏的吗?感谢您的回答。将此标记为正确答案,因为没有提供其他答案,而且我还没有找到解决方案。最后,我将我们的API更改为使用游标分页,并且不从该端点返回计数,客户机同意了这一点,因此我能够避免性能损失。不太理想,但这是务实的。谢谢你的回应。将此标记为正确答案,因为没有提供其他答案,而且我还没有找到解决方案。最后,我将我们的API更改为使用游标分页,并且不从该端点返回计数,客户机同意了这一点,因此我能够避免性能损失。不理想,但它是务实的
explain analyze SELECT count(*) FROM "accounts" where updated_at >= '2015-12-01T10:10:10Z';
Aggregate (cost=54936.49..54936.50 rows=1 width=0) (actual time=676.955..676.955 rows=1 loops=1)
-> Index Only Scan using accounts_updated_at on accounts
(cost=0.43..50502.48 rows=1773603 width=0) (actual time=0.026..552.442 rows=1773603 loops=1)
Index Cond: (updated_at >= '2015-12-01 10:10:10'::timestamp without time zone)
Heap Fetches: 0
Planning time: 4.643 ms
Execution time: 678.962 ms