web-dev-qa-db-ja.com

MySQLのUUIDパフォーマンス?

MySQLデータベースの主キーとしてUUID値を使用することを検討しています。挿入されるデータは、数十、数百、または数千ものリモートコンピューターから生成され、1秒あたり100〜40,000の挿入速度で挿入されます。更新は一切行われません。

通常、データベース自体は、データの収集を開始する前に約5,000万レコードに達するため、大規模なデータベースではなく、小さなデータベースでもありません。また、InnoDBで実行することも計画していますが、実行中のエンジンが改善されれば、それを変更することもできます。

JavaのType 4 UUIDを使用する準備ができましたが、テスト中に奇妙な動作が見られました。 1つは、varchar(36)として保存しているため、binary(16)を使用する方が良いと思います。

より大きな問題は、5,000万件のレコードがあるときに、このランダムデータがインデックスをどれほどひどく破壊するのかということです。たとえば、左端のビットにタイムスタンプが付けられたタイプ1 UUIDを使用した方が良いでしょうか?または、UUIDを完全に捨てて、auto_increment主キーを検討する必要がありますか?

さまざまな種類のUUIDがMySQLのインデックス/プライマリキーとして保存されている場合のパフォーマンスに関する一般的な考え/ヒントを探しています。ありがとう!

74

UUIDは、Universally Unique IDです。ここで検討すべき普遍的な部分です。

本当に IDは普遍的に一意である必要がありますか?その場合、UUIDのみが選択可能です。

do UUIDを使用する場合は、文字列ではなく数字として保存することを強くお勧めします。 5,000万以上のレコードがある場合、ストレージスペースを節約することでパフォーマンスが向上します(ただし、その程度はわかりません)。

IDが普遍的に一意である必要がない場合、テーブル内でIDが一意になることを保証するauto_incrementを使用するよりもはるかに良いことはできないと思います(値は毎回増加するため)

31
Dancrumb

私の仕事では、UUIDをPKとして使用します。経験から言えることは、それらをPKとして使用しないでください(ところでSQL Server)。

記録が1000未満の場合は問題ありませんが、数百万の場合は最悪の事態になります。どうして? UUIDはシーケンシャルではないため、新しいレコードが挿入されるたびに、MSSQLはレコードを挿入する正しいページを確認してからレコードを挿入する必要があります。これによる本当にい結果は、ページがすべて異なるサイズになり、最終的に断片化されることです。そのため、定期的にデフラグを行う必要があります。

自動インクリメントを使用すると、MSSQLは常に最後のページに移動し、(理論的には)同じサイズのページになります。そのため、これらのレコードを選択するパフォーマンスははるかに優れています(INSERTがテーブル/ページをブロックしないため)さよなら)。

ただし、UUIDをPKとして使用する大きな利点は、DBのクラスターがある場合、マージ時に競合が発生しないことです。

次のモデルをお勧めします。1. PK INT Identity 2. UUIDとして自動的に生成される追加の列。

これにより、マージプロセスが可能になります(UUIDがREALキーになり、PKが一時的なものになり、パフォーマンスが向上します)。

注:最良の解決策は(コメントで言ったように)NEWSEQUENTIALIDを使用することですが、リファクタリングする時間があまりない(さらに悪いことに、すべての挿入を制御しない)従来のアプリの場合は、実行できません。しかし実際、2017年現在、ここでの最善の解決策はNEWSEQUENTIALIDまたはNHibernateでGuid.Combを実行することです。

お役に立てれば

72
Kat Lim Ruiz

考慮すべきことは、自動インクリメントが1つずつ生成され、並列ソリューションを使用して解決できないことです。 UUIDを使用するための戦いは、最終的には、あなたが達成したいものとあなたが犠牲にする可能性のあるものに帰着します。

パフォーマンスについては、 簡潔に

上記のようなUUIDは、ダッシュを含めて36文字です。このVARCHAR(36)を格納すると、比較パフォーマンスが劇的に低下します。これが主キーであり、遅くしたくないです。

