2016-10-22 1 views
2

私たちが開発しているアプリケーションは、毎日約4〜500万行のデータを書きます。そして、過去90日間これらのデータを保存する必要があります。アプリケーションについて古いデータをより速くアクセスできるように保存する方法

id INT PRIMARY AUTOINCREMENT 
dt TIMESTAMP CURRENT_TIMESTAMP 
user_id varchar(20) 
data varchar(20) 

user_dataは、次の構造(簡体字)を持って7日齢より古い

  • データが更新/書き込まれません。
  • データはほとんどがuser_idに基づいてアクセスされます(つまり、すべてのクエリはWHERE user_id = XXXになります)
  • 現在約13000人のユーザーがいます。
  • ユーザーは引き続き古いデータにアクセスできます。しかし、古いデータにアクセスする際には、時間範囲ではなく、1日のデータのみを取得できるように制限できます。 (たとえば、ユーザーが2016-10-01のデータを取得しようとすると、その日のデータが取得され、2016-10-01 13:00 - 2016-10のデータを取得することはできません-01 14:00)。現時点で

、我々は最新のデータ(すなわち、7日以降)を格納するためにMySQL InnoDBを使用していて、それが正常に動作してinnodb_buffer_poolにフィットされます。

古いデータでは、user_data_YYYYMMDDの形式で小さいテーブルを作成しました。しばらくすると、私たちはこれらのテーブルがinnodb_buffer_poolに収まりきらないと思って、それが減速し始めました。

日付に基づくシャーディング、つまりuser_idsに基づくシャーディングは、より良い(つまり、ユーザーと日付に基づくより小さいデータセットを使用して、user_data_[YYYYMMDD]_[USER_ID]など)と考えています。これにより、テーブルをずっと小さな数にすることができます(せいぜい10K行程度)。日あたりの利用者(すなわちuser_data_[YYYYMMDD]_[USER_ID])ごとに保存するためにmysqlのテーブルを使用し

  • 周りに調査した結果、我々はいくつかのそこに選択肢があることを見出しました。

  • user_data_[YYYYMMDD]_[USER_ID]
  • ためのMongoDBのコレクションを使用して、私はこの中に見最大の詐欺は、ときに我々我々は、テーブル/コレクション/ファイルの膨大な数を持っているということです[USER_ID]/[YYYYMMDD].txt

に(JSONエンコードされた)古いデータを書きますこれを行う(すなわち、13000 x 90 = 1.170.000)。私たちが将来のスケーラビリティの点でこれに適切にアプローチしているのだろうかと思います。あるいは、これに対して他の標準化された解決策がある場合。

答えて

0

100万回以上の表が悪い考えです。ランタイム時にアプリケーションコードによる動的テーブル命名によってシャーディングすることは、私にとって好ましいパターンではありませんでした。このタイプの問題のための私の最初の仕事は、パーティショニングです。おそらく、1つのパーティション化されていないテーブルに400M +の行を追加することは望ましくありません。 MySQL 5.7では、サブパーティション化することもできます(しかし、それはより複雑になります)。私は最初に1日に1つのパーティションで日付フィールドのパーティションを範囲指定します。 user_idのインデックス。あなたが5.7でサブパーティション化をしたいのであれば、範囲パーティションを日付別に、そしてサブパーティションをuser_idでハッシュすることをお勧めします。出発点として、16〜32ハッシュバケットを試してみてください。それでもuser_idフィールドのインデックスを作成します。

編集:ここで一緒にプレイするために何か:データベースをスケーリング

CREATE TABLE user_data (
    id INT AUTO_INCREMENT 
    , dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
    , user_id VARCHAR(20) 
    , data varchar(20) 
    , PRIMARY KEY (id, user_id, dt) 
    , KEY (user_id, dt) 
) PARTITION BY RANGE (UNIX_TIMESTAMP(dt)) 
    SUBPARTITION BY KEY (user_id) 
    SUBPARTITIONS 16 (
    PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-25')), 
    PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-26')), 
    PARTITION p3 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-27')), 
    PARTITION p4 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-28')), 
    PARTITION pMax VALUES LESS THAN MAXVALUE 
); 

-- View the metadata if you're interested 
SELECT * FROM information_schema.partitions WHERE table_name='user_data'; 
+0

