MySQLでのUUIDパフォーマンス?


86

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

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

Javaのタイプ4UUIDを使用する準備ができましたが、テストでは奇妙な動作が見られました。1つは、varchar(36)として格納しているため、binary(16)を使用した方がよいことに気付きましたが、どれだけ良いかはわかりません。

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

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


2
重要な詳細が1つ欠けています。主キーは、ログサーバーまたはクライアントマシン自体によって生成されますか?

1
@hopそれらはデータを挿入する10-1000クライアントによって生成されています
Patrick Lightbody

シナリオのどこに普遍的な独自性が必要ですか?私のアドバイスは、auto_incrementに固執し、データを送信するリモートコンピューターを説明するために別のフィールドを使用することです。ここで車輪の再発明をする必要はありません。
セオドアゾグラフォス2011

回答:


36

UUIDは、Universally UniqueIDです。ここで検討する必要があるのは、普遍的な部分です。

IDが普遍的に一意である必要が本当にありますか?もしそうなら、UUIDがあなたの唯一の選択かもしれません。

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

IDが普遍的に一意である必要がない場合は、auto_incrementを使用するよりもはるかに優れているとは思いません。auto_incrementを使用すると、IDがテーブル内で一意になることが保証されます(値は毎回インクリメントされるため)


2
興味深い点; これにより、キーの生成が並列化されます。これにより、鍵生成のパフォーマンスが向上すると思います。ただし、UUIDの格納にVARCHARを使用する場合は、SELECTパフォーマンスではなくINSERTパフォーマンスを選択します。SELECTのパフォーマンスを確保するために、保存にはVARBINARYを選択する必要があります。追加の手順INSERTのパフォーマンスに影響を与える可能性がありますが、SELECTのパフォーマンスの向上によって報われるでしょう。
ダンクラム2010年

12
最終的に、実際のデータでベンチマークを実行しました。キーなしのGUIDはかなり高速で、キー付きのGUIDは(BINARYとして保存されている場合でも)ひどいもので、AUTO_COMPLETE付きのintが最速でした。私たちの場合、シーケンスの生成は、より多くのデータを格納するコストに比べて取るに足らないように見え、GUIDのランダム性のために本当にくだらないBTREEを持っているため、実際にツリーからフォレストが欠落していると思います
Patrick Lightbody

1
数値として保存するということは、バイナリ形式で保存することを意味しますか?しかし、バイナリ形式は人間には読めません。大きなバイトのuuid主キーがあるので遅いですか?もしそうなら、uuidの別の列で自動インクリメントを保存できます。そうすれば、パフォーマンスが低下することはありません。私は正しいですか?
チャムナップ2012年

4
厳密に言えば、UUIDは普遍的に一意であり、世界の他の場所に表示されることはありません。これが必要になるのは、データを公に共有している場合のみです。UUIDを数値として格納することに関しては、binary形式を意味するものではありません。つまり、288ビットの文字列ではなく、128ビットの数値です。たとえば、ASCIIの「hello」という単語68 65 6C 6C 6Fは、448,378,203,247という数字です。文字列「68656C6C6F」の保存には10バイトが必要です。448,378,203,247という数字は5つしか必要としません。全体として、UUIDの最初のUが本当に必要でない限り、これ以上のことはできませんauto_increment
Dancrumb 2012年

1
@Chamnap:Stack Overflowの質問をすることをお勧めします:o)
Dancrumb 2012年

78

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

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

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

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

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

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

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

お役に立てれば


これらの用語の意味はよくわかりませんが、実際には、インデックスを毎月再インデックスする必要があります。あなたが言ったことがインデックスの再作成タスクを排除するかどうかはわかりませんが、私は尋ねることができます。
Kat Lim Ruiz

3
私が考えていたのは、これは親子関係ではうまく機能しないかもしれないということです。この場合、子テーブルにparent-pk、parent-guidを追加する必要があると思います。そうしないと、データベース間の参照が失われる可能性があります。私はこれについてあまり考えたり、例を挙げたりしていませんが、これが必要になる可能性があります
Kat Lim Ruiz

4
SQLサーバーの@KatLimRuizでは、NEWSEQUENTIALID()technet.microsoft.com/en-us/library/ms189786.aspxを使用して、パフォーマンスの問題を回避できます
giammin 2013