そのビットレベルでは、UUIDは128ビットです。これは、16バイトに収まることを意味します。これは人間が読めるものではないことに注意してください。 64ビット整数の倍。理論的にはVARBINARY(16)を使用しますが、これは多くのオーバーヘッドなしで機能します。

次の2つの投稿を読むことをお勧めします。

両者の間では、彼らはあなたの質問に答えます。

25
Kyle Rozendo

UUIDを保存するのが面倒で、主キーとして使用するのが面倒なので、UUIDを避ける傾向がありますが、利点があります。主なものは、それらが一意であることです。

私は通常、問題を解決し、デュアルキーフィールドを使用してUUIDを回避します。

コレクター=マシンに割り当てられた一意の

ID = COLLECTORによって収集されたレコード(auto_incフィールド)

これは私に二つのことを提供します。自動収集フィールドの速度と、収集されてグループ化された後、中央の場所に保存されるデータの一意性。また、データが収集された場所を閲覧している間も知っています。これは私のニーズにとって非常に重要です。

UUIDを使用することに決めたクライアント用の他のデータセットを扱う際に多くのケースを見てきましたが、それでもデータを収集するためのフィールドがあり、これは本当に無駄です。キーとして2つの(または必要に応じてそれ以上の)フィールドを使用するだけで本当に役立ちます。

UUIDを使用したパフォーマンスヒットが多すぎます。彼らはチートのように感じます...

5

挿入ごとに一意のキーを集中的に生成する代わりに、キーのブロックを個々のサーバーに割り当てるのはどうですか?キーが不足すると、新しいブロックを要求できます。次に、挿入ごとに接続することでオーバーヘッドの問題を解決します。

キーサーバーは次に使用可能なIDを保持します

  • サーバー1はIDブロックを要求します。
  • キーサーバーが返す(1,1000)
    サーバー1は、新しいブロックを要求するまで1000レコードを挿入できます
  • サーバー2はインデックスブロックを要求します。
  • キーサーバーが返す(1001,2000)
  • 等...

サーバーが必要なキーの数を要求したり、使用されていないブロックのマップを維持する必要があるキーサーバーに未使用のブロックを返すことができる、より洗練されたバージョンを考え出すことができます。

3
Bouke Versteegh

各サーバーにトランザクションIDで数値IDを割り当てます。次に、挿入された各レコードは、独自のカウンターを自動インクリメントします。 ServerIDとRecordIDの組み合わせは一意です。 ServerIDフィールドにはインデックスを付けることができ、ServerIDに基づく将来の選択パフォーマンス(必要な場合)ははるかに優れている場合があります。

2
Nikolai

手作りのUIDはどうですか?数千のサーバーのそれぞれにIDを与え、主キーをautoincrement、MachineIDのコンボキーにします。

1
MindStalker

簡単な答えは、多くのデータベースでは、インデックス方法と高次ビットでのUUIDの意図的なエントロピーの競合により、特にINSERTボリュームが大きい場合にパフォーマンスの問題が発生することです。一般的なハッキングがいくつかあります。

  • 気にしない別の種類のインデックス(MSSQLの非クラスター化など)を選択します
  • データを変更してエントロピーを下位ビットに移動します(たとえば、MySQLでV1 UUIDのバイトを並べ替える)
  • uUIDを自動インクリメントintプライマリキーを持つセカンダリキーにします

...しかし、これらはすべてハックであり、おそらく脆弱なものです。

最良の答えは、残念ながら最も遅いものですが、他のタイプと同様に主キーとしてUUIDを処理できるように、ベンダーに製品の改善を要求することです。彼らは、一般的なユースケースになり、成長し続けるものを解決するために彼らの失敗を補うために、あなた自身の半分焼きハックをロールバックすることを強制するべきではありません。

1
StephenS

主キーは分散型で生成されるため、いずれにしてもauto_incrementを使用するオプションはありません。

リモートマシンのIDを非表示にする必要がない場合は、UUIDの代わりにタイプ1 UUIDを使用します。生成は簡単で、少なくともデータベースのパフォーマンスを損なうことはありません。

同じことは、varchar(char、本当に)対バイナリについても言えます。それは本当に重要ですか、どのくらいパフォーマンスが改善されますか?

1
user3850