データベースのサイズが数百万行を超えるようになったら、アプリケーションのスケーリングを開始し、データベースを複数の物理サーバーに分割することが推奨されます。
データベースを複数に分割した場合の最大の問題は、ユーザーが特定のデータを要求した場合のその後の同期である。
例えば、articles
というテーブルがあったとして、巨大なサイトであるため、上位には数千万件の記事があり、物理的に複数のマシンに分けて表示する必要があるとします。
もし、普通の整数をid`(主キー)として、autoincrementの設定で使うと、分散して異なるマシンでレコードを作成し、それを同期させるときに、idの衝突が起こり、複雑な方法でレコード番号を変更しなければならないことがすぐに分かってしまうでしょう。さらに、多くのセッションを他のテーブルに解決する場合、これは非常に複雑なオーバーヘッドとなり、ミスを犯しやすくなります。
そこで、数字の識別子の代わりに、複数のマシンで独立に生成しても一意になることが保証される複雑なアルゴリズムで生成されたテキスト文字列であるUUID
を生成することができるようにした。
メリット
19010018
であることがわかれば、ユーザー19010017
なども存在することが容易に推測される。この攻撃はベクトル攻撃と呼ばれる。UUIDは単純なSQLクエリ SELECT UUID();
によっても取得できるが、これではデータベースへの問い合わせ回数が増え、アプリケーションロジックで最初にまとめてデータを用意し、それを一気に書き込むということができなくなる。
そこで、良い解決策として、Composerで取得したramsey/uuidパッケージを使いたいのです。UUID自体にはいくつかのバージョンがあり、パッケージは必要に応じてあらゆる種類のものを遊び心で生成することができます。
そのため、使い勝手が良い。
require 'ベンダ/autoload.php';use Ramsey\Uuid\Uuid;// バージョン 1(時間軸)の UUID オブジェクトを生成する。$uuid1 = Uuid::uuid1();echo $uuid1->toString() . "\n"; // e4eaaaf2-d142-11e1-b3e4-080027620cdd// バージョン 3 (名前ベースと MD5 のハッシュ化) の UUID オブジェクトを生成します。$uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'ピーピーネット');echo $uuid3->toString() . "\n"; // 11a38b9a-b3da-360f-9353-a5a725514269// バージョン4の(ランダムな)UUIDオブジェクトを生成する$uuid4 = Uuid::uuid4();echo $uuid4->toString() . "\n"; // 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a// バージョン 5(名前ベース、SHA1 ハッシュ)の UUID オブジェクトを生成する$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'ピーピーネット');echo $uuid5->toString() . "\n"; // c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Doctrine を使っている場合、ID をデータ型として直接生成する拡張機能 ramsey/uuid-doctrine があります。
最初の試みでは、主キー(ID)として varchar(36)
を使いましたが、それは全く良いアイデアではありません。
内部ロジックの説明:。
MySql データベース (そして他の多くのデータベース) では、
varchar
やchar
などの文字列を表すデータ型を主キーとして効率的に使用することができません。 いくつかのデータベースでは、UUID を直接保存するためのGUID
データ型が存在します。この型が使えない場合は、binary(16)
という形で適当な代用品があります。
データベースを物理的に調べる場合、IDはHEX形式で表現されます(バイナリ形式は表示できないため)。素敵なID 726c67c4-e5eb-4a4c-8fcc-031da5d6f3c6
の代わりに、INSERTクエリの '?kYߟKg2c;'
のように見える726C67CE5EB4A4C8FCC031DA5D6F3C6
が表示されているだけでしょう。
varchar(36)
から binary(16)
に変換する。新しく設定されたIDをデータベースで表現している(または表現する予定)のでしょうね。
`id` binary(16) NOT NULL
ただし、データ型を変えるだけではうまくいかないので、次のようなものを。
SET FOREIGN_KEY_CHECKS=0;ALTER TABLE article CHANGE id id BINARY(16) NOT NULLSET FOREIGN_KEY_CHECKS=1;
理由は基本的に2つあります。
同じデータ型でなければなりません
。そのため、論文IDのデータ型と、例えば、論文と著者をマッチングするリレーショナルテーブルの両方を変更する必要があります。したがって、唯一の正しい解決策は、データをバックアップし(ただし、いずれにせよ各移行の前にこれを行う必要があります)、機能リレーションのある空のデータベースを準備し、移行によってそこにデータを再び置くことです。
以前、UUIDを変に生成してしまった場合は、何か順次取得する方法を選び、すべてのレコードの番号を変更した方がよいでしょう。理由は、シーケンシャルレイアウトによって、値の順序付けがうまくいき、 btree
を作成できるため、bigint
とほぼ同じ性能になるからです。
既存のデータベースを、複雑なマイグレーションを考案することなく、外部キーを保持したまま、UUIDをvarcharで保存したものからバイナリ形式に変換する良い方法をご存知でしたら、フィードバックをいただけると幸いです。
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:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | ja