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


146

varchar(36)を使用しますか、それとももっと良い方法がありますか?


1
「thaBadDawg」は良い答えを提供します。Stack Overflowには、このトピックについて説明する並列スレッドがあります。スレッドにコメントを追加して、リソースへのリンクに詳細に回答します。こちらが質問リンクです。stackoverflow.com / questions / 547118 / storing-mysql-guid-uuids -AWSとAuroraを検討し始めたときに、このトピックがより一般的になることを期待しています。
ザックヤンセン、2016

回答:


104

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

記憶領域を最適に使用する場合は、GUIDをCHAR(16)バイナリとして格納できます。


176
16バイトを使用すると、さまざまなデータベース、さまざまなマシン、さまざまなタイミングでデータを生成でき、データをシームレスにマージできます:)
Billy ONeal

4
返信が必要です。char16バイナリとは何ですか?チャーじゃない?バイナリではありませんか?mysqlのguiツールや、mysqlのサイトにあるドキュメントにそのタイプが表示されません。@BillyONeal
nawfal

3
@nawfal:Charはデータ型です。BINARYは、型に対する型指定子です。唯一の効果は、MySQLが照合を行う方法を変更することです。詳細については、dev.mysql.com / doc / refman / 5.0 / en / charset-binary-op.htmlを参照してください。もちろん、データベース編集ツールで可能であれば、BINARY型を直接使用することもできます。(古いツールはバイナリデータタイプを認識していませんが、バイナリ列フラグを認識しています)
Billy ONeal

2
CHARフィールドとBINARYフィールドは基本的に同じです。最も基本的なレベルにしたい場合、CHARは0〜255の値を期待するバイナリフィールドであり、ルックアップテーブルからマップされた値(ほとんどの場合、現在はUTF8)でその値を表すことを意図しています。BINARYフィールドは、ルックアップテーブルからの前記データを表す意図なしに、同じ種類の値を期待します。4.xの時代にCHAR(16)を使用したのは、当時のMySQLは現在ほど良くなかったためです。
thaBadDawg

15
GUIDが自動インクリメントよりもはるかに優れている理由はいくつかあります。ジェフ・アトウッドがこれらをリストします。私にとって、GUIDを使用する最大の利点は、エンティティのキ​​ーを知るためにデータベースのラウンドトリップをアプリで行う必要がないことです。自動インクリメントフィールドを使用している場合は、プログラムでエンティティを設定できましたが、これはできませんでした。これにより、いくつかの頭痛の種から私を救いました:GUIDを使用すると、エンティティが既に永続化されているか、まったく新しいものであるかに関係なく、同じ方法でエンティティを管理できます。
Arialdo Martini

48

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


5
-s を保存する理由がわかりません。
Afshin Mehrabani 2017年

2
@AfshinMehrabaniシンプルでわかりやすく、人間が読める形式です。もちろん、それは必要ではありませんが、それらの余分なバイトを格納しても問題がなければ、これが最善の解決策です。
user1717828 2018年

2
ダッシュを格納するとオーバーヘッドが増えるため、適切な方法ではない場合があります。人間が読めるようにしたい場合は、アプリケーションをダッシュ​​で読み取ります。
ルッカフェリ

@AfshinMehrabaniもう1つの考慮事項は、データベースからの解析です。ほとんどの実装では、有効なGUIDにダッシュが必要です。
ライアンゲイツ

フェッチ時にハイフンを挿入して、char(32)をchar(36)に簡単に変換できます。mySqlのInsert FNを使用します。
joedotnot

33

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

文字列からハイフンを削除しない上記のGuidToBinaryは次のとおりです。CREATE FUNCTION GuidToBinary($ guid char(36))RETURNS binary(16)RETURN CONCAT(UNHEX(SUBSTRING($ guid、7、2))、UNHEX(SUBSTRING($ guid、 5、2))、UNHEX(SUBSTRING($ guid、3、2))、UNHEX(SUBSTRING($ guid、1、2))、UNHEX(SUBSTRING($ guid、12、2))、UNHEX(SUBSTRING($ guid、10、2))、UNHEX(SUBSTRING($ guid、17、2))、UNHEX(SUBSTRING($ guid、15、2))、UNHEX(SUBSTRING($ guid、20、4))、UNHEX(SUBSTRING ($ guid、25、12)));
Jonathan Oliver

