Sql 每15分钟高效查询一行大型时间序列表
我有两个表,Sql 每15分钟高效查询一行大型时间序列表,sql,postgresql,aggregate,group-by,postgresql-performance,Sql,Postgresql,Aggregate,Group By,Postgresql Performance,我有两个表,conttagtable(t)和contfloattable(cf)。T大约有4.3万行。CF已超过90亿美元 我在两个表的tagindex列上都创建了一个索引。可以将此列视为conttagtable的唯一标识符,并将其视为conttagtable的外键。我没有在与另一个表相关的任何一个表上显式创建PK或外键,尽管这两个表上的tagindex列在逻辑上都与此数据相关,就好像conttagtable一样。tagindex是主键和contfloatable.tagindex其中是外键(t
conttagtable
(t)和contfloattable
(cf)。T大约有4.3万行。CF已超过90亿美元
我在两个表的tagindex
列上都创建了一个索引。可以将此列视为conttagtable
的唯一标识符,并将其视为conttagtable
的外键。我没有在与另一个表相关的任何一个表上显式创建PK或外键,尽管这两个表上的tagindex
列在逻辑上都与此数据相关,就好像conttagtable一样。tagindex
是主键
和contfloatable.tagindex
其中是外键(tagindex)引用conttagtable(tagindex)
。数据来自microsoft access转储,我不知道是否可以相信tagindex是唯一的,因此“唯一性”不强制执行
数据本身非常大
我需要从contfloatable
中获取任意选择的行,每15分钟contfloatable.dateandtime
间隔一次conttagtable.tagid
。因此,如果给定的tagid
的contfloatable
具有跨越30分钟的4000个样本,我需要0-14分钟范围内的样本和15-30分钟范围内的样本。15分钟范围内的任何一个样品均可接受;第一,最后,随机,随便什么
简而言之,我需要每15分钟获取一个样本,但每个t.tagname只需要一个样本。现在的样本每5秒记录一次,数据跨度为两年。这是一个大数据问题,在sql方面超出了我的理解范围。我在谷歌或搜索中尝试过的所有时间间隔解决方案都产生了很长的查询时间,因此不实用
- 我的索引是否足以进行快速连接?(省略时间间隔部分时,它们似乎是)
- 添加任何其他索引会使我受益吗
- 实现上述目标的最佳/最快查询是什么
Table "public.conttagtable" (t)
Column | Type | Modifiers
-------------+---------+-----------
tagname | text |
tagindex | integer |
tagtype | integer |
tagdatatype | integer |
Indexes:
"tagindex" btree (tagindex)
Table "public.contfloattable" (CF)
Column | Type | Modifiers
-------------+-----------------------------+-----------
dateandtime | timestamp without time zone |
millitm | integer |
tagindex | integer |
Val | double precision |
status | text |
marker | text |
Indexes:
"tagindex_contfloat" btree (tagindex)
我想看到的输出是这样的:
cf.dateandtime |cf."Val"|cf.status|t.tagname
--------------------------------------------------
2012-11-16 00:00:02 45 S SuperAlpha
2012-11-16 00:00:02 45 S SuperBeta
2012-11-16 00:00:02 45 S SuperGamma
2012-11-16 00:00:02 45 S SuperDelta
2012-11-16 00:15:02 45 S SuperAlpha
2012-11-16 00:15:02 45 S SuperBeta
2012-11-16 00:15:02 45 S SuperGamma
2012-11-16 00:15:02 45 S SuperDelta
2012-11-16 00:30:02 45 S SuperAlpha
2012-11-16 00:30:02 45 S SuperBeta
2012-11-16 00:30:02 45 S SuperGamma
2012-11-16 00:30:02 45 S SuperDelta
2012-11-16 00:45:02 42 S SuperAlpha
……等等
正如Clodoaldo所建议的,这是我的最新尝试,有没有加快速度的建议
with i as (
select cf.tagindex, min(dateandtime) dateandtime
from contfloattable cf
group by
floor(extract(epoch from dateandtime) / 60 / 15),
cf.tagindex
)
select cf.dateandtime, cf."Val", cf.status, t.tagname
from
contfloattable cf
inner join
conttagtable t on cf.tagindex = t.tagindex
inner join
i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime
order by floor(extract(epoch from cf.dateandtime) / 60 / 15), cf.tagindex
从上面查询计划:每15分钟一次:
with i as (
select cf.tagindex, min(dateandtime) dateandtime
from contfloattable cf
group by
floor(extract(epoch from dateandtime) / 60 / 15),
cf.tagindex
)
select cf.dateandtime, cf."Val", cf.status, t.tagname
from
contfloattable cf
inner join
conttagtable t on cf.tagindex = t.tagindex
inner join
i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime
order by cf.dateandtime, t.tagname
显示此查询的解释输出(如果有效),以便我们可以尝试优化。你可以把它贴在这个答案里
解释输出
"Sort (cost=15102462177.06..15263487805.24 rows=64410251271 width=57)"
" Sort Key: cf.dateandtime, t.tagname"
" CTE i"
" -> HashAggregate (cost=49093252.56..49481978.32 rows=19436288 width=12)"
" -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)"
" -> Hash Join (cost=270117658.06..1067549320.69 rows=64410251271 width=57)"
" Hash Cond: (cf.tagindex = t.tagindex)"
" -> Merge Join (cost=270117116.39..298434544.23 rows=1408582784 width=25)"
" Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
" -> Sort (cost=2741707.02..2790297.74 rows=19436288 width=12)"
" Sort Key: i.tagindex, i.dateandtime"
" -> CTE Scan on i (cost=0.00..388725.76 rows=19436288 width=12)"
" -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
" -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
" Sort Key: cf.tagindex, cf.dateandtime"
" -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)"
" -> Hash (cost=335.74..335.74 rows=16474 width=44)"
" -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)"
看起来您需要此索引:
create index cf_tag_datetime on contfloattable (tagindex, dateandtime)
创建后运行analyze
。现在请注意,大表上的任何索引都会对数据更改(insert等)产生显著的性能影响,因为每次更改都必须对其进行更新
更新
我添加了cf_tag_datetime索引(tagindex,dateandtime),下面是新的解释:
"Sort (cost=15349296514.90..15512953953.25 rows=65462975340 width=57)"
" Sort Key: cf.dateandtime, t.tagname"
" CTE i"
" -> HashAggregate (cost=49093252.56..49490287.76 rows=19851760 width=12)"
" -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)"
" -> Hash Join (cost=270179293.86..1078141313.22 rows=65462975340 width=57)"
" Hash Cond: (cf.tagindex = t.tagindex)"
" -> Merge Join (cost=270178752.20..298499296.08 rows=1408582784 width=25)"
" Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
" -> Sort (cost=2803342.82..2852972.22 rows=19851760 width=12)"
" Sort Key: i.tagindex, i.dateandtime"
" -> CTE Scan on i (cost=0.00..397035.20 rows=19851760 width=12)"
" -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
" -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
" Sort Key: cf.tagindex, cf.dateandtime"
" -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)"
" -> Hash (cost=335.74..335.74 rows=16474 width=44)"
" -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)"
它似乎已经及时上升:(但是,如果我删除order by子句(不完全是我需要的,但会起作用),就会发生这种情况,大幅减少:
"Hash Join (cost=319669581.62..1127631600.98 rows=65462975340 width=57)"
" Hash Cond: (cf.tagindex = t.tagindex)"
" CTE i"
" -> HashAggregate (cost=49093252.56..49490287.76 rows=19851760 width=12)"
" -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)"
" -> Merge Join (cost=270178752.20..298499296.08 rows=1408582784 width=25)"
" Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
" -> Sort (cost=2803342.82..2852972.22 rows=19851760 width=12)"
" Sort Key: i.tagindex, i.dateandtime"
" -> CTE Scan on i (cost=0.00..397035.20 rows=19851760 width=12)"
" -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
" -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
" Sort Key: cf.tagindex, cf.dateandtime"
" -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)"
" -> Hash (cost=335.74..335.74 rows=16474 width=44)"
" -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)"
我还没有尝试这个索引…不过我会尝试的。待机
现在再看一遍,我认为反向索引可能更好,因为它不仅可以用于合并联接
,还可以用于最终的排序
:
create index cf_tag_datetime on contfloattable (dateandtime, tagindex)
这是另一个公式。我很好奇它在完整数据集上的伸缩性。首先创建此索引:
CREATE INDEX contfloattable_tag_and_timeseg
ON contfloattable(tagindex, (floor(extract(epoch FROM dateandtime) / 60 / 15) ));
然后尽可能多地使用work\u mem
执行此操作:
SELECT
(first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime) / 60 / 15))).*,
(SELECT t.tagname FROM conttagtable t WHERE t.tagindex = x.tagindex) AS tagname
FROM contfloattable x ORDER BY dateandtime, tagname;
鬼鬼祟祟的袋熊:从上面的sql解释完整的数据集(没有建议的索引):
或者,在这里,只需要通过contfloatable
进行一次顺序传递,将值收集到一个tuplestore中,然后对其进行JOIN
ed以获得标记名。这需要大量工作\u mem
:
SELECT cf.dateandtime, cf.dataVal, cf.status, t.tagname
FROM
(
SELECT (first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime) / 60 / 15))).*
FROM contfloattable x
) cf
INNER JOIN
conttagtable t ON cf.tagindex = t.tagindex
ORDER BY cf.dateandtime, t.tagname;
鬼鬼祟祟的袋熊:从上面的sql解释完整的数据集(没有建议的索引):
如果它有效,您将希望在查询时投入尽可能多的work\u mem
。您没有提到系统的RAM,但您需要一块合适的内存;请尝试:
SET work_mem = '500MB';
…或者更多,如果你有至少4GB的RAM并且在64位CPU上。同样,我很想看看它在整个数据集上是如何工作的
顺便说一句,为了确保这些查询的正确性,我建议您altertable conttagtable ADD PRIMARY KEY(tagindex)
然后删除索引t_tagindex;
。这将需要一些时间,因为它将构建一个唯一的索引。这里提到的大多数查询都假设t.tagindex
在conttagtable
中是唯一的,这确实应该强制执行。唯一索引可以用于旧的非唯一中的其他优化>t_tagindex
不能,它可以产生更好的统计估计
此外,在比较查询计划时,请注意,
成本
不一定与实际执行时间成严格比例。如果估计值良好,则它应该大致相关,但估计值仅此而已。有时,由于行数错误等原因,您会看到高成本计划比假定的低成本计划执行得更快匹配或索引选择性估计、查询计划器推断关系的能力限制、意外的相关性或成本参数,如与实际系统不匹配的random\u page\u cost
和seq\u page\u cost
。每{t.tagname一行,间隔15分钟}。标记名只是描述设备的一列。本例中的设备是一个传感器,记录了某个时间点的值。要求我为每个设备(标记名)提供一个离散值每15分钟一次。我不太理解您的查询要求。平均而言,每个conttagable
记录大约有200000条contfloatable
记录。