MySQL 테이블에 GUID를 어떻게 저장해야합니까?


146

varchar (36)를 사용하거나 더 좋은 방법이 있습니까?


1
"thaBadDawg"는 좋은 답변을 제공합니다. 스택 오버플로에는 주제를 설명하는 병렬 스레드가 있습니다. 스레드에 답변을 추가하여 리소스에 대한 자세한 내용을 제공합니다. 질문 링크는 다음과 같습니다. stackoverflow.com/questions/547118/storing-mysql-guid-uuids- 사람들이 AWS와 Aurora를 고려하기 시작할 때이 주제가 더 일반적이 될 것으로 기대합니다.
잭 얀센

답변:


104

내 DBA는 객체에 GUID를 저장하는 가장 좋은 방법에 대해 물었을 때 Integer로 4 바이트로 같은 일을 할 수있을 때 16 바이트를 저장 해야하는 이유를 물었습니다. 그가 저에게 그 도전을 줬기 때문에 나는 그것을 언급하기에 좋은 때라고 생각했습니다. 그 말은 ...

스토리지 공간을 최대한 활용하려면 guid를 CHAR (16) 이진으로 저장할 수 있습니다.


176
16 바이트를 사용하면 다른 데이터베이스, 다른 머신, 다른 시간에 다른 것을 생성하고 데이터를 완벽하게 병합 할 수 있습니다. :)
Billy ONeal

4
응답이 필요합니다. char 16 바이너리는 무엇입니까? 숯이 아니야? 바이너리가 아닙니까? 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은 룩업 테이블 (대부분의 경우 현재 UTF8)에서 맵핑 된 값으로 해당 값을 표시하기 위해 0-255 값을 예상하는 2 진 필드입니다. BINARY 필드는 룩업 테이블에서 상기 데이터를 나타내려는 의도없이 동일한 종류의 값을 예상합니다. MySQL은 지금만큼 좋지 않았기 때문에 4.x 일에 CHAR (16)을 다시 사용했습니다.
thaBadDawg

15
GUID가 자동 증분보다 훨씬 좋은 몇 가지 이유가 있습니다. Jeff Atwood가 이것들을 나열 합니다 . GUID를 사용하는 가장 좋은 장점은 내 앱에 엔터티의 키를 알기 위해 데이터베이스 왕복이 필요하지 않다는 것입니다. 프로그래밍 방식으로 데이터를 채울 수 있으며 자동 증가 필드를 사용하는 경우에는 수행 할 수 없습니다. GUID를 사용하면 엔터티가 이미 지속되었거나 새로운 것이 든 관계없이 동일한 방식으로 엔터티를 관리 할 수 ​​있습니다.
Arialdo Martini

48

나는 그것을 char (36)로 저장할 것이다.


5
-s 를 보관해야하는 이유를 알 수 없습니다 .
Afshin Mehrabani

2
@AfshinMehrabani 간단하고 간단하며 사람이 읽을 수 있습니다. 물론 필요하지는 않지만 여분의 바이트를 저장해도 문제가되지 않으면 이것이 최선의 해결책입니다.
user1717828

2
대시를 저장하면 더 많은 오버 헤드가 발생하기 때문에 좋지 않습니다. 사람이 읽을 수있게하려면 응용 프로그램을 대시와 함께 읽습니다.
Lucca Ferri

@AfshinMehrabani 또 다른 고려 사항은 데이터베이스에서 구문 분석하는 것입니다. 대부분의 구현은 유효한 guid로 대시를 기대합니다.
Ryan Gates

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 (), '-', ''))보다 우수합니다. 클러스터형 인덱스에서 비트가 더 잘 수행되는 순서로 비트를 정렬하기 때문입니다.
슬래시 릭스

