MySQL의 UUID 성능?


83

MySQL 데이터베이스의 기본 키로 UUID 값을 사용하는 것을 고려하고 있습니다. 삽입되는 데이터는 수십, 수백 또는 수천 대의 원격 컴퓨터에서 생성되며 초당 100 ~ 40,000 개의 삽입 속도로 삽입되며 업데이트를 수행하지 않습니다.

데이터베이스 자체는 일반적으로 데이터를 추출하기 시작하기 전에 약 5 천만 개의 레코드를 가져 오므로 대규모 데이터베이스는 아니지만 작지도 않습니다. 우리는 또한 InnoDB에서 실행할 계획이지만, 우리가하고있는 일에 더 나은 엔진이 있다면 그것을 변경할 수 있습니다.

우리는 Java의 Type 4 UUID를 사용할 준비가되었지만 테스트에서 이상한 동작을 보았습니다. 첫째로, 우리는 varchar (36)로 저장하고 있으며 이제 binary (16)을 사용하는 것이 더 낫다는 것을 알고 있습니다.

더 큰 질문은 5 천만 개의 레코드가있을 때이 임의의 데이터가 인덱스를 얼마나 심하게 망가 뜨리는가입니다. 예를 들어 가장 왼쪽 비트에 타임 스탬프가 찍힌 유형 1 UUID를 사용하는 것이 더 나을까요? 아니면 UUID를 완전히 버리고 auto_increment 기본 키를 고려해야할까요?

여러 유형의 UUID가 MySQL에서 인덱스 / 기본 키로 저장 될 때 성능에 대한 일반적인 생각 / 팁을 찾고 있습니다. 감사!


2
한 가지 중요한 세부 정보가 누락되었습니다. 기본 키가 로깅 서버에 의해 생성됩니까 아니면 클라이언트 시스템 자체에 의해 생성됩니까?

1
@hop 그들은 데이터를 삽입하는 10-1000 클라이언트에 의해 생성되고 있습니다
Patrick Lightbody

시나리오에서 보편적 인 고유성이 필요한 곳은 어디입니까? 내 조언은 auto_increment를 고수하고 데이터를 보내는 원격 컴퓨터를 설명하기 위해 별도의 필드를 사용하는 것입니다. 여기서 바퀴를 재발 명 할 필요가 없습니다.
Theodore Zographos 2011

답변:


35

UUID는 범용 고유 ID입니다. 여기서 고려해야 할 보편적 인 부분입니다.

당신이 할 정말 보편적으로 고유하게 ID를해야합니까? 그렇다면 UUID가 유일한 선택 일 수 있습니다.

난 강력하게 당신이 경우에 것을 제안 사용 UUID를, 당신은 숫자로 아닌 문자열로 저장합니다. 5 천만 개 이상의 레코드가있는 경우 저장 공간을 절약하면 성능이 향상됩니다 (얼마나 말할 수는 없지만).

ID가 보편적으로 고유 할 필요가없는 경우 auto_increment를 사용하는 것보다 훨씬 더 잘할 수 있다고 생각하지 않습니다. 이는 ID가 테이블 내에서 고유하다는 것을 보장합니다 (값이 매번 증가하기 때문에).


2
흥미로운 점; 이것은 키 생성을 병렬화합니다. 나는 이것이 키 생성의 성능을 향상시킬 것이라고 믿습니다. 그러나 UUID를 저장하기 위해 VARCHAR을 사용하는 경우 SELECT 성능보다 INSERT 성능을 선택합니다. SELECT 성능을 보장하려면 저장을 위해 VARBINARY를 선택해야합니다. 추가 단계 INSERT 성능에 영향을 줄 있지만 SELECT 성능 향상으로 보상을 받게됩니다.
Dancrumb 2010 년

12
우리는 실제 데이터에 대한 벤치마킹을 수행했으며 키가없는 GUID는 매우 빠르며 키가있는 GUID는 끔찍했으며 (BINARY로 저장되는 경우에도) int w / AUTO_COMPLETE가 가장 빠릅니다. 나는 우리의 경우에, 더 많은 데이터를 저장하는 비용 + GUID의 무작위성으로 인해 정말 엉터리 BTREE를 갖는 것에 비해 시퀀스 생성이 중요하지 않은 것처럼 보였기 때문에 우리는 실제로 나무에서 숲을 놓치고 있다고 생각합니다
Patrick Lightbody

