如何在mysql DB中有效地存储和搜索每分钟数据?

时间:2017-07-31 20:44:42

标签: mysql performance

我正在尝试每分钟记录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.每月创建新表。 更新:今天我在网上阅读了更多内容,并且有一项称为分区的技术。我对它感兴趣,因为它没有改变架构。我想按年和月分区。我可以就分区问题发表看法吗?

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

由于您正在那个订单中寻找pmidPRIMARY 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;好几年了。