ありがとう、Joshua。間違いなくPARTITIONの詳細を探そうとします。 –

1

は、アプリケーションに固有の問題です。たいていの場合、他の誰かのアプローチは、ほぼすべてのアプリケーションが独自の方法でデータを書き込むため、使用できません。したがって、データをどのように管理するかを把握する必要があります。

あなたのデータが拡大し続ける場合、最良の解決策は、異なるサーバー間でデータを配信できるシャドリングです。異なるテーブルを作成するような単一のサーバーにバインドされている限り、メモリ、記憶域、処理能力などのリソース制限に晒されています。無制限に増やすことはできません。

ビジネスユースケースに基づいて把握しなければならないデータの配布方法。あなたが言及したように、あなたが古いデータをもっと要求していなければ、データベースを日付に配布する最良の方法です。 2016年のデータの場合はDB、2015年の場合はDBのようになります。後で古いデータがあるサーバーを削除またはシャットダウンすることがあります。

0

これは大きなテーブルですが、扱いにくいものではありません。

user_id + dtがUNIQUEの場合は、それをPRIMARY KEYにして、idなら削除して、スペースを節約してください。 (分単位で...)

SMALLINT UNSIGNED(2バイト)を標準化するか、安全性を高めるためにMEDIUMINT UNSIGNED(3バイト)です。これにより、かなりのスペースが節約されます。

大きなテーブルの速度(I/O)にはスペースを保存することが重要です。 92個のパーティションを持つ

PARTITION BY RANGE(TO_DAYS(dt)) 

- あなたが必要とする90、プラス1 DROPpedと満たされた一つであることが待っています。詳細hereを参照してください。

ENGINE=InnoDB 

PRIMARY KEYをクラスタリングする。

PRIMARY KEY(user_id, dt) 

これが「一意」の場合、単一のユーザーの任意の時間範囲に効率的にアクセスできます。注:「ちょうど1日」の制限を削除することができます。しかし、に関数内にdtを隠すことなくクエリを作成する必要があります。私はお勧め:

WHERE user_id = ? 
    AND dt >= ? 
    AND dt < ? + INTERVAL 1 DAY 

さらに、

PRIMARY KEY(user_id, dt, id), 
INDEX(id) 

も(USER_ID、dt)が一意でない場合でも、効率的です。 idをPKに追加することは、それを一意にすることです。 INDEX(id)の追加はAUTO_INCREMENTを満足させることです。 (いいえ、UNIQUE(id)は必要ありません。)(SIGNEDです)

INT --> BIGINT UNSIGNED ?? 

INTは約20億で実施トップます。それはほんの数年後に起こります。それは大丈夫ですか?そうでない場合は、BIGINT(8バイト対4)が必要です。

このパーティション設計では、7日間のルールは考慮されません。ルールを保持し、アプリに適用することを選択できます。

BY HASH 

は同様に動作しません。

SUBPARTITION 

は一般的に役に立たない。

他のクエリはありますか?もしそうならば、でなければならない。

トラフィックが1台のサーバーに多すぎる場合は、user_idによるシャーディングが便利です。 MySQL自体にはシャーリングソリューションはありません(まだ)。

+0

詳細な説明をありがとうございます。間違いなくPARTITIONを見ていきます。私はすべてのユーザーが1つのテーブル(区切り)にある場合、それは同じ種類のロックの種類は、同じ日付範囲の異なるユーザーの同時読書があるときには興味がありますか? –

+0

ロックはInnoDBの行レベルにあります。したがって、(システム全体がビジー状態ではなく)別のユーザーのクエリ間に干渉はありません。 –

+0

InnoDBの使用では、テーブルのサイズ(1日あたり約2GB×90 = 180GB)が 'innodb_buffer_pool'に収まらない可能性があります。 'dt'の上に' user_id'に基づいて検索する必要があるので、これはパーティションのクエリ速度に影響しますか? –

0

https://www.percona.com/software/mysql-database/percona-tokudb

アーカイブデータでTokuDBエンジンを試してみてはTokuDBに最適です。 InnoDBと比較してデータセットを処理するためのANDメモリと、アーカイブされたmyisamの約2〜3倍のディスクスペースを必要としません。

+0

ありがとうございます。間違いなくTokuDBを見ていきます。 –

関連する問題