1
숫자로 저장은 이진 형식으로 저장하는 것을 의미합니까? 그러나 바이너리 형식은 사람이 읽을 수 없습니다. uuid 기본 키의 큰 바이트 때문에 느립니다? 그렇다면 uuid에 대한 다른 열과 함께 자동 증가를 저장할 수 있습니다. 그러면 성능이 저하되지 않습니다. 내가 맞아?
Chamnap

4
엄밀히 말해서 UUID는 보편적으로 독특합니다. 즉, 세계 어느 곳에서도 나타나지 않을 것입니다. 데이터를 공개적으로 공유하는 경우에만 필요합니다. UUID를 숫자로 저장하는 것은 binary형식을 의미하지 않습니다 . 288 비트 문자열이 아니라 128 비트 숫자를 의미합니다. 예를 들어 ASCII에서 'hello'라는 단어 68 65 6C 6C 6F는 448,378,203,247입니다. 문자열 '68656C6C6F'를 저장하려면 10 바이트가 필요합니다. 번호 448378203247 당신이하지 않는 한, 모든 만 5 모두 필요 정말 UUID의 첫 U이 필요합니다, 당신은 훨씬 더 이상 할 수 없어auto_increment
Dancrumb

1
O) : @Chamnap : 당신은 스택 오버플로 질문 물어 제안
Dancrumb

78

제 직장에서는 UUID를 PK로 사용합니다. 경험을 통해 알 수있는 것은 PK (SQL Server)로 사용하지 마십시오.

레코드가 1000 개 미만이면 괜찮지 만, 수백만 개가 있으면 할 수있는 최악의 일입니다. 왜? UUID는 순차적이 아니기 때문에 새 레코드가 삽입 될 때마다 MSSQL은 레코드를 삽입 할 올바른 페이지를 찾은 다음 레코드를 삽입해야합니다. 이로 인한 정말 추악한 결과는 페이지가 모두 다른 크기로 끝나고 조각화되므로 이제 주기적으로 조각 모음을 수행해야합니다.

자동 증가를 사용할 때 MSSQL은 항상 마지막 페이지로 이동하고 동일한 크기의 페이지 (이론상)로 끝나므로 해당 레코드를 선택하는 성능이 훨씬 좋습니다 (또한 INSERT가 테이블 / 페이지를 차단하지 않기 때문입니다. 안녕).

그러나 UUID를 PK로 사용하는 가장 큰 장점은 DB 클러스터가 있으면 병합 할 때 충돌이 발생하지 않는다는 것입니다.

다음 모델을 권장합니다. 1. PK INT Identity 2. UUID로 자동 생성되는 추가 열.

이런 식으로 병합 프로세스가 가능합니다 (UUID는 REAL 키가되고 PK는 좋은 성능을 제공하는 일시적인 것입니다).

참고 : 가장 좋은 해결책은 NEWSEQUENTIALID를 사용하는 것입니다 (댓글에서 말한 것처럼).하지만 리팩터링 할 시간이 많지 않은 레거시 앱의 경우 (심지어 모든 삽입을 제어하지 않는 경우) 불가능합니다. 그러나 실제로 2017 년 현재 가장 좋은 해결책은 NEWSEQUENTIALID 또는 NHibernate와 함께 Guid.Comb을 수행하는 것입니다.

도움이 되었기를 바랍니다


이 용어가 무엇을 의미하는지 잘 모르겠지만 사실은 인덱스를 매월 다시 색인화해야한다는 것입니다. 당신이 언급 한 것이 재 인덱싱 작업을 제거한다면, 모르겠지만 물어볼 수 있습니다.
Kat Lim Ruiz 2012

3
제가 생각했던 것은 이것이 부모-자녀 관계에서 잘 작동하지 않을 수 있다는 것입니다. 이 경우에는 parent-pk, parent-guid라는 자식 테이블을 추가해야한다고 생각합니다. 그렇지 않으면 데이터베이스 간의 참조가 손실 될 수 있습니다. 내가 너무 많은이의 생각이나 어떤 예제를 수행하지만,이 필요할 수 있습니다하지 않은
캣 임 루이즈를