4
奇妙なことに、これらの関数はUNHEX(REPLACE(UUID()、 '-'、 ''))よりも優れています。これは、クラスター化インデックスでパフォーマンスが向上する順序でビットを配置するためです。
Slashterix、2014

これは非常に便利ですが、私はためのソースで改善することができた感じCHARBINARY(等価ドキュメントは重要な違いとクラスタ化インデックスのパフォーマンスは、並べ替えバイトで優れている理由の説明がある暗示しているようだ。
パトリック・M

これを使用すると、私のguidが変更されます。unhex(replace(string、 '-'、 ''))と上記の関数の両方を使用して挿入しようとしましたが、同じメソッドを使用してそれらを元に戻すと、選択されたGUIDは挿入されたものではありません。GUIDの変換とは何ですか?私がやったことはすべて上からコードをコピーしたことです。
vsdev 2015

@JonathanOliver BinaryToGuid()関数のコードを共有していただけませんか?
アルンAvanathan

27

char(36)は良い選択です。また、MySQLのUUID()関数を使用して、データベースからのIDの取得に使用できる36文字のテキスト形式(ハイフン付きの16進数)を返すこともできます。


19

「より良い」とは、最適化する対象によって異なります。

ストレージのサイズ/パフォーマンスと開発の容易さのどちらを気にしますか?さらに重要なことは、重要なことですが、十分なGUIDを生成していますか、それとも頻繁にフェッチしていますか?

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


2
ソフトウェア(たとえばWebページなど)をホストしていて、クライアントで販売/インストールしない場合は、常にchar(36)から始めて、ソフトウェアの初期段階で簡単に開発し、よりコンパクトに変更できます。システムの使用が拡大し、最適化が必要になると、フォーマットが変わります。
Xavi Montero 2014

1
はるかに大きなchar(36)の最大の欠点は、インデックスが占める領域です。データベースに多数のレコードがある場合、インデックスのサイズが2倍になります。
bpeikes 2015


7

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が最初に移動し、その後に中位ビットと下位ビットが続きます。1分間でuuid()を使用してダースほどの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が同じニブルで始まることを意味します。記事は同じことを行います。


これはストレージスペースを節約する目的ですか?またはそれらを分類することを有用にするために?
MD004

1
@ MD004。それはより良いソートインデックスを作成します。スペースは変わりません。
bigh_29 2017年

5

これを偶然見つけた人のために、Perconaによる研究によると、はるかに優れた代替手段が今あります。

これは、インデックスを最適化するためにUUIDチャンクを再編成し、ストレージを削減するためにバイナリに変換することで構成されます。

ここで記事全体を読む


以前その記事を読んだ。非常に興味深いと思いますが、バイナリのIDでフィルター処理したい場合は、クエリをどのように実行すればよいでしょうか。もう一度ヘクスして基準を適用する必要があると思います。それはとても厳しいですか?8バイトのbigintではなく、binary(16)を格納する理由(varchar(36)より優れていることを確認してください)。
Maximus Decimus

2
あなたの質問に答える必要がありMariaDBから更新された記事がありますmariadb.com/kb/en/mariadb/guiduuid-performanceを
sleepycal

fwiw、UUIDv4は完全にランダムであり、チャンクを必要としません。
Mahmoud Al-Qudsi

2

@ 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 ;

-4

char / varchar値が標準のGUIDとしてフォーマットされている場合は、単純なCAST(MyString AS BINARY16)を使用して、BINCAT(16)として格納するだけで済みます。

BINARY(16)フィールドは、文字列よりもはるかに高速に比較/並べ替え/インデックス付けされ、データベース内のスペースが2分の1になります


2
このクエリを実行すると、CASTがuuid文字列をASCIIバイトに変換することがわかります。set @a = uuid(); select @a、hex(cast(@a AS BINARY(16))); 16f20d98-9760-11e4-b981-feb7b39d48d6:3136663230643938 2D 39373630 2D 3131(フォーマット用にスペースが追加されました)が表示されます。0x31 = ascii 1、0x36 = ascii 6.ハイフンである0x2Dも得られます。これは、GUIDを文字列として格納することと大差ありませんが、16番目の文字で文字列を切り捨て、マシン固有のIDの部分を切り離します。
bigh_29

はい、これは単に切り捨てです。select CAST("hello world, this is as long as uiid" AS BINARY(16));生産hello world, thi
MD004
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.