이것은 매우 도움이되지만 소스 CHARBINARY동등성 으로 개선 될 수 있다고 생각합니다 ( 문서 는 중요한 차이가 있고 순서가 바뀐 바이트에서 클러스터형 인덱스 성능이 더 나은 이유에 대한 설명을 암시하는 것 같습니다.
Patrick M

이것을 사용하면 내 guid가 변경됩니다. unhex (replace (string, '-', ''))와 위의 함수를 모두 사용하여 삽입을 시도했지만 동일한 방법을 사용하여 다시 변환하면 선택한 guid가 삽입 된 것이 아닙니다. guid를 변형시키는 것은 무엇입니까? 내가 한 모든 것은 위에서 코드를 복사 한 것입니다.
vsdev

@JonathanOliver BinaryToGuid () 함수의 코드를 공유해 주시겠습니까?
Arun Avanathan

27

char (36)이 좋은 선택입니다. 또한 MySQL의 UUID () 함수를 사용하여 36 자 텍스트 형식 (하이픈이있는 16 진수)을 반환하여 db에서 이러한 ID를 검색하는 데 사용할 수 있습니다.


19

"더 나은"은 최적화하는 대상에 따라 다릅니다.

스토리지 크기 / 성능 대 개발 용이성에 대해 얼마나 관심이 있습니까? 더 중요한 것은-충분한 GUID를 생성하거나 자주 가져 오는 것이 중요합니까?

대답이 "아니오"이면 char(36)충분하고 GUID를 저장 / 가져 오기가 매우 간단합니다. 그렇지 않으면 binary(16)합리적이지만 일반적인 문자열 표현에서 앞뒤로 변환하려면 MySQL 및 / 또는 프로그래밍 언어를 사용해야합니다.


2
소프트웨어 (예 : 웹 페이지)를 호스팅하고 클라이언트에서 판매 / 설치하지 않는 경우, 소프트웨어 초기 단계에서 쉽게 개발할 수 있도록 항상 char (36)으로 시작하여보다 컴팩트하게 변경할 수 있습니다 시스템 사용량이 증가하고 최적화가 필요 해짐에 따라
Xavi Montero

1
훨씬 큰 char (36)의 가장 큰 단점은 인덱스가 차지하는 공간입니다. 데이터베이스에 많은 수의 레코드가있는 경우 인덱스 크기가 두 배가됩니다.
bpeikes


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 ()를 사용하여 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 

처음 두 UUID는 가장 가까운 시간에 생성되었습니다. 그것들은 첫 번째 블록의 마지막 3 니블에서만 다릅니다. 이것들은 타임 스탬프에서 가장 중요하지 않은 비트이므로 인덱스 가능한 바이트 배열로 변환 할 때 오른쪽으로 푸시하려고합니다. 반대의 예로서, 마지막 ID가 가장 최신이지만, KCD의 스와핑 알고리즘은이를 3 번째 ID (3e 앞의 3e, 첫 번째 블록의 마지막 바이트) 앞에 둡니다.

올바른 색인 순서는 다음과 같습니다.

1e497556eec5eb6... 
1e497556eec5f10... 
1e497556eec8ddc... 
1e497556eee30d0... 
1e497556efda038... 
1e497556f9641bf... 
1e49755758c3e3e... 

지원 정보는이 기사를 참조하십시오 : http://mysql.rjweb.org/doc.php/uuid

*** 버전 니블을 타임 스탬프의 상위 12 비트에서 분리하지 않습니다. 이것은 귀하의 예에서 D 니블입니다. 방금 던졌습니다. 따라서 이진 시퀀스는 DEFG9ABC 등이됩니다. 이것은 색인 된 모든 UUID가 동일한 니블로 시작한다는 것을 의미합니다. 기사도 마찬가지입니다.


저장 공간을 절약하기위한 목적입니까? 또는 정렬을 유용하게 만들려면?
MD004

1
@ MD004. 더 나은 정렬 색인을 작성합니다. 공간은 동일하게 유지됩니다.
bigh_29

5

이 문제를 극복하는 데있어 Percona의 연구에 따르면 훨씬 더 나은 대안이 있습니다.

최적의 인덱싱을 위해 UUID 청크를 재구성 한 다음 스토리지를 줄이기 위해 이진으로 변환합니다.

전체 기사를 여기에서 읽으 십시오


나는 전에 그 기사를 읽었다. 매우 흥미롭지 만 이진 ID로 필터링하려면 어떻게 쿼리를 수행해야합니까? 우리는 다시 16 진수를 한 다음 기준을 적용해야한다고 생각합니다. 너무 까다로운가요? 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

표준 GUID로 형식화 된 char / varchar 값이있는 경우 CONCAT + SUBSTR의 복잡한 순서없이 간단한 CAST (MyString AS BINARY16)를 사용하여 간단히 BINARY (16)로 저장할 수 있습니다.

BINARY (16) 필드는 문자열보다 훨씬 빠르게 비교 / 정렬 / 인덱싱되며 데이터베이스의 공간이 2 배 줄어 듭니다.


2
이 쿼리를 실행하면 CAST가 uuid 문자열을 ASCII 바이트로 변환합니다. set @a = uuid (); @a, hex (cast (@a AS BINARY (16)))를 선택하십시오. 16f20d98-9760-11e4-b981-feb7b39d48d6 : 3136663230643938 2D 39373630 2D 3131 (포맷을 위해 추가 된 공백)이 표시됩니다. 0x31 = ascii 1, 0x36 = ascii 6. 심지어 하이픈 인 0x2D도 얻습니다. 16 번째 문자에서 문자열을 자르는 것을 제외하고는 guid를 문자열로 저장하는 것과 크게 다르지 않습니다. 이는 기계 고유의 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.