11

「エンティティ」テーブルで行った変更を保存したい。これはログのようになります。現在、MySQL では次のテーブルで実装されています。

CREATE TABLE `entitychange` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `entity_id` int(10) unsigned NOT NULL,
  `entitytype` enum('STRING_1','STRING_2','SOMEBOOL','SOMEDOUBLE','SOMETIMESTAMP') NOT NULL DEFAULT 'STRING_1',
  `when` TIMESTAMP NOT NULL,
  `value` TEXT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  • entity_identity= 私のテーブルの主キー。
  • entitytype=entityテーブルで変更されたフィールド。1 つのフィールドのみが変更されることもあれば、複数のフィールドが変更されることもあります。1 つの変更 = 1 行。
  • value= フィールドの「新しい値」の文字列表現。

フィールドentity.somedoubleを 3 から 2 に変更する例として、これらのクエリを実行します。

UPDATE entity SET somedouble = 2 WHERE entity_id = 123;
INSERT INTO entitychange (entity_id,entitytype,value) VALUES (123,'SOMEDOUBLE',2);

select過去 15 日間の特定のエンティティとエンティティ タイプの変更が必要です。例:過去 15 日以内のSOMEDOUBLEentity_idの最後の変更。123

今、私が嫌いなことが2つあります:

  1. ほとんどTEXT(1% 未満) は実際にはテキストではありませんが、私の場合、ほとんどの値はDOUBLE. これは大きな問題ですか?
  2. テーブルにはすでに 2 億行あるため、テーブルの挿入が非常に遅くなります。現在、このため、サーバーの負荷は最大で 10 ~ 15 です。

私の質問:これら 2 つの「ボトルネック」に対処するにはどうすればよいですか? スケーリングする必要があります。

私のアプローチは次のとおりです。

  1. 次のように保存します: http://sqlfiddle.com/#!2/df9d0 (参照をクリック) -entitychangeテーブルに変更を保存し、そのデータ型に従って値を保存しますentitychange_[bool|timestamp|double|string]
  2. パーティショニングを使用HASH(entity_id)- 50 個までのパーティションを考えました。
  3. MongoDB など、別のデータベース システムを使用する必要がありますか?
4

8 に答える 8

5

あなたが言及した問題に直面していた場合、次のように LOG テーブルを設計します。

  1. EntityName: (文字列) 操作されているエンティティ。(必須)
  2. ObjectId: 操作されているエンティティ、主キー。
  3. FieldName: (文字列) エンティティ フィールド名。
  4. OldValue: (文字列) エンティティ フィールドの古い値。
  5. NewValue: (文字列) エンティティ フィールドの新しい値。
  6. UserCode: アプリケーション ユーザーの一意の識別子。(必須)
  7. TransactionCode: エンティティを変更する操作には、固有のトランザクション コード (GUID など) が必要です (必須)。
    複数のフィールドを変更するエンティティの更新の場合、これらの列は、更新のすべての変更を追跡するためのキー ポイントになります(トランザクション)
  8. ChangeDate: 取引日。(必須)
  9. FieldType: TEXT や Double などのフィールド タイプを示す列挙またはテキスト。(必須)

このアプローチを使用すると、
任意のエンティティ (テーブル) を追跡できます。
レポートは読み取り可能
になります。変更のみがログに記録されます。
トランザクションコードは、ワンアクションで変更を検出するためのポイントになります。

ところで

Store the changes in the entitychange table and then store the value 
according to its datatype in entitychange_[bool|timestamp|double|string]

必要ありません。単一のテーブルには変更とデータ型があります

Use partitioning by HASH(entity_id)

ChangeDate によるパーティショニング、またはバックアップするのに十分古い changeDate のバックアップ テーブルを作成し、メインの LOG テーブルから削除することをお勧めします。

Should I use another database system, maybe MongoDB?

どのデータベースにも独自の長所と短所があり、どの RDBMS でも設計を使用できます。MongoDB のような文書ベースのデータベースの有用な比較は、ここで見つけることができます