確かに、しかしNEWSEQUENTIALIDはデフォルトとしてのみ機能します。したがって、これを中心にDAL全体を設計する必要があります。これは、新しいプロジェクトでは問題ありませんが、大きなレガシーではそれほど簡単ではありません
Kat Lim Ruiz

@KatLimRuizの天才。それは大きな妥協です
jmgunn87 2014年

26

考慮すべき点は、自動インクリメントは一度に1つずつ生成され、並列ソリューションを使用して解決できないことです。UUIDを使用するための戦いは、最終的には、達成したいことと、潜在的に犠牲にすることとの関係になります。

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

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

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

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

私は2つの間で考えます、彼らはあなたの質問に答えます。


2
実際、私はこの質問を投稿する前に両方の記事を読みましたが、ここではまだ良い答えがありませんでした。たとえば、タイプ1とタイプ4のUUIDについてはどちらも話しません:(
Patrick Lightbody

それは公平です、私は私の答えを少し更新しました。しかし、それがあまり多くの追加の洞察を提供するとは思わない。
カイルロセンド2010年

@Patrick:質問にさまざまなトピックを入れすぎています。

1
9年後ですが、後世のために、整数IDとは異なり、アプリはUUIDを安全に生成でき、データベースから生成を完全に削除できることにも注意してください。パフォーマンスを最適化するためのUUIDの操作(タイムスタンプベースですが、単純にソートできるように変更されています)は、SQL以外のほぼすべての言語で特に簡単です。幸いなことに、今日のほとんどすべてのデータベース(MySQLを含む)は、以前よりもはるかに優れたUUID主キーを処理します。
マイルエラム

5

保存するのが面倒で、主キーとして使用するのが面倒であるという理由だけでUUIDを避ける傾向がありますが、利点があります。主なものは彼らがユニークであるということです。

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

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

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

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

UUIDを使用することを決定したクライアントの他のデータセットを処理しているときに、データが収集されたフィールドがまだある場合が多く、これは本当に労力の無駄です。キーとして2つ(または必要に応じてそれ以上)のフィールドを使用するだけで本当に役立ちます。

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


3

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

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

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

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


理論的には興味深い提案です。これは実際には管理が複雑になります。より実用的な解決策は、おそらくschworakによって提起された答えでしょう。
サイモンイースト

2

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


2

簡単に言うと、多くのデータベースでは、インデックス作成方法とUUIDの上位ビットの意図的なエントロピーとの競合が原因で、パフォーマンスの問題が発生します(特にINSERTボリュームが大きい場合)。一般的なハックがいくつかあります。

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

...しかし、これらはすべてハックであり、おそらくそれは壊れやすいものです。

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


1

手作りのUIDはどうですか?何千ものサーバーのそれぞれにIDを与え、主キーを自動インクリメントのコンボキー、MachineIDにしますか?


私はそれについて考えました、そしていくつかのベンチマークを実行する必要があるかもしれません。1000台のマシンのそれぞれでの一時的なローカルシーケンスでさえ、タイムスタンプと組み合わせて十分かもしれません。例:machine_id + temp_seq +タイムスタンプ
Patrick Lightbody

すべてのタイムスタンプティックをリセットするtemp_sequenceを持つことは可能ですか?よく分かりません。
MindStalker 2010年

1

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

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

同じことがvarchar(char、本当に)とbinaryにも当てはまります。それは問題を助けるだけです。パフォーマンスがどれだけ向上するかは本当に重要ですか?


0

この質問はかなり古いと思いますが、私は自分の研究でそれを思いつきました。多くのことが起こったので(SSDはユビキタスなInnoDBがアップデートを取得したなど)。

私の調査では、パフォーマンスに関するこのかなり興味深い投稿を見つけました。

GUID / UUIDインデックスツリーのランダム性のために、かなり不均衡になる可能があると主張します。MariaDB KBで、別の投稿が解決策を提案しているのを見つけました。しかし、新しいUUID_TO_BINがこれを処理するので。この関数は、MySQL(テスト済みバージョン8.0.18)でのみ使用でき、MariaDB(バージョン10.4.10)では使用できません。

TL; DR:変換/最適化されたBINARY(16)値としてUUIDを格納します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.