4
당신이 NEWSEQUENTIALID ()를 사용하여 SQL 서버에 @KatLimRuiz technet.microsoft.com/en-us/library/ms189786.aspx 성능 문제를 방지하기 위해
giammin

사실, 그러나 NEWSEQUENTIALID는 DEFAULT로만 작동합니다. 당신은 큰 유산을 위해 그렇게 쉬운 일이 아닙니다 새로운 프로젝트에 대한 확인 그러나 이것은 주위에 당신의 전체 DAL, 디자인해야합니다 그래서
캣 임 루이즈

@KatLimRuiz 천재. 즉 좋은 타협이다
jmgunn87

26

고려해야 할 사항은 자동 증가가 한 번에 하나씩 생성되며 병렬 솔루션을 사용하여 해결할 수 없다는 것입니다. UUID 사용을위한 싸움은 궁극적으로 달성하고자하는 것과 잠재적으로 희생하는 것에 달려 있습니다.

성능에 대해 간단히 :

위와 같은 UUID는 대시를 포함하여 36 자입니다. 이 VARCHAR (36)을 저장하면 비교 성능이 크게 저하됩니다. 이것이 기본 키이며 느리기를 원하지 않습니다.

비트 수준에서 UUID는 128 비트입니다. 즉, 16 바이트에 맞습니다. 사람이 읽을 수있는 수준은 아니지만 저장소를 낮게 유지하고 32 비트 int보다 4 배 더 큽니다. 64 비트 정수보다 배 큽니다. 이론적으로는 VARBINARY (16)를 사용하겠습니다. 이것은 많은 오버 헤드없이 작동 할 수 있습니다.

다음 두 게시물을 읽는 것이 좋습니다.

나는 둘 사이에서 생각하고 그들은 당신의 질문에 대답합니다.