役立つことを願っています。

于 2013-06-15T21:02:55.777 に答える
1

多くの詳細なテストを行うことをお勧めしますが、私のテストでは、以前に投稿したテーブル定義を使用して INSERT と SELECT の両方で非常に良い結果を達成しています。誰でも簡単に繰り返して、より良い結果が得られるかどうかを確認できるように、テストの詳細を詳しく説明します。テストの前にデータをバックアップしてください。
これらは単なるテストであり、実際のケースを反映または改善しない可能性があると言わざるを得ませんが、学習の良い方法であり、おそらく有用な情報と結果を見つける方法です.

ここで見たアドバイスは非常に優れており、TEXT の代わりに事前定義された型 VARCHAR とサイズを使用することで、速度が大幅に向上することに気付くでしょう。ただし、速度を上げることはできますが、データの整合性の理由から MyISAM を使用せず、InnoDB を使用することをお勧めします。

テスト:

1. テーブルを設定し、2 億のデータを挿入します。

CREATE TABLE `entity_versionable` (
  `version` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `fk_entity` INT(10) UNSIGNED NOT NULL,
  `str1` VARCHAR(255) DEFAULT NULL,
  `str2` VARCHAR(255) DEFAULT NULL,
  `bool1` TINYINT(1) DEFAULT NULL,
  `double1` DOUBLE DEFAULT NULL,
  `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`version`,`fk_entity`)
) ENGINE=INNODB AUTO_INCREMENT=230297534 DEFAULT CHARSET=latin1

約 35 分で +2 億行をテーブルに挿入するには、petermがテーブルを埋める最良の方法の 1 つに答えた私の他の質問を確認してください。それは完全に機能します。

ランダム データのない 2 億行を挿入するために、次のクエリを 2 回実行します (ランダム データを挿入するたびにデータを変更します)。

INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
SELECT 1, 'a1', 238, 2, 524627, '2013-06-16 14:42:25'
FROM
(
    SELECT a.N + b.N * 10 + c.N * 100 + d.N * 1000 + e.N * 10000 + f.N * 100000 + g.N * 1000000 + h.N * 10000000 + 1 N FROM 
     (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) f
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) g
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) h
) t;


* 2 億行の実際のランダム データを含む元のテーブルが既にあるため、テーブル データとスキーマをエクスポートし、同じスキーマを持つ新しい Testing テーブルにインポートするだけで、おそらくテーブルに入力する必要はありません。そうすれば、実際のデータを使用して新しいテーブルでテストを行うことができ、得られる改善は元のテーブルでも機能します。

2. パフォーマンスのために新しい Test テーブルを変更します(または、上記の手順 1 の例を使用して、より良い結果を取得します)。新しい Test テーブルをセットアップしてランダム データを入力したら、上記のアドバイスを確認し、テーブルを ALTER して高速化する必要があります。

  • TEXT を VARCHAR(255) に変更します。
  • 2 つまたは 3 つの列を持つ適切な主キーの一意のインデックスを選択して作成します。最初のテストで、バージョンの自動インクリメントと fk_entity を使用してテストします。
  • 必要に応じてテーブルを分割し、速度が向上するかどうかを確認します。データ型とmysql構成を変更して実際のパフォーマンスの向上を確認するために、最初のテストではパーティション分割しないことをお勧めします. 分割と改善のヒントについては、次のリンクを確認してください。
  • テーブルを最適化して修復します。インデックスが再度作成され、検索が大幅に高速化されます。

テーブルを最適化しますtestentity_versionable;
テーブルを修復しtestます。entity_versionable;
*最適化を実行するスクリプトを作成し、インデックスを最新の状態に維持し、毎晩起動します。


3.次のスレッドを注意深く読んで、MySQL とハードウェアの構成を改善してください。それらは読む価値があり、より良い結果が得られると確信しています。

  • 多少の費用をかけて、データベースのハード ディスク構成を簡単に改善します
    。可能であれば、メインの MySQL データベースに SSD を使用し、
    バックアップ用にスタンドアロンの機械式ハード ディスクを使用します。INSERTの速度を向上させるために、MySQL ログを別の 3 番目のハードディスクに保存するように設定します
    。(数週間後に機械式ハードディスクの最適化を忘れずに)。
  • パフォーマンス リンク:一般 & マルチコア構成IO の最適化Debiancores最適な構成、 構成48 GB RAM ..
  • SQL クエリのプロファイリング: クエリのプロファイリング方法、クエリのボトルネックの可能性を確認する
  • MySQL は非常にメモリを集中的に使用するため、可能であれば低レイテンシの CL7 DDR3 メモリを使用してください。トピックから少し外れますが、システム データが重要な場合は、ECC メモリを探すことができますが、高価です。

4. 最後に、テスト テーブルで INSERT と SEARCH をテストします。上記のテーブル スキーマを使用して 2 億以上のランダム データをテストしたところ、新しい行を挿入するのに 0.001 秒、1 億行を検索して選択するのに約 2 分かかりました。ただし、それは単なるテストであり、良い結果のようです:)


5. 私のシステム構成:

  • データベース: MySQL 5.6.10 InnoDB データベース (テスト)。
  • プロセッサー: AMD Phenom II 1090T X6 コア、各コア 3910Mhz。
  • RAM: 16GB DDR3 1600Mhz CL8。
  • HD: SSD に Windows 7 64 ビット SP1、SSD に mySQL をインストール、機械式ハード ディスクにログを書き込みます。MySQL は 1 つの SQL に対して 1 つのコアしか使用しない
    ため、おそらく最新の Intel i5 または i7 の 1 つを 4500Mhz+ に簡単にオーバークロックすると、より良い結果が得られるはずです。コア速度が高いほど、実行速度が速くなります。

6. MySQL の詳細:
O'Reilly High Performance MySQL
MySQL SQL ステートメントの最適化


7. 別のデータベースの使用: MongoDB またはRedisはこのケースに最適で、おそらく MySQL よりもはるかに高速です。どちらも習得が非常に簡単で、どちらにも利点があります。
- MongoDB: MongoDB ログ ファイルの増大

レディス

私は間違いなくRedisを選びます。ログを Redis に保存する方法を学べば、非常に高速にログを管理するための最良の方法になります

  • Redis は C でコンパイルされ、メモリに格納されます。情報をディスクに自動的に保存する (永続化する) いくつかの異なる方法があります。おそらく心配する必要はありません。(災害シナリオの場合、約 1 秒のログが失われます)。

  • Redis は、テラバイト単位のデータを管理する多くのサイトで使用されています。その非常識な量の情報を処理する方法はたくさんあります。つまり、Redis が安全であることを意味します (ここでは、stackoverflow、blizzard、twitter、youporn で使用されています..)

  • ログは非常に大きくなるため、ハードディスクにアクセスせずに高速化するには、メモリに収まる必要があります。異なる日付の異なるログを保存し、それらの一部のみをメモリに設定できます。メモリの上限に達した場合でも、エラーは発生せず、すべてが完全に機能しますが、詳細についてはRedis FAQを確認してください。

  • この目的では、Redis が MySQL よりもはるかに高速になると確信しています。listsデータの操作方法と更新方法、および setsデータのクエリ/検索方法について学習する必要があります。非常に高度なクエリ検索が必要な場合は、MongoDB を使用する必要がありますが、この場合、単純な日付検索は Redis に最適です。

Instagram ブログの素敵な Redis 記事。

于 2013-06-18T10:57:08.483 に答える
0

職場では、顧客の状況 (金融セクター) のために、ほぼすべてのテーブルにログテーブルがあります。

2 つのテーブル (「通常の」テーブルとログ テーブル) を実行し、キーワード (I、U、D) と古いレコード (更新時) を格納する通常のテーブルの挿入/更新/削除でトリガーします。 、削除) またはログテーブル内の新しいもの (挿入時)

同じデータベーススキーマに両方のテーブルがあります

于 2013-06-16T09:00:27.150 に答える