varchar(36) を使用するか、それを行うためのより良い方法はありますか?
10 に答える
オブジェクトの GUID を格納する最良の方法について尋ねたところ、DBA から、同じことを整数で 4 バイトで実行できるのに、なぜ 16 バイトを格納する必要があるのかと尋ねられました。彼がその挑戦を私に提示したので、私は今がそれについて言及する良い機会だと思いました. そうは言っても…
ストレージ スペースを最大限に活用したい場合は、guid を CHAR(16) バイナリとして格納できます。
char(36) として保存します。
ThaBadDawg による回答に加えて、これらの便利な関数 (私の賢明な同僚のおかげで) を使用して、36 の長さの文字列から 16 のバイト配列に戻します。
DELIMITER $$
CREATE FUNCTION `GuidToBinary`(
$Data VARCHAR(36)
) RETURNS binary(16)
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result BINARY(16) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Data = REPLACE($Data,'-','');
SET $Result =
CONCAT( UNHEX(SUBSTRING($Data,7,2)), UNHEX(SUBSTRING($Data,5,2)),
UNHEX(SUBSTRING($Data,3,2)), UNHEX(SUBSTRING($Data,1,2)),
UNHEX(SUBSTRING($Data,11,2)),UNHEX(SUBSTRING($Data,9,2)),
UNHEX(SUBSTRING($Data,15,2)),UNHEX(SUBSTRING($Data,13,2)),
UNHEX(SUBSTRING($Data,17,16)));
END IF;
RETURN $Result;
END
$$
CREATE FUNCTION `ToGuid`(
$Data BINARY(16)
) RETURNS char(36) CHARSET utf8
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result CHAR(36) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Result =
CONCAT(
HEX(SUBSTRING($Data,4,1)), HEX(SUBSTRING($Data,3,1)),
HEX(SUBSTRING($Data,2,1)), HEX(SUBSTRING($Data,1,1)), '-',
HEX(SUBSTRING($Data,6,1)), HEX(SUBSTRING($Data,5,1)), '-',
HEX(SUBSTRING($Data,8,1)), HEX(SUBSTRING($Data,7,1)), '-',
HEX(SUBSTRING($Data,9,2)), '-', HEX(SUBSTRING($Data,11,6)));
END IF;
RETURN $Result;
END
$$
CHAR(16)
ですBINARY(16)
。好みのフレーバーを選択してください
コードをよりよく理解するために、以下の数字順の GUID の例を見てください。(不正な文字は、説明のために使用されています。それぞれに一意の文字が配置されています。) 関数はバイト順を変換して、優れたインデックス クラスタリングのビット順を実現します。並べ替えられた GUID は、例の下に示されています。
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
削除されたダッシュ:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
char(36) は良い選択です。また、MySQL の UUID() 関数を使用して、データベースからそのような ID を取得するために使用できる 36 文字のテキスト形式 (ハイフン付きの 16 進数) を返すことができます。
Binary(16) は問題なく、varchar(32) を使用するよりも優れています。
KCD によって投稿された GuidToBinary ルーチンは、GUID 文字列のタイムスタンプのビット レイアウトを考慮して微調整する必要があります。uuid() mysql ルーチンによって返されるもののように、文字列がバージョン 1 UUID を表す場合、時刻コンポーネントは D を除く文字 1 から G に埋め込まれます。
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678 = least significant 4 bytes of the timestamp in big endian order
9ABC = middle 2 timestamp bytes in big endian
D = 1 to signify a version 1 UUID
EFG = most significant 12 bits of the timestamp in big endian
バイナリに変換する場合、索引付けに最適な順序は、EFG9ABC12345678D + 残りです。
12345678 を 78563412 にスワップしたくないのは、ビッグ エンディアンが既に最適なバイナリ インデックス バイト順を生成しているためです。ただし、最上位バイトを下位バイトの前に移動する必要があります。したがって、EFG が最初に実行され、次に中間ビットと下位ビットが実行されます。uuid() を使用して 1 分間で 12 個ほどの UUID を生成すると、この順序で正しいランクが得られることがわかります。
select uuid(), 0
union
select uuid(), sleep(.001)
union
select uuid(), sleep(.010)
union
select uuid(), sleep(.100)
union
select uuid(), sleep(1)
union
select uuid(), sleep(10)
union
select uuid(), 0;
/* output */
6eec5eb6-9755-11e4-b981-feb7b39d48d6
6eec5f10-9755-11e4-b981-feb7b39d48d6
6eec8ddc-9755-11e4-b981-feb7b39d48d6
6eee30d0-9755-11e4-b981-feb7b39d48d6
6efda038-9755-11e4-b981-feb7b39d48d6
6f9641bf-9755-11e4-b981-feb7b39d48d6
758c3e3e-9755-11e4-b981-feb7b39d48d6
最初の 2 つの UUID は、最も近い時間に生成されました。最初のブロックの最後の 3 つのニブルのみが異なります。これらはタイムスタンプの最下位ビットです。つまり、これをインデックス可能なバイト配列に変換するときに、それらを右にプッシュする必要があります。反例として、最後の ID が最新ですが、KCD のスワッピング アルゴリズムはそれを 3 番目の ID の前に配置します (dc の前に 3e、最初のブロックの最後のバイト)。
インデックス付けの正しい順序は次のとおりです。
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
サポート情報については、この記事を参照してください: http://mysql.rjweb.org/doc.php/uuid
*** タイムスタンプの上位 12 ビットからバージョン ニブルを分割しないことに注意してください。これは、例の D ニブルです。前に投げるだけです。したがって、私のバイナリ シーケンスは DEFG9ABC などになります。これは、インデックス化されたすべての UUID が同じニブルで始まることを意味します。記事は同じことをしています。
これに出くわしたばかりの人には、Percona の調査によると、はるかに優れた代替手段があります。
これは、最適なインデックス作成のために UUID チャンクを再編成し、ストレージを削減するためにバイナリに変換することで構成されます。
記事全文はこちら
@ big_29 によって言及された関数は私の GUID を新しいものに変換するため、以下の関数を使用することをお勧めします (理由がわかりません)。また、テーブルで行ったテストでは、これらは少し高速です。https://gist.github.com/damienb/159151
DELIMITER |
CREATE FUNCTION uuid_from_bin(b BINARY(16))
RETURNS CHAR(36) DETERMINISTIC
BEGIN
DECLARE hex CHAR(32);
SET hex = HEX(b);
RETURN LOWER(CONCAT(LEFT(hex, 8), '-', MID(hex, 9,4), '-', MID(hex, 13,4), '-', MID(hex, 17,4), '-', RIGHT(hex, 12)));
END
|
CREATE FUNCTION uuid_to_bin(s CHAR(36))
RETURNS BINARY(16) DETERMINISTIC
RETURN UNHEX(CONCAT(LEFT(s, 8), MID(s, 10, 4), MID(s, 15, 4), MID(s, 20, 4), RIGHT(s, 12)))
|
DELIMITER ;
標準の GUID としてフォーマットされた char/varchar 値がある場合、単純な CAST(MyString AS BINARY16) を使用して単純に BINARY(16) として保存できます。CONCAT + SUBSTR という気が遠くなるようなシーケンスは必要ありません。
BINARY(16) フィールドは、文字列よりもはるかに高速に比較/ソート/インデックス付けされ、データベース内のスペースも 2 分の 1 しか必要としません。