2017-07-31 3 views
2

私は200人のpowermetersの1分あたりの記録を取ろうとしています。各powermeterにはuniqueid(pmid)があります。スキーマは次のようである:mysql DBに分単位のデータを効果的に保存して検索するには?

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)、および月次レコード(日= 1 &時間&分= 0)、(ここで、分= 0)時給取得している

の特定のpowermeter。

最初の2か月では、クエリが機能し、高速です。しかし、レコードが増えるほど、クエリ時間は非常に遅くなります。

私はパフォーマンスを改善するための意見を聞きたいと思いますか? 私の心の中でいくつかの考えがあります。各月の

`year_2digit` tinyint NOT NULL, 
`month` tinyint NOT NULL, 
`day` tinyint NOT NULL, 
`hour` tinyint NOT NULL, 
`minute` tinyint NOT NULL, 

2.create新しいテーブル:別々のフィールドへ 1.changeの日時は次のように。 更新:今日私はウェブ上でもっと多くを読んでいました。私はそれがスキーマを変更しないので、それに興味があります。私は年と月に分割したいと思います。私はパーティショニングについてあなたの意見もありますか?

代わり

`year_2digit` tinyint 

の私は

`year` year 

YEARデータ型を使用するのと同じストレージ・サイズを持っています

+0

問題がインデックスの不足のように見える – Strawberry

+1

あなたのMySQLのバージョンは何ですか?あなたの質問は何ですか? –

+0

私のMySQLバージョンは5.7.18です。私は通常これを使用して1時間ごとのレコードを見つけます:select * from pmdここでpmid = 2、データタイム( '2017-04-01 00:00'、 '2017-04-01 00:01'、...、 '2017 -04-01 23:59 ')制限1440; – marco

答えて

1

あなたの最初のアイデアは、私はおそらくまた、いくつかのマイナーな例外を除いて何をするのかでありますTINYINT(1バイト)。

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` 
+0

あなたの複合インデックスはあなたが言及しているものと逆の順序で、 '(年、月、日、時、分)'にする必要があります。 year、month、day、hourのwhere句を使用して、この複合インデックスを使用することができます。 – nos

+0

@nos提案したインデックスも同様に有用かもしれませんが、固定年なしのどのような状況でも役に立たなくなります。 –

0

私のユースケースは、毎時間、毎日(ここで、分= 0)、(ここで、時間&分= 0)、および月次レコード(日= 1 &を取得している:それは、次の条件のすべてをサポートします時間&分= 0)特定の粉末計

パート1 - あなたは1 pmidを探しているのでそのためには、PRIMARY KEY(pmid, datetime)を持って、右のPKに

を取得します。また、InnoDBを使用して、PKがデータと「クラスタ化」されるようにします。

このように、必要な行はテーブル全体に散らばっていませんが、一緒にクラスタ化されています。少なくとも分単位で。

あなたのクエリは、この形式である必要があります。

ある
WHERE pmid = <constant> 
    AND `datetime` >= '2016-07-11' 
    AND `datetime` < '2016-07-11' + INTERVAL 3 DAY 
    AND MINUTE(`datetime`) = 0 

、特定のPMIDを指定して、日時の範囲を提供する - これは、テーブルの限られた部分にスキャンを焦点を当てます。次に、微妙に/時間ごとに/ etcのフィルタリングを行います。

パート2 - サマリー表(S)

上記技術は、短い時間範囲にわたって、「分による」の非常に良好に機能します。それは長い時間の範囲で "日中"にひどく働く。これは索引で修正することはできません。

「サマリーテーブル」という用語は、ここにはあまり適していませんが、とにかく... 1つはトップタイム用、もう1つは深夜用の2つのテーブルをもう1つ作成します。これらの読みをこれらの小さなテーブルに冗長に格納します。テーブルには同一のスキーマがあり、データはほとんどありません。クエリはデータの周りを飛び回らないため、はるかに高速になります。

これは、TRIGGERを使用して、1時間の値と真夜中の値を他のテーブルにコピーすることです。 (アプリケーションコードを使用する代わりに)

パーティショニング?

これは半分焼いたアイデアなので、実現可能で効率的なのかどうかはわかりません。

PARTITION BY LISTを使用し、 '分'、 '時間'、 '日'の3つのパーティションがあります。これらの3つの値を持つ余分な列があります(何らかの方法でエンコードされているので、パーティショニングは幸せに保たれます)。

AND scale >= 2 -- to get hourly data 

新しいデータを挿入する場合::さんはWHEREにこれを追加あなたは

scale TINYINT UNSIGNED NOT NULL -- 1=minute, 2=hour, 3=day 

を持っているとしましょう

scale値はクライアントコードに(離れて選ぶかによって計算される
INSERT INTO pmd 
    (scale, pmd, `datetime`, ...) 
    VALUES 
    (...<see below>, $pmd, $datetime, ...) 

ストアドファンクション)datetime

これは、3つのテーブル(3つのパーティションの形式)を提供しながら、パート2が示す冗長データを回避します。 「クラスタリング」は優れています。

必要なこの:

PRIMARY KEY(pmd, `datetime`, scale) 

私は「半分は焼き」と述べたが、私はこのすべてを入力したとして、一緒に保持するようです。

パーティショニングで成功した場合は、http://mysql.rjweb.org/doc.php/partitionmaintで「ケース5」の賞を獲得します。私は数年間、「ケース5」を探していました。

関連する問題