web-dev-qa-db-ja.com

1000万のエントリからレコードをフェッチするための最適化されたクエリ

テーブルには2つのフィールドがあります

| UniqueKey |  TimeStamp |
-------------------------
| xfsddddq  | 1024125412 |
| xfstttdx  | 1024125413 |<
| xfsdxxau  | 1024125415 |

推定では1000万件のレコードがあります。 UniqueKey(CURRENT_TIME - 5MINS)内に存在するかどうかを確認する必要があります。


現在

  • 1時間ごとにテーブルをフラッシュします(これ以上減らすことはできません)。
  • 1時間で1000万より多くのレコードを挿入します。時間が増えると、1つのレコードをフェッチするために実行される行の数も増えるため、実行時間が長くなります。
  • クエリを制限して(CURRENT_TIME-5MINS)から1つのレコードをチェックするか、結果を効果的にフェッチして、実行時間が5分と59分で同じになるようにする方法。
2
nuthan

テーブルmydb.mytable UniqueKeyとtimeStampを使用して、UniqueKeyが過去5分以内に存在するかどうかを確認するには、これを実行します

SELECT COUNT(1) FROM mydb.mytable
WHERE UniqueKey = ????
AND timeStamp >= ( NOW() - INTERVAL 5 MINUTE );

または

SELECT COUNT(1) FROM mydb.mytable
WHERE UniqueKey = ????
AND timeStamp >= ( NOW() - INTERVAL 300 SECOND );

値は何を示していますか???

  • 0を取得した場合、UniqueKeyは5分より古い
  • 1を取得した場合、UniqueKeyは5分以内です

UniqueKeyに一意のインデックスがあることを確認してください。

試してみる !!!

タイムスタンプはUNIXタイムスタンプなので、 UNIX_TIMESTAMP() 関数を使用してコードを調整します

SELECT COUNT(1) FROM mydb.mytable
WHERE UniqueKey = ????
AND timeStamp >= UNIX_TIMESTAMP(NOW() - INTERVAL 5 MINUTE);
1
RolandoMySQLDBA

これを実行するクエリは次の形式になります。

SELECT t.UniqueKey
  FROM mytable t
 WHERE t.UniqueKey = ?
   AND t.timeStamp >= NOW() - INTERVAL 5 MINUTE
   AND t.timeStamp <= NOW()
  LIMIT 1

(このクエリでは、timeStamp列がデータ型[〜#〜] timestamp [〜#〜]として定義されていると想定しています。)

クエリは1行または0行を返します。これは、指定したUniqueKeyが過去5分間に「存在する」か、「存在しない」ことを示します。

そのクエリはインデックスを要求します:

... ON mytable (UniqueKey,timeStamp)

UniqueKeyがテーブル内で本当に一意である場合、UniqueKeyのみのインデックスで十分です。列のデータ型、ストレージエンジン、既存のキー/インデックス、データ分布などに関する知識がなければ、何が最善かを実際に判断することはできません。


[〜#〜]更新[〜#〜]

(上記の答えはStackOverflowからコピーされたもので、質問が最初に表示されました。)

OPは質問をサンプルデータで更新し、timeStamp列がおそらく[〜#〜] not [〜#〜] MySQLであることを示しています[〜#〜] timestamp [〜#〜]ですが、整数値のようです。

整数データ型(INT、BIGINTなど)として格納されていると想定し、整数値が日時順に昇順であるとすると、最も効率的なクエリは次の形式になります。

SELECT t.UniqueKey
  FROM mytable t
 WHERE t.UniqueKey = ?
   AND t.timeStamp >= ?
   AND t.timeStamp <= ?
  LIMIT 1

値は述語で提供されます(timeStampと比較するために整数値であると想定されています。これは単なる前提であり、実際には整数値であるという知識はありません。また、知識もありません。日時がどのようにエンコードされるかについて。

timeStamp値は、エポックの開始(UTC 1970年1月1日の真夜中)からの秒数を表す整数である可能性が非常に高いです。これは広く採用されている規則ですが、timeStampという名前のすべての列にこの方法でエンコードされた値があるという「規則」ではなく、単なる規則です。

だから、私は仮定に基づいてさらなる提案をすることをためらっています。 MySQLは変換を可能にする関数を提供しますが、いくつかは非常に便利です。しかし、実際にtimeStamp列に格納されている値がわからない。これらの推奨事項は、仮定に基づいています。

3
spencer7593

最初に、UniqueKeyのタイプをテキストからINTに変更する必要があります。マルチバイト文字に照合順序を適用する必要がないため、数値検索は文字列検索よりも高速です。

次に、レコードとセットを区別するために必要なすべてのフィールドを含む複雑なインデックスを作成する必要があります。ここでの秘訣-インデックス定義でフィールドをリストする順序は非常に重要であり、高速化するクエリによって異なります。クエリ用

_SELECT * FROM table 
 WHERE UniqueKey = someValue
   AND TimeStamp > NOW() - INTERVAL 5 MINUTE
 ORDER BY TimeStamp DEC
 LIMIT 1;
_

最小の結果セットの生成からより広い条件までの範囲のインデックスフィールドを順序付ける必要があります。テーブルに1時間で1000万レコードが含まれている場合、5分の部分には約1/12または800kレコードが含まれます。 1000個の一意のキーがある場合次に、同じ一意のキーを持つ約1/1000または100kのレコードがあります。したがって、インデックスは最初にUniqueKeyで定義し、次にTimestampで定義する必要があります。

_CREATE TABLE ...
. . . . .
INDEX `Key_TS` (`UniqueKey`, `TimeStamp`)
. . . . . 
_

5秒の間隔内で選択する場合は、タイムスタンプがより狭い基準になる可能性があるため、インデックス定義でUniqueKeyの前にタイムスタンプフィールドを記述する必要があります。インデックスをその場で変更できない限り、同じフィールドで異なる順序で2つのインデックスを定義する必要があります。

_CREATE TABLE ...
. . . . .
INDEX `UKey_TS` (`UniqueKey`, `TimeStamp`)
INDEX `TS_UKey` (`TimeStamp`, `UniqueKey`)
. . . . . 
_

次に、推測する代わりに、mysqlに特定のインデックスを使用することを明示的に強制する必要があります。

_SELECT * FROM table USE INDEX (`UKey_TS`)
 WHERE UniqueKey = someValue
   AND TimeStamp > NOW() - INTERVAL 5 MINUTE
 ORDER BY TimeStamp DEC
 LIMIT 1;
_

どのインデックスがより良い結果を得るかをテストする必要があります。インデックスは高価なので、本当に必要なインデックスのみを作成してください。

もう1つの提案は、クエリ内で一定であるすべての値を事前計算することです。実際、NOW() - INTERVAL 5 MINUTEは、_UniqueKey = someValue_を持つレコードの数だけ再計算されます。値を変数に事前計算すると、計算は1回だけ実行されます。

_SET @min_ts = NOW() - INTERVAL 5 MINUTE;
SELECT * FROM table USE INDEX (`UKey_TS`)
 WHERE UniqueKey = someValue
   AND TimeStamp > @min_ts
 ORDER BY TimeStamp DEC
 LIMIT 1;
_
1
Kondybas