見つかった関数ごとに、PHPを使用してUUIDを生成しています here
次に、それをMySQLデータベースに保存します。 UUID v4を保存するための最良/最も効率的なMySQLフィールド形式は何ですか?
現在varchar(256)を持っていますが、必要以上に大きいと確信しています。ほとんどの答えをたくさん見つけましたが、それらが参照しているUUIDの形式については一般に曖昧なので、特定の形式を求めています。
正確にフィットさせたい場合はVARCHAR(36)
として保存します。とにかく同じストレージコストで機能するVARCHAR(255)
として保存します。ここでバイトをめちゃくちゃにする理由はありません。
VARCHAR
フィールドは可変長であるため、ストレージコストは実際にそこにあるデータの量ではなく、実際にそこにあるデータの量に比例します。
BINARY
として保存するのは非常に面倒で、値は印刷できず、クエリの実行時にゴミとして表示される可能性があります。リテラルバイナリ表現を使用する理由はめったにありません。人間が読み取れる値はコピーして貼り付けることができ、簡単に操作できます。
Postgresのような他のプラットフォームには、よりコンパクトな形式で内部的に格納する適切なUUID列がありますが、人間が読める形式で表示するので、両方のアプローチを最大限に活用できます。
各行に常にUUIDがある場合は、CHAR(36)
として保存し、VARCHAR(36)
を介して行ごとに1バイトを保存できます。
uuid CHAR(36) CHARACTER SET ascii
CHARとは対照的に、VARCHAR値は1バイトまたは2バイトの長さのプレフィックスとデータとして保存されます。長さのプレフィックスは、値のバイト数を示します。値が255バイトを超える必要がない場合、列は1バイトを使用し、値が255バイトを超える場合は2バイトを使用します。 https://dev.mysql.com/doc/refman/5.7/en/char.html
CHAR
には注意が必要ですが、フィールドが空のままであっても、定義された完全な長さを常に消費します。また、文字セットにはASCIIを使用してください。そうしないと、CHAR
は最悪のシナリオを計画します(つまり、utf8
、4のutf8mb4
)
[...] MySQLは、CHAR CHARACTER SET utf8mb4カラムの各文字に対して4バイトを予約する必要があります。これは、可能な最大の長さだからです。たとえば、MySQLはCHAR(10)CHARACTER SET utf8mb4カラム用に40バイトを予約する必要があります。 https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
質問は、UUIDをMySQLに保存することです。
MySQLバージョン8.0以降では、binary(16)
を_UUID_TO_BIN/BIN_TO_UUID
_関数を介した自動変換で使用できます。 https://mysqlserverteam.com/mysql-8-0-uuid-support/ =
MySQLには、UUIDを主キーとして生成する高速な方法もあることに注意してください。
VALUES(UUID_TO_BIN(UUID()、true))へのINT INT
最も効率的なのは間違いなくBINARY(16)
であり、人間が読める文字を保存する場合は2倍以上の記憶領域を使用し、インデックスが大きくなりルックアップが遅くなります。データが十分に小さく、テキストとして保存してもパフォーマンスが低下しない場合は、退屈な整数キーに対してUUIDは必要ないでしょう。まともなdb管理ツールは、「テキスト」のリテラルバイトではなく、16進数としてオクテットを表示/ダンプするため、rawの保存は他の人が示唆するほど苦痛ではありません。データベースでUUIDを手動で検索する必要はありません。必要な場合は、HEX()
およびx'deadbeef01'
リテラルはあなたの友達です。これに対処するために、あなたが参照したようなアプリで関数を書くのは簡単です。おそらく、データベースで仮想列およびストアドプロシージャとして実行することもできます。これにより、アプリが生データに煩わされることがなくなります。
UUID生成ロジックを表示ロジックから分離して、既存のデータが変更されず、エラーが検出されるようにします。
function guidv4($prettify = false)
{
static $native = function_exists('random_bytes');
$data = $native ? random_bytes(16) : openssl_random_pseudo_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
if ($prettify) {
return guidv4_pretty($data);
}
return $data;
}
function guidv4_pretty($data)
{
return strlen($data) == 16 ?
vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) :
false;
}
function guidv4_ugly($data)
{
$data = preg_replace('/[^\\dA-F]+/i', '', $data);
return strlen($data) == 32 ? hex2bin($data) : false;
}
編集:データベースの読み取り時に列のみが必要な場合は、次のようなステートメントで十分です。
ALTER TABLE test ADD uuid_pretty CHAR(36) GENERATED ALWAYS AS (CONCAT_WS('-', LEFT(HEX(uuid_ugly), 8), SUBSTR(HEX(uuid_ugly), 9, 4), SUBSTR(HEX(uuid_ugly), 13, 4), SUBSTR(HEX(uuid_ugly), 17, 4), RIGHT(HEX(uuid_ugly), 12))) VIRTUAL;
最もスペース効率がよいのは、BINARY(16)
または2つのBIGINT UNSIGNED
。
前者は、手動クエリでは(簡単な方法で)読み取り可能/コピー可能な値が得られないため、頭痛の種になります。後者は、1つの値と2つの列の間をマッピングする必要があるため、頭痛の種になるかもしれません。
これがプライマリキーである場合、すべてのセカンダリインデックスの一部にもなるため、スペースを無駄にすることはありません。つまり、これらのタイプのいずれかを選択します。
パフォーマンスのために、ランダムUUID(つまり、ランダム化されたUUID v4)のランダム性は、深刻な損害を与えます。これは、UUIDが主キーである場合、またはUUIDに対して多くの範囲クエリを実行する場合に適用されます。プライマリインデックスへの挿入は、最後(またはその近く)のすべてではなく、場所全体に行われます。データは一時的な局所性を失います。これはさまざまな場合に役立つプロパティでした。
私の主な改善点は、データの一部としてタイムスタンプを使用するUUID v1に似たものを使用し、タイムスタンプが最上位ビットになるようにすることです。たとえば、UUIDは次のように構成されます。
Timestamp | Machine Identifier | Counter
このようにして、自動インクリメント値に似た局所性を取得します。