web-dev-qa-db-ja.com

GUIDをMySQLテーブルに保存するにはどうすればよいですか?

Varchar(36)を使用しますか、それを行うより良い方法はありますか?

136
CDR

オブジェクトのGUIDを保存する最良の方法について尋ねたとき、DBAから、整数で4バイトで同じことができるのに16バイトを保存する必要がある理由が尋ねられました。彼がその挑戦を私にそこに置いたので、私は今それを言及する良い時期だと思いました。言われていること...

ストレージスペースを最適に使用したい場合は、GUIDをCHAR(16)バイナリとして保存できます。

97
thaBadDawg

Char(36)として保存します。

41
Brian Fisher

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
31
KCD

char(36)が適切な選択です。また、MySQLのUUID()関数を使用して、dbからそのようなIDを取得するために使用できる36文字のテキスト形式(ハイフン付きの16進数)を返すことができます。

24
Learning

「より良い」は最適化の対象に依存します。

ストレージのサイズ/パフォーマンスと開発の容易さをどの程度重視していますか?さらに重要なこと-十分なGUIDを生成しますか、それとも頻繁に取得しますか?

答えが「いいえ」の場合、char(36)で十分であり、GUIDの保存/フェッチが非常に簡単になります。それ以外の場合、binary(16)は妥当ですが、通常の文字列表現から前後に変換するには、MySQLや選択したプログラミング言語に頼る必要があります。

17
candu

Binary(16)は、varchar(32)の使用よりも優れています。

8
Onkar Janwa

GUID文字列のタイムスタンプのビットレイアウトを考慮して、KCDによって投稿されたGuidToBinaryルーチンを調整する必要があります。文字列が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が最初に進み、次に中間ビットと下位ビットが続きます。 1分間でuuid()を使用して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が同じニブルで始まることを意味します。記事は同じことをします。

7
bigh_29

これにつまずいた人のために、Perconaの研究によると、今でははるかに優れた代替手段があります。

最適なインデックス作成のためにUUIDチャンクを再編成し、ストレージを削減するためにバイナリに変換します。

記事全文を読む here

5
sleepycal

@ bigh_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 ;
2
vsdev