与timestamp列查询相比,Postgres JSONB时间戳查询非常慢

时间:2017-03-23 06:24:44

标签: postgresql jsonb

我有一个包含170万条记录的Postgres 9.4.4数据库,其中以下信息存储在名为accounts的表中名为data: { "lastUpdated": "2016-12-26T12:09:43.901Z", "lastUpdatedTimestamp": "1482754183" } } 的JSONB列中:

lastUpdated

实际的JSONB列存储了更多信息,但我省略了不相关的数据。由于这是遗留信息,因此无法更改数据格式。

我试图有效地获取2015-12-01T10:10:10Z值大于或等于某个参考时间的所有记录的计数(我在以下示例中使用explain analyze SELECT count(*) FROM "accounts" WHERE data->>'lastUpdated' >= '2015-12-01T10:10:10Z'; ) :

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

这需要22秒:

CREATE INDEX accounts_last_updated ON accounts ((data->>'lastUpdated'));

我尝试添加以下文字索引:

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

但查询仍然相当缓慢,超过17秒:

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'));

我也尝试按照Create timestamp index from JSON on PostgreSQL中的说明操作,并尝试创建以下功能和索引:

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

但这并没有给我任何改进,事实上它更慢,查询时间超过24秒,而未编制索引的时间则为22秒:

data->>'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

这给了我迄今为止最好的表现:

updated_at

但是,我非常希望避免添加其他专栏,以提高一次查询的速度。

这给我留下了以下问题:有没有办法提高我的JSONB查询的性能,因此它可以像单个列查询一样高效(我使用的最后一个查询{{ 1}}而不是data->>'lastUpdated')?就目前而言,我需要17秒到24秒才能使用data->>'lastUpdated'查询JSONB数据,而查询updated_at列只需要678毫秒。 JSONB查询会慢得多,这没有意义。我希望通过使用text_to_timestamp函数来提高性能,但事实并非如此(或者我做错了)。

1 个答案:

答案 0 :(得分:2)

在你的第一次和第二次尝试中,大多数执行时间花在索引重新检查或过滤上,这必须读取每个json字段索引命中,读取json是昂贵的。如果索引命中几百行,查询将很快,但如果索引达到数千或数十万行 - 过滤/重新检查json字段将花费一些时间。在第二次尝试中,另外使用另一个功能会使情况更糟。 JSON字段适用于存储数据,但不打算用于分析查询,如摘要,统计及其不良做法,以便在条件的情况下使用json对象,至少作为主要过滤条件,如您的情况。 最后一次沮丧你的行为是正确的方法:)

要提高查询性能,您必须添加一个或多个具有键值的列,这些列将在条件适用的地方使用最多。

相关问题