PHP Manual

UUIDから整数値へ戻す

02. 05. 2021

ソフトウェア開発において、プログラマーは、今後何十年にもわたって自分の仕事の将来に大きな影響を与えるアーキテクチャーの決定に直面したとき、かなりの頻度で行き詰まることになる。同時に、後戻りのできない決断であり、すべての過ちに対して親愛なる代償を払うことになります。データベースは、小さなミスをするたびに首が飛ぶようなアーキテクチャの決定の典型的な例です。

最近の大きな決断の1つは、データベースのテーブルに主キーをどのように格納するかということです。これは些細な問題のように思えますが、その背景には意外と多くのことが隠されているのです。

主キーのオプション

基本的には4つの基本的な選択肢があります。

  • 整数
  • 整数無記号
  • ビッグイン
  • UUID

Integer は単純に整数です(unsignedの場合は符号なし、つまり常に正数、big intの場合は非常に大きな値をとることができます)。とてもシンプルなコンセプトです。UUIDは、いくつかの部分からなるテキスト文字列(例えば c4a760a8-dbcf-5254-a0d9-6a4474bd1b62 という形式)で、それぞれが特定の特性を持つことができ、巨大なマルチサーバーや分散型アプリケーションの構築に有用です。UUIDに関する有用な技術の大きなエコシステムは、あなたが知らない、あるいは将来的に持つことになるであろう問題を解決するために存在するのです。

正しいハンマーを使用する

少し前(2020年冬)、友人のポールが、与えられた大きさの問題に対して適切な解決策を適用するという概念を説明していました。これは、多くの開発者が忘れたがっている、偉大で重要なアイデアです。必要ないのに、非常に複雑なソリューションを作り出してしまうのです。英語では、このオーバーエンジニアリングを表す素敵なフレーズがあります。

UUIDのサイズと一意性

UUIDの基本的な利点は、アプリケーションが大きくなりすぎて、多くのウェブサーバーにデータベースを分割した場合、1つのデータベーステーブルが非常に巨大で1台のマシンのディスクに収まらない場合、多くの物理マシンに分割して、それぞれがテーブルの自分の部分を知っていて、残りの部分を同僚に問い合わせることができることです。また、UUIDは、新しい行を挿入する際に、非常に大規模なアプリケーションの場合、多くの場所で並行して行を書き込む必要があり、メインサーバーの書き込み能力が解放されるのを待つ必要がないという根本的な問題も解決してくれます。

同時に多くの場所に書き込むというコンセプトは、例えばチャットアプリケーションで使われています。Messengerでメッセージを送ると、一番近いFacebookのデータベースサーバーに送られ、サーバーがメッセージにUUIDとタイムスタンプを割り当て、ローカルのデータベースに書き込む。地球の反対側にいるあなたの友人は、今度は彼の地元のデータセンターにメッセージを書き込みます。その間、クラウド・インフラ全体が、世界中の同期を保証します。カッコイイでしょ?:)

このような並列書き込みが機能するためには、レコードの衝突の問題を解決する必要がある。個々のローカルデータベースが単純な整数を使用する場合、すぐに2つの独立したサーバーが同じ識別子の下に2つの異なるレコードを書き込むことになる。これらのレコードが同期されると、衝突が発生する。通常、解決策はありません。IDの番号を変更しても、他のセッションがそれにつながるかもしれないからです。

UUIDはこれを解決するために、例えば各サーバーに合意したプレフィックスを与え、それを各UUIDの先頭に挿入し、次にタイムスタンプを挿入し、そして識別子そのものを挿入します。

興味深い事実: このような膨大なデータを書き込む場合、どのレコードがいつ書き込まれたかということよりも、どのような順序で書き込まれたかに関心があります(たとえば、ユーザーに対するメッセージの順序を入れ替えないようにするためです)。

どのプレフィックスを使用するかについて、サーバー同士が合意できない場合の対処法について質問されることがあります。この問題は、例えば、分散型アプリケーションやオフラインアプリケーションで発生する。この場合、UUIDはランダムに生成することも可能です。

そこで問題は、UUIDをランダムに生成するときに、競合が発生する可能性はどの程度あるかということです。まあ、あなたにはたぶん起きないでしょうけど。UUIDはおよそ2^122個のユニークなものがあります(128ビット数であるため)。実際には、競合が発生する確率は約0.00000000006(6×10-11)である。実際には、今後100年間、毎秒10億個のUUID**を生成すれば、衝突の確率は50%`になる、ということです。そのため、競合が発生しない可能性が高く、UUIDはデータベースの問題を解決する決定打となります。

このような堅牢なソリューションが必要なのでしょうか?

わからないなら、答えはNOです。

主キーが intunsigned フラグの場合、取り得る値は 4,294,967,295 (40億) になります。整数のサイズの比較については、MySql ドキュメントを参照してください。

1つのテーブルに40億件のレコードを格納する頃には、おそらくすぐにディスク容量が足りなくなるでしょう。

整数と結合のパフォーマンス

整数は本当に速いですね。MySql には、これらのためのネイティブな最適化があります。インデックスはきちんと機能し(しかもずっと小さい)、4バイトしか取らず、結合も非常に速く、ほとんどの場合において問題ないでしょう。

データベースのレプリケーションの問題であれば、データベース全体をMS Azureなどのクラウドに置き、外部から問い合わせるのがベストな解決策かもしれません。数千万件のレコードを格納する場合でも、特定の行への整数によるアクセスはミリ秒単位(よく構成されたサーバーでは3ms以下)であり、クラスタ化インデックスを用いれば、大量のリクエストを受けた場合でも十分に時間を維持することが可能である。

UUID を本当に使う必要があるなら、MySql の世界から離れ、Postgres データベースを使った方が良いでしょう。Postgres は MySql とは異なり、UUID という独自のデータ型を持っているのですから。UUIDとMySqlでは結合が大きな問題で、たった3つのテーブル(それぞれが数万件以下のレコードしかない)を結合する場合、クエリ全体の処理に数百ミリ秒から数秒かかることがあるのです。そして、これは残念ながらMySqlの問題で、おそらく解決できないでしょう。

Jan Barášek   Více o autorovi

Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.

Rád vám pomůžu:

Související články

1.
4.
Status:
All systems normal.
2024