2
사실 저는이 질문을 게시하기 전에 두 기사를 모두 읽었지만 여전히 여기에 좋은 대답이 없었습니다. 예를 들어, 유형 1 대 유형 4 UUIDS에 대해 이야기하지 마십시오. :(
Patrick Lightbody

공평하게, 나는 내 대답을 업데이트했습니다. 그러나 나는 그것이 너무 많은 추가 통찰력을 제공한다고 생각하지 않습니다.
Kyle Rosendo

@Patrick : 질문에 너무 많은 주제를 넣었습니다.

1
9 년이 지났지 만 정수 ID와 달리 앱은 UUID를 안전하게 생성하여 데이터베이스에서 생성을 완전히 제거 할 수 있다는 점에 주목해야합니다. 성능 최적화를위한 UUID의 조작 (타임 스탬프 기반이지만 순진하게 정렬 할 수 있도록 수정 됨)은 SQL 이외의 거의 모든 언어에서 매우 쉽습니다. 다행히 오늘날 거의 모든 데이터베이스 (MySQL 포함)는 UUID 기본 키를 예전보다 훨씬 더 잘 처리합니다.
Miles Elam

5

나는 단순히 저장하는 것이 고통스럽고 기본 키로 사용하는 것이 고통이기 때문에 UUID를 피하는 경향이 있지만 장점이 있습니다. 주요한 것은 그들이 고유하다는 것입니다.

나는 일반적으로 이중 키 필드를 사용하여 문제를 해결하고 UUID를 피합니다.

수집기 = 기계에 할당 된 고유

ID = COLLECTOR가 수집 한 레코드 (auto_inc 필드)

이것은 나에게 두 가지를 제공합니다. 자동 통합 필드의 속도와 함께 수집 및 그룹화 한 후 중앙 위치에 저장되는 데이터의 고유성. 나는 또한 데이터가 수집 된 위치를 탐색하는 동안 알고 있는데 이는 종종 내 필요에 매우 중요합니다.

나는 UUID를 사용하기로 결정한 클라이언트에 대한 다른 데이터 세트를 처리하는 동안 많은 경우를 보았습니다.하지만 여전히 데이터가 수집 된 필드가있어 실제로 노력 낭비입니다. 두 개 (또는 필요한 경우 더 많은) 필드를 키로 사용하면 정말 도움이됩니다.

UUID를 사용하여 너무 많은 성능 저하를 보았습니다. 그들은 속임수처럼 느낍니다 ...


3

각 삽입에 대해 중앙에서 고유 키를 생성하는 대신 개별 서버에 키 블록을 할당하는 것은 어떻습니까? 키가 부족하면 새 블록을 요청할 수 있습니다. 그런 다음 각 인서트를 연결하여 오버 헤드 문제를 해결합니다.

키 서버는 사용 가능한 다음 ID를 유지합니다.

  • 서버 1은 ID 블록을 요청합니다.
  • 서버 는 (1,1000)을 반환합니다.
    서버 1은 새 블록을 요청할 때까지 1000 개의 레코드를 삽입 할 수 있습니다.
  • 서버 2는 인덱스 블록을 요청합니다.
  • 키 서버 반환 (1001,2000)
  • 기타...

서버가 필요한 키의 수를 요청하거나 사용되지 않은 블록을 키 서버에 반환 할 수있는보다 정교한 버전을 생각해 낼 수 있습니다. 그러면 사용 / 미사용 블록의 맵을 유지해야합니다.


이론에 대한 흥미로운 제안. 이것은 실제로 관리하기가 복잡합니다. 더 실용적인 해결책은 아마도 schworak이 제시 한 대답 일 것입니다.
Simon East

2

트랜잭션 방식으로 각 서버에 숫자 ID를 할당합니다. 그런 다음 삽입 된 각 레코드는 자체 카운터를 자동으로 증가시킵니다. ServerID와 RecordID의 조합은 고유합니다. ServerID 필드는 인덱싱 될 수 있으며 ServerID (필요한 경우)를 기반으로 향후 선택 성능이 훨씬 더 좋을 수 있습니다.


2

짧은 대답은 많은 데이터베이스가 인덱싱 방법과 고차 비트에서 UUID의 의도적 인 엔트로피 간의 충돌로 인해 성능 문제 (특히 높은 INSERT 볼륨)가 있다는 것입니다. 몇 가지 일반적인 해킹이 있습니다.

  • 신경 쓰지 않는 다른 인덱스 유형 (예 : MSSQL에서 클러스터되지 않음)을 선택하십시오.
  • 데이터를 뭉쳐서 엔트로피를 하위 비트로 이동 (예 : MySQL에서 V1 UUID의 바이트 재정렬)
  • 자동 증가 int 기본 키를 사용하여 UUID를 보조 키로 만듭니다.

...하지만 이것들은 모두 해킹이며 아마도 깨지기 쉬운 것입니다.

가장 좋은 대답은 안타깝게도 가장 느린 방법은 다른 유형과 마찬가지로 UUID를 기본 키로 처리 할 수 ​​있도록 공급 업체에 제품 개선을 요구하는 것입니다. 그들은 일반적인 사용 사례가 된 것을 해결하지 못한 것을 보완하기 위해 자신의 반쯤 구운 해킹을 강요해서는 안되며 계속 성장할 것입니다.


1

손으로 만든 UID는 어떻습니까? 수천 대의 서버 각각에 ID를 부여하고 기본 키를 autoincrement, MachineID의 콤보 키로 만듭니다. ???


나는 그것에 대해 생각했고 몇 가지 벤치 마크를 실행해야 할 수도 있습니다. 타임 스탬프와 결합 된 1000 대의 머신 각각에 대한 임시 로컬 시퀀스도 충분할 수 있습니다. 예 : machine_id + temp_seq + timestamp
Patrick Lightbody

모든 타임 스탬프 틱을 재설정하는 temp_sequence를 가질 수 있습니까? 잘 모르겠습니다.
MindStalker 2010 년

1

기본 키는 분산되어 생성되므로 어쨌든 auto_increment를 사용할 수있는 옵션이 없습니다.

원격 컴퓨터의 ID를 숨길 필요가없는 경우 UUID 대신 유형 1 UUID를 사용합니다. 생성하기가 더 쉽고 최소한 데이터베이스의 성능을 손상시키지 않습니다.

varchar (사실상)와 바이너리의 경우도 마찬가지입니다. 성능이 얼마나 향상되는지 정말 중요합니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.