【问题标题】:Slow PostgreSQL Query for Mins and Maxs within equal intervals in a time period一个时间段内相等间隔内的最小值和最大值的慢速 PostgreSQL 查询
【发布时间】:2014-09-09 13:06:57
【问题描述】:

我有一个包含许多测量设备的系统。这些测量值存储在表“sample_data”中。 每台设备一年内可能进行 1000 万次测量。大多数情况下,用户只对特定时期内等间隔内的 100 分钟最大配对感兴趣,例如过去 24 小时或过去 53 周。为了得到这 100 分钟和最大值,周期被分成 100 个相等的间隔。从每个间隔中提取最小值和最大值。您会推荐最有效的数据查询方法吗?到目前为止,我已经尝试了以下查询:

WITH periods AS (
  SELECT time.start AS st, time.start + (interval '1 year' / 100) AS en
  FROM generate_series(now() - interval '1 year', now(), interval '1 year' / 100) AS time(start)
)
SELECT s.* FROM sample_data s
  JOIN periods ON s.time BETWEEN periods.st AND periods.en 
  JOIN devices d ON d.customer_id = 23
  WHERE
    s.id = (SELECT id FROM sample_data WHERE device_id = d.id and time BETWEEN periods.st AND periods.en ORDER BY sample ASC LIMIT 1) OR
    s.id = (SELECT id FROM sample_data WHERE device_id = d.id and time BETWEEN periods.st     AND periods.en ORDER BY sample DESC LIMIT 1)

此查询大约需要 4 秒。它不是很合适,因为 sample_data 表每个设备最多可以包含 10M 行。 我看到它运行的方式不是非常优化,但不知道为什么。我以为我已经索引了这个查询中使用的所有关键字段。

您能推荐我一种更快地获取此类统计信息的方法吗?

表“设备”:

       Column       |            Type             |                      Modifiers                       
--------------------+-----------------------------+------------------------------------------------------
 id                 | integer                     | not null default nextval('devices_id_seq'::regclass)
 customer_id        | integer                     | 

    <Other fields skipped as they are not involved into the query>
Indexes:
"devices_pkey" PRIMARY KEY, btree (id)
"index_devices_on_iccid" UNIQUE, btree (iccid)

查询中指定的 customer_id = 23,它有 12 个设备,只有 4 个设备。

表“sample_data”:

     Column     |            Type             |                        Modifiers                         
----------------+-----------------------------+----------------------------------------------------------
id             | integer                     | not null default nextval('sample_data_id_seq'::regclass)
sample         | numeric                     | not null
time           | timestamp without time zone | not null
device_id      | integer                     | not null
customer_id    | integer                     | not null
Indexes:
"sample_data_pkey" PRIMARY KEY, btree (id)
"sample_data_device_id_time_sample_idx" btree (device_id, "time", sample)

它有大约 170 万行。每个属于 customer_id = 23 的 4 个设备大约有 720K 行。 该表现在由测试数据填充。

“选择版本()”结果:

PostgreSQL 9.3.5 on x86_64-apple-darwin13.3.0, compiled by Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn), 64-bit

track_io_timing 设置为“开启”

在此处解释(分析,缓冲区)结果: http://explain.depesz.com/s/kA12

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    我的猜测是性能的驱动因素是where 子句中的查询。让我们看看其中之一:

    WHERE s.id = (SELECT sd.id
                  FROM sample_data sd
                  WHERE sd.device_id = d.id and
                        sd.time BETWEEN periods.st AND periods.en
                  ORDER BY sd.sample ASC
                  LIMIT 1
                 ) 
    

    您在sample_data(devide_id, time, sample) 上有一个索引,并且您希望数据库引擎使用该索引。不幸的是,它只能为where 子句充分利用索引。由于between,它可能不会使用order by 的索引。

    是否可以使用time 编写order by

    WHERE s.id = (SELECT id
                  FROM sample_data
                  WHERE device_id = d.id and
                        time BETWEEN periods.st AND periods.en
                  ORDER BY time ASC
                  LIMIT 1
                 ) 
    

    【讨论】:

    • 知道了,谢谢。不幸的是我不能。 “样本”字段用于测量值,我使用它的排序来获取一段时间内的最小值和最大值。
    • @eug.nikolaev 。 . .嗯,这确实表明 Postgres 将使用索引做正确的事情。
    • 那么有没有可能通过查询得到那种数据呢?如果没有 - 看起来我将不得不准备数据插入的最大分钟数..
    • 戈登林诺夫,感谢您的解释!决定不在那里使用排序。它会起作用(现在)。
    猜你喜欢
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 2021-06-04
    • 1970-01-01
    • 2015-03-28
    • 2012-08-29
    相关资源
    最近更新 更多