我正在尝试每分钟记录200个功率计。每个功率计都有唯一的(pmid)。架构如下:
CREATE TABLE `pmd` (
`datatime` datetime NOT NULL,
`pmid` smallint(5) unsigned NOT NULL,
`statusid` tinyint(3) unsigned NOT NULL,
`I1` double NOT NULL,
`I2` double NOT NULL,
`I3` double NOT NULL,
`I0` double NOT NULL,
PRIMARY KEY (`datatime`,`pmid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我的用例是检索每小时(其中分钟= 0),每日(其中小时和分钟= 0),以及特定功率计的每月记录(其中日= 1&小时和分钟= 0)。
在前2个月,查询有效并且速度很快。但是,记录越多,查询时间就越慢。
我想征求意见,如何提高绩效? 我心中有一些想法: 1.将日期时间更改为单独的字段,如:
`year_2digit` tinyint NOT NULL,
`month` tinyint NOT NULL,
`day` tinyint NOT NULL,
`hour` tinyint NOT NULL,
`minute` tinyint NOT NULL,
2.每月创建新表。 更新:今天我在网上阅读了更多内容,并且有一项称为分区的技术。我对它感兴趣,因为它没有改变架构。我想按年和月分区。我可以就分区问题发表看法吗?
答案 0 :(得分:1)
你的第一个想法是我可能会做一些小的例外:
而不是
`year_2digit` tinyint
我会用
`year` year
YEAR数据类型与TINYINT(1 Byte)具有相同的存储大小。
保留datatime
列。您可能需要它用于其他查询。例如,有效的范围条件(如BETWEEN)是MySQL中具有多列的噩梦。
最新的MySQL和MariaDB版本支持生成(虚拟)列。您可以使用该功能自动生成datetime
列中的值。如果您的版本不支持,我会使用触发器。
在(minute, hour, day, month, year)
上定义综合索引。它将支持以下所有条件:
WHERE `minute` = 0
WHERE `minute` = 0 AND `hour` = 0
WHERE `minute` = 0 AND `hour` = 0 and `day` = 1
WHERE `minute` = 0 AND `hour` = 0 and `day` = 1 AND `month` = 1
WHERE `minute` = 0 AND `hour` = 0 and `day` = 1 AND `month` = 1
AND `year` BETWEEN `2010` AND `2020`
答案 1 :(得分:0)
我的用例是检索每小时(其中分钟= 0),每日(其中小时和分钟= 0)和每月记录(其中日= 1&amp;小时&分; = 0)特定功率计< /强>
第1部分 - 获得正确的PK
由于您正在那个订单中寻找pmid
,PRIMARY KEY(pmid, datetime)
。并使用InnoDB,以便PK是&#34;群集&#34;与数据。
这样,您需要的行不会遍布整个表,而是聚集在一起。至少是一分钟。
您的查询必须是以下形式:
WHERE pmid = <constant>
AND `datetime` >= '2016-07-11'
AND `datetime` < '2016-07-11' + INTERVAL 3 DAY
AND MINUTE(`datetime`) = 0
即,指定特定的pmid并提供日期时间范围 - 这将把扫描重点放在表的有限部分上。然后进行每分钟/每小时/等过滤。
第2部分 - 汇总表
上述技术适用于&#34;分钟&#34;在很短的时间内。它非常适用于白天&#34;在很长的时间范围内。这不能通过索引修复。
嗯,术语&#34;汇总表&#34;不太适合这里,但无论如何......另外两个桌子,一个用于上午,一个用于午夜。将这些读数冗余地存储在这些较小的表中。这些表具有相同的模式,只有更少的数据。查询会更快,因为他们再次不会跳过数据。
这方面的一个变体是使用TRIGGER
将上午小时和午夜值复制到其他表。 (而不是使用应用程序代码。)
<强>分区吗
这是一个半生不熟的想法,所以我不确定它是否可行和有效。
使用PARTITION BY LIST
并拥有3个分区:&#39;分钟&#39;&#39;小时&#39;和&#39;天&#39;。有一个额外的列具有这三个值(以某种方式编码以保持分区快乐 - 所以可能是一个tinyint)。我们说你有
scale TINYINT UNSIGNED NOT NULL -- 1=minute, 2=hour, 3=day
将其添加到WHERE
:
AND scale >= 2 -- to get hourly data
插入新数据时:
INSERT INTO pmd
(scale, pmd, `datetime`, ...)
VALUES
(...<see below>, $pmd, $datetime, ...)
其中scale
值是通过挑选(在客户端代码或存储函数中)datetime
来计算的。
这避免了第2部分隐含的冗余数据,同时提供3个表(以3个分区的形式)。 &#34;聚类&#34;很棒。
这需要:
PRIMARY KEY(pmd, `datetime`, scale)
我说&#34;半烤&#34;,但是当我输入所有这些时,它似乎在一起。
如果您成功使用分区,那么您将赢得#34;案例5&#34;在http://mysql.rjweb.org/doc.php/partitionmaint - 我一直在寻找&#34;案例5&#34;好几年了。