GUID를 기본 키로 사용


32

나는 일반적으로 데이터베이스에서 자동 증가 ID를 기본 키로 사용합니다. GUID 사용의 이점을 배우려고합니다. 나는이 기사를 읽었다 : https://betterexplained.com/articles/the-quick-guide-to-guids/

이 GUID는 응용 프로그램 수준에서 개체를 식별하는 데 사용됩니다. 또한 데이터베이스 레벨에서 기본 키로 저장됩니다. 예를 들어 다음과 같은 클래스가 있다고 가정 해보십시오.

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

메모리에 새 사람을 만든 다음 사람을 데이터베이스에 삽입하고 싶다고 가정 해보십시오. 그냥 할 수 있을까요 :

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

GUID를 기본 키로 사용하여 수백만 행과 수백만 행을 포함하는 데이터베이스가 있다고 가정 해보십시오. 이것이 항상 독특합니까? GUID를 올바르게 이해하고 있습니까?

나는이 기사를 이전에 읽었다 : http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . GUID와 정수 사이의 행복한 매체를 기본 키로 권장하는 것처럼 보이므로 약간 혼란 스럽습니다.

11/06/18 수정

Guids가 내 요구 사항에 맞는 정수보다 더 적합하다고 믿었습니다. 요즘 CQRS를 더 많이 사용하고 있으며 GUID가 더 잘 맞습니다.

일부 개발자는 도메인 모델에서 GUID를 문자열로 모델링합니다 (예 : https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs- 이 경우 : IdentityGuid는 문자열로 모델링 된 GUID입니다. 여기에 명시된 것 이외의 다른 이유가 있습니까? 분산 시스템에서 사용자 지정 값 개체 또는 Guid를 엔터티 식별자로 사용합니까? . GUID를 문자열로 모델링하는 것이 "정상"입니까, 아니면 모델 및 데이터베이스에서 GUID로 모델링해야합니까?



7
독특하다고 보장 할 수는 없지만 충돌이 발생할 가능성은 거의 없습니다. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik

2
참조 : UUID 충돌
gnat

2
dba.stackexchange.com/questions/54690/… 및 기타 많은 질문을 참조하십시오. 이 주제는 자주 질문되고 답변되었으며 논쟁이있었습니다.
그린 스톤 워커

1
현재 작업중 인 시스템은 UUID를 사용합니다. 좋은 특성은 해당 테이블에서 레코드를 식별하는 순차 ID와 달리 ID가 레코드를 고유하게 식별한다는 것입니다.
Justin

답변:


41

GUID는 정의상 "전역 고유 식별자"입니다. Java에는 UUID "Universally Unique IDentifiers"라는 개념이 비슷하지만 약간 다릅니다. 모든 실제 사용을 위해 이름을 서로 바꿀 수 있습니다.

GUID는 Microsoft가 계획 한 데이터베이스 클러스터링 작동 방식의 중심이며, 때때로 연결된 소스의 데이터를 통합해야하는 경우 실제로 데이터 충돌을 방지하는 데 도움이됩니다.

일부 Pro-GUID 사실 :

  • GUID는 키 충돌을 방지합니다
  • GUID는 네트워크, 컴퓨터 등의 데이터를 병합하는 데 도움이됩니다.
  • SQL Server는 인덱스 순차 화를 최소화하기 위해 반 순차 GUIDS를 지원합니다 ( ref , 일부 경고)

GUID를 사용한 일부 추악함

  • 그들은 각각 16 바이트입니다.
  • 순서가 잘못되었으므로 ID를 기준으로 정렬 할 수 없으며 자동 증가 ID에서와 같이 게재 신청서를 얻을 수 있기를 바랍니다.
  • 특히 작은 데이터 세트 (조회 테이블과 같은)에서 작업하기가 더 번거 롭습니다.
  • 새로운 GUID 구현은 C # 라이브러리보다 SQL Server에서 더 강력합니다 (C #에서는 임의의 SQL Server에서 순차적 GUIDS를 가질 수 있음)

GUID를 사용하면 인덱스가 더 커지므로 열을 인덱싱하는 데 필요한 디스크 공간 비용이 높아집니다. 임의의 GUID는 색인을 조각화합니다.

다른 네트워크의 데이터를 동기화하지 않을 것으로 예상되는 경우 GUID는 가치보다 더 많은 오버 헤드를 전달할 수 있습니다.

때때로 연결된 클라이언트에서 데이터를 수집해야하는 경우 해당 클라이언트의 시퀀스 범위 설정에 의존하는 것보다 키 충돌을 방지하는 데 훨씬 강력 할 수 있습니다.


18
GUID는 UUID와 동의어입니다. UUID는 표준 이름입니다. GUID는 RFC 4122 이전에 Microsoft가 만든 것 입니다.
JimmyJames

13
"그들은 순서가 잘못되었으므로 ID를 정렬 할 수 없으며 자동 증분 ID와 마찬가지로 게재 신청서를 얻을 수 있기를 바랍니다." 극단적 인 경우에는 하위 ID가 나중에 디스크에 커밋 될 수 있지만 삽입 타임 스탬프와 같은 유용한 정렬 데이터에 의존하고 싶습니다. ID는 메모리 주소처럼 취급되어야합니다. 모든 것이 하나를 가지고 있지만 그 자체는 의미가 없습니다. 타이 브레이커를 최대한 활용하십시오. 특히 대량로드가있는 경우 게재 신청서가 보장되지 않습니다.
Clockwork-Muse

8
@CortAmmon WikipediaRFC 4122 에 따르면 동의어입니다. Microsoft의 P. Leach는 RFC 제작자 중 하나였습니다. RFC가 만들어진 이후 두 가지가 동일하다고 생각합니다. RFC에서 : "GUUI (Globally Unique IDentifier)라고도하는 UUID (Universally Unique IDentifier)" 나는 GUID가 MS에 의해 생성되지 않았다는 것을 아는 것도 유용하다고 생각한다. 그들은 다른 곳에서 채택 된 기술의 새로운 이름을 만들었습니다.
JimmyJames

6
"SQL Server는 GUID 처리에 최적화되어 있으므로 쿼리 성능에 큰 영향을 미치지 않아야합니다." -1 충분히 최적화되지 않았습니다. 모든 PK가 guid이며 성능 저하의 주요 원인 중 하나 인 DB로 작업하고 있습니다.
Andy

7
"SQL Server는 GUID를 처리하는 데 최적화 되어 있으므로 쿼리 성능에 큰 영향을 미치지 않아야합니다. " 이 문장은 다른 데이터 유형이 최적화되지 않았다고 가정합니다. 예를 들어, 데이터베이스 서버는 간단한 int 값을 처리하기위한 최적화 기능이 있습니다. GUID / UUID는 4 바이트 int 값을 사용하는 것보다 훨씬 느립니다. 16 바이트는 4 바이트만큼 빠르지 않습니다. 특히 기본적으로 최대 4 ~ 8 바이트를 처리하는 시스템에서는 더욱 그렇습니다.
Andrew Henle

28

이것이 항상 독특합니까?

항상? 아니요, 항상 그런 것은 아닙니다. 유한 한 비트 시퀀스입니다.

GUID를 기본 키로 사용하여 수백만 행과 수백만 행을 포함하는 데이터베이스가 있다고 가정 해보십시오.

수백만 및 수백만, 아마 안전 할 것입니다. 백만 수백만 의 충돌 가능성이 커 집니다. 그러나 좋은 소식이 있습니다. 발생 시간에 따라 이미 디스크 공간이 부족한 것입니다.

그냥 할 수 있을까요?

당신은 할 수 있습니다; 완전히 좋은 생각은 아닙니다. 도메인 모델은 일반적으로 난수를 생성하지 않아야합니다. 그것들은 당신의 모델에 대한 입력이어야합니다.

그 외에도, 중복 메시지를받을 수있는 신뢰할 수없는 네트워크를 처리 할 때 결정적으로 생성 된 UUID는 중복 엔터티가 없도록 보호합니다. 그러나 각각에 새로운 난수를 할당하면 중복을 식별하기 위해 더 많은 작업이 필요합니다.

RFC 4122 의 이름 기반 UUID에 대한 설명 참조

GUID를 문자열로 모델링하는 것이 "정상"입니까, 아니면 모델 및 데이터베이스에서 GUID로 모델링해야합니까?

나는 그것이별로 중요하지 않다고 생각합니다. 대부분의 도메인 모델의 경우 식별자입니다 . 당신이 묻는 유일한 쿼리는 다른 식별자와 같은지 여부입니다. 도메인 모델은 일반적으로 식별자의 메모리 내 표시를 보지 않습니다.

도메인과 무관 한 설정에서 GUID를 "기본 유형"으로 사용할 수 있다면 GUID를 사용합니다. 이를 통해 지원 컨텍스트가 사용 가능한 적절한 최적화를 선택할 수 있습니다.

그러나 기억해야 할 것은 메모리와 스토리지 모두에서 식별자의 표현이 구현에서 내리는 결정이므로 코드의 풋 프린트가 그와 결합되도록 보장하는 단계를 수행해야한다는 것입니다. 결정은 작다 ( Parnas 1972 참조) .


20
"일어날 때까지 이미 디스크 공간이 부족합니다."+1
w0051977

2
" 결정적으로 생성 된 UUID " 라는 개념 이 필수적 이라고 생각합니다 (데이터 저장소 2 참조)
alk

실제로 다른 데이터를 기반으로 UUID / GUID를 다시 계산할 수 있다는 것은 특히 중복을 감지하는 데 큰 도움이됩니다. 한 번은 메시지를 저장하고 메시지를 처리 ​​파이프 라인을 통해 푸시하는 메시지 처리 시스템을 구축했습니다. 메시지의 해시를 만들어 시스템 전체에서 기본 키로 사용했습니다. 그것만으로도 확장해야 할 때 메시지를 식별하는 많은 문제를 해결했습니다.
Newtopian

백만 백만 = 2 ^ 40. 2 ^ 79 쌍의 충돌이 발생합니다. GUID에는 2 ^ 128 비트가 있으므로 기회는 2 ^ 49에 1입니다. 동일한 GUID를 두 개의 레코드에 재사용하거나 충돌이 없다고 잘못 생각하는 버그가있을 가능성이 훨씬 높습니다.
gnasher729

나는 역사적 질문을 다시하고있다. 수락하기 전에; 내 편집 내용을 볼 수 있습니까?
w0051977

11

GUID 또는 UUID 는 생성 방식으로 인해 고유 할 가능성이 높으며 중앙 기관과 통신하지 않고도 고유성을 보장하는 안전한 방법을 제공합니다.

기본 키로서 GUID의 장점 :

  • 클러스터의 여러 샤드간에 데이터를 복사 할 수 있으며 PK 충돌에 대해 걱정할 필요가 없습니다.
  • 레코드를 삽입하기 전에 기본 키를 알 수 있습니다.
  • 하위 레코드를 삽입하기위한 트랜잭션 논리를 단순화합니다.
  • 쉽게 추측 할 수 없습니다.

예제에서 제공 한 내용 :

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

삽입 시간 전에 GUID를 지정하면 연속적인 하위 레코드를 삽입 할 때 데이터베이스에 대한 왕복을 절약 할 수 있으며 동일한 트랜잭션에서이를 커밋 할 수 있습니다.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

기본 키로 GUID에 해로운 영향 :

  • 16 바이트가 크므로 인덱스와 외래 키가 추가되면 더 많은 공간을 소비합니다.
  • 그들은 본질적으로 난수이므로 잘 정렬되지 않습니다.
  • 인덱스 사용법은 매우, 매우 나쁩니다.
  • 잎이 많이 움직입니다.
  • 그들은 기억하기 어렵다.
  • 그들은 구두하기가 어렵습니다.
  • URL을 읽기 어렵게 만들 수 있습니다.

응용 프로그램에서 샤딩 또는 클러스터링이 필요하지 않은 경우 int 또는 bigint와 같이 더 작고 간단한 데이터 유형을 사용하는 것이 가장 좋습니다.

많은 데이터베이스에는 GUID로 인한 저장소 문제를 완화하려는 자체 내부 구현이 있으며 SQL Server는 newsequentialid 함수 를 사용하여 UUID를 정렬하여 인덱스를보다 효율적으로 사용할 수 있으며 일반적으로 더 나은 성능 특성을 갖습니다.

또한 응용 프로그램을 사용하는 테스터, 사용자 또는 개발자의 관점에서 GUID를 통해 ID를 사용하면 통신이 크게 향상됩니다. 전화로 GUID를 읽어야한다고 상상해보십시오.

결국 대규모 클러스터링 또는 난독 화 URL이 필요한 경우가 아니라면 자동 증가 ID를 사용하는 것이 더 실용적입니다.


1
고려해야 할 한 가지는 UUID 유형에 따라 생성 된 머신을 식별하는 데 사용될 수있는 정보가 포함되어 있다는 것입니다. 순수한 랜덤 변이체는 충분한 엔트로피없이 충돌 할 가능성이 더 높다. 이것은 URI에서 사용하기 전에 고려해야합니다.
JimmyJames

동의하지만 기본 키를 URL에 노출해서는 안됩니다. 외부 시스템에 데이터가 유출되지 않도록보다 적절한 방법을 사용해야합니다.
icirellik

1
시퀀스에 대한 잠금이 병목 현상이 발생하는 무거운 인서트 OLTP 데이터베이스가 하나 더 사용됩니다. 내 Oracle DBA 친구에 따르면, 이것이 들리는 것만 큼 드문 일은 아니며, 대규모 또는 클러스터가 필요하지 않습니다. • 결국, 장점과 단점을 무게 (일부 포스터처럼 UUID를 특정하지 않은 프로 / 죄수의 UUID의 혼동 장점 / 단점을하지 않는) 및 측정 .
mirabilos

1
newsequentialid를 사용하는 경우 ID를 얻으려면 db로 이동해야합니다 (identity int와 같은). 이점은 무엇입니까?
w0051977

1
@mirabilos 명확하게 말하면, 두려운 말을 할 때 행당 몇 분이 걸리는 인서트가 생겼습니다 . 그것은 괜찮아 시작했지만 수만 행이 된 후에는 옆으로 정말 빨랐습니다. 확실하지 않은 경우 수만 행은 매우 작은 테이블입니다.
JimmyJames

4

아니요, GUID를 기본 키로 사용하지 마십시오. 실제로 이러한 DB를 처리하고 있으며 성능 문제의 주요 원인 중 하나입니다.

여분의 12 바이트는 빠르게 합산됩니다. 대부분의 PK는 다른 테이블의 FK이며 테이블의 세 FK는 이제 모든 행에 48 바이트가 추가됩니다. 이는 테이블과 인덱스에 합산됩니다. 디스크 I / O에도 추가됩니다. 이러한 추가 12 바이트는 읽고 쓸 필요가 있습니다.

그리고 순차 guid를 사용하지 않고 PK가 클러스터링되는 경우 (기본적으로 발생 함) SQL은 때때로 전체 데이터 페이지를 이동하여 오른쪽 "스팟"으로 더 많이 압축해야합니다. 삽입, 업데이트 및 삭제가 많은 트랜잭션 데이터베이스의 경우 상황이 빠르게 줄어 듭니다.

동기화를 위해 일종의 고유 식별자가 필요한 경우 guid 열을 추가하십시오. PK로 만들지 마십시오.


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

이것이 GUID를 사용하는 가장 중요한 이유입니다.

퍼시스턴스 레이어에 대해 알고 있거나 통신하지 않고 코드없이 고유 한 ID를 생성 할 수 있다는 사실은 큰 이점입니다.

서버, PC 전화, 랩톱, 오프라인 장치 또는 전 세계 모든 서버에서 고유 한 모든 개체에 방금 생성 한 Person 개체를 배포 할 수 있습니다.

모든 종류의 데이터베이스 rdb 또는 no-sql, 파일에 붙여 넣거나 웹 서비스로 보내거나 원하지 않는 즉시 버릴 수 있습니다.

아니요 충돌하지 않습니다.

예. 인덱스를 조정해야 할 경우 인서트가 약간 느려질 수 있습니다.

예, 정수보다 큽니다.

  • 편집하다. 마무리하기 전에 발사했다.

많은 사람들이 자동 int int에 대해 강하게 느끼는 것을 알고 있으며 이는 DBA의 논란이되는 주제

그러나 나는 정말로 우수한 guid가 얼마나 강한지를 말할 수 없다. 모든 응용 프로그램에서 기본적으로 guid를 사용해야 합니다.

자동 int ints에는 많은 결함이 있습니다

  • SQL이없는 분산 DB를 사용합니다. 다른 모든 인스턴스와 대화하여 다음 숫자가 무엇인지 알 수 없습니다.

  • 메시지 큐 시스템을 사용합니다. DB에 도달하기 전에 ID가 필요합니다.

  • 저장하기 전에 여러 항목을 작성하고 편집하고 있습니다. db를 누르기 전에 각각 ID가 필요합니다.

  • 행을 삭제하고 다시 삽입하려고합니다. 자동 inc ID를 세지 않고 소진하십시오!

  • 올해에 몇 건의 주문을 모든 사용자에게 노출시키지 않으려 고

  • 익명 데이터를 프로덕션에서 테스트로 이동하고 관계를 그대로 유지하려고합니다. 그러나 기존 테스트 데이터를 모두 삭제하지는 마십시오.

  • 단일 테넌트 제품을 다중 테넌트 데이터베이스에 병합하려고하지만 모든 사람이 주문 56을 갖습니다.

  • 지속되지만 일시적인 개체를 만듭니다. (불완전한 주문) 다시, 더 이상 존재하지 않는 물건으로 모든 정수를 사용하지 마십시오.

이 목록은 끝이 없으며 항상 사람들에게 발생하는 실제 문제입니다. 약간 더 큰 FK 열로 인해 디스크 공간이 부족한 것과 달리

마지막으로 ints의 거대한 문제는 당신이 그들에게 부족 하다는 것입니다 ! 좋아, 이론적으로는 짐이있다. 그러나 실제로 사람들은 의미가없는 난수로 취급하지 않기 때문에 그렇게합니다. 그들은 같은 일을

  • 고객이 우리가 새로운 것이라고 생각하지 않기를 바랍니다. 10,000에서 시작

  • 나는 많은 양의 데이터를 가져와야 했으므로 시드를 1m까지 올렸습니다.

  • 카테고리의 데이터가 필요합니다. 모든 기간은 다음 백만에서 시작하므로 첫 숫자를 마법의 숫자로 사용할 수 있습니다.

  • 새로운 ID로 모든 데이터를 삭제하고 다시 가져 왔습니다. 예, 심지어 감사 로그.

  • 복합 키인이 숫자를이 다른 것의 id로 사용하십시오.


1
이 답변에는 실제로 잘못된 점은 없지만 실제 응용 프로그램에서 충돌이 발생하지 않더라도 이론적으로 가능하다는 단점을 분명히 밝힐 것입니다. (또는 아마도 45+ 이상의 엑사 바이트 데이터베이스가 생각보다 널리 퍼져 있습니다.) 비록 "가장 중요한 이유"라는 언어가 약간 강하다고 생각하지만 이것이 가장 유용한 것입니다.
BurnsBA

2
자동 int int가 guid보다 충돌 할 가능성이 더 높습니다
Ewan

4
"기본적으로 모든 응용 프로그램에서 guid를 사용해야합니다."에 대해 -1 의존합니다 ™. 그리고 다른 사람들이 보여 주듯이, GUID / UUID는 절대적으로 고유하지 않을 수도 있습니다.
맥스 버논

3
"그것은 달려있다"라는 대답은 쓸모가 없으며, int가 더 나은 이상한 응용 프로그램이있을 것입니다. 그러나 응용 프로그램이 그들 중 하나가 아닌 가능성이 있습니다. GUID는 가장 독창적 인 것입니다
Ewan

2
guid가 더 좋은 이상한 응용 프로그램이있을 것이라고 생각합니다. 고려해야 할 가장 중요한 것은 고유하지 않습니다. int의 "결함"은 엄청나게 과장되어 있으며 많은 guids의 단점을 고려하지 않습니다.
Andy

2

이 GUID는 응용 프로그램 수준에서 개체를 식별하는 데 사용됩니다. 또한 데이터베이스 레벨에서 기본 키로 저장됩니다.

바로 여기서 멈추고 다시 생각해야합니다.

데이터베이스 기본 키는 절대 비즈니스 의미가 없어야합니다. 정의상 의미가 없어야합니다.

따라서 GUID를 비즈니스 키로, 일반 기본 키 (일반적으로 long int)를 데이터베이스 기본 키로 추가하십시오. 고유성을 보장하기 위해 항상 GUID에 고유 색인을 배치 할 수 있습니다.

그것은 물론 데이터베이스 이론을 말하는 것이지만 좋은 습관입니다. 나는 기본 키가 비즈니스 의미를 갖는 데이터베이스를 다루었 고 (한 고객은 직원 번호, 고객 번호 등으로 일부 데이터베이스 리소스를 사용하여 데이터베이스 리소스를 절약하려고 생각했습니다) 항상 문제를 일으 킵니다.


1
정수 기본 키를 사용하여 응용 프로그램 계층에서 쿼리하는 것과 다른 점은 무엇입니까? 이때 응용 프로그램 계층에서 개체를 식별하는 데에도 사용됩니다. 응용 프로그램 계층에서 데이터베이스의 개체를 식별하는 방법이 필요합니다.
icirellik

@icirellik 기본 키는 부모 레코드와 자식 레코드 등을 연결하기 위해 데이터베이스에서 내부적으로 사용하기위한 것입니다. 응용 프로그램 논리에서 사용하기위한 것이 아니며 제품 번호 나 이름과 같은 비즈니스 ID를 사용합니다.
jwenting

2

데이터베이스 생성, 자동 증가 기본 키 (PK)를 항상 사용하십시오.

GUID / UUID 대신 자동 증가를 사용하는 이유는 무엇입니까?

  • GUID (UUID)는 고유하지 않기 때문에 키 충돌을 방지하지 않으며 여러 소스에서 생성되므로 고유하게 만들 방법이 없습니다.
  • GUID는 처리하는 데 많은 시간이 걸리는 매우 길고 정수가 아닌 PK 및 FK 열을 사용하여 이미 시간이 많이 걸리는 병합 프로세스를 크게 늘리므로 병합에 도움이되지 않습니다. 대부분의 PK에는 같은 크기의 키가 2 개 이상인 다른 테이블이 하나 이상 있어야합니다. PK는 자체 테이블이고 첫 번째 테이블에는 FK입니다. 모두 병합으로 해결해야합니다.

그렇다면 샤드, 클러스터 등을 처리하는 방법은 무엇입니까?

  • 자체 샤드 / 클러스터 / 데이터베이스 / 자체 자동 증분 키를 관리하는 모든 것을 식별하는 별도의 열로 구성된 다중 열 PK를 만듭니다. 예를 들어 ...

클러스터 된 테이블의 3 열 PK는 다음과 같습니다.

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

근데 ...?

  • 데이터베이스로 여러 번 이동-대부분의 응용 프로그램은 스레드 / 세션 / 한 번에 하나씩 만 작동하므로 데이터베이스에 삽입 될 때까지 생성되는 레코드를 고유하게 식별 할 필요가 없습니다. 응용 프로그램에 실제로이 기능이 필요한 경우 데이터베이스로 전송되지 않은 응용 프로그램 생성 임시 PK 를 사용하십시오 . 그런 다음 데이터베이스가 삽입 될 때 자체 자동 증가 PK를 행에 두십시오. 삽입은 임시 PK를 사용하고 업데이트 및 삭제는 데이터베이스가 할당 한 영구 PK를 사용합니다.

  • 성능-컴퓨터는 GUID (37)와 정수 (10)의 요소 당 가능한 값이 큰 경우 도메인이 훨씬 크기 때문에 다른 정수보다 훨씬 빠른 정수를 처리 할 수 ​​있습니다. GUID의 각 문자는 먼저 CPU에서 조작 할 수있는 숫자로 변환되어야합니다.

기본 키의 일반적인 오용 PK는 테이블에서 행을 완전히 고유하게 식별하기위한 목적이 하나뿐입니다. 다른 것은 너무나 흔한 오용입니다.

누락 된 레코드 감지

  • PK를 보면 누락 된 레코드를 감지 할 수 없습니다. 최소한 데이터 품질을 보장하기 위해 QA를 축복하십시오. 그러나 현대 데이터베이스 시스템의 키가 어떻게 할당되는지에 대한 이해가 부족하고 프로그래머는 자동 증분 PK에서 숫자가 누락되면 데이터가 누락된다는 잘못된 믿음을 갖게됩니다. 그것은 수행 하지 때문에 ...
  • 성능을 위해 데이터베이스 시스템은 스토리지의 실제 데이터베이스로의 트립을 최소화하기 위해 '시퀀스'(배치, 범위)로 숫자 블록을 할당합니다. 이러한 일련의 숫자의 크기는 종종 DBA의 통제하에 있지만 테이블별로 조정할 수는 없습니다.
  • 핵심 테이크 아웃은 ...이 시퀀스에서 사용되지 않은 숫자는 데이터베이스로 반환되지 않으므로 항상 PK 숫자에 공백이 있습니다.
  • 왜 사용하지 않는 번호가 필요할까요? 다양한 데이터베이스 유지 관리 작업으로 인해 시퀀스가 ​​중단 될 수 있습니다. 다시 시작, 테이블의 대량 다시로드, 일부 유형의 백업 복원 및 일부 다른 작업이 있습니다.

정렬

  • PK를 기준으로 정렬하면 오류가 발생하기 쉽습니다. 대부분의 사람들은 행을 작성된 순서대로 나열하고 시계 시간에 해당하는 행을 나열한다고 생각하기 때문입니다. 대부분은 아니지만 꼭 필요한 것은 아닙니다.
  • 데이터베이스 엔진은 최대한의 성능을 발휘하도록 최적화되었으며, 짧고 간단한 트랜잭션을 삽입하기 위해 오래 실행되는 복잡한 트랜잭션의 결과 삽입을 지연시킬 수 있습니다.

유일한 고유 컬럼이 데이터베이스 작성 자동 증가 기본 키가되도록 테이블 스키마에 대해 어떻게 생각하십니까? 특히 외래 키가 없지만 기본 키가 여러 관련 테이블의 외래 키인 테이블의 경우?
RibaldEddie

나는 그 라인을 따라 답변에 훨씬 더 많은 것을 추가했습니다. 내가 응답중인 Android SE 앱으로 인해 원래 답변이 불완전했습니다. 앱의 주요 재 작성은 개발 중이라고 생각합니다.
DocSalvager

따라서 귀하의보기에서 자동 증가 기본 키에 대해 동일한 저장 횟수를 가진 테이블이 테이블에 포함되어 있으면됩니까?
RibaldEddie

@RibaldEddie-DB가 허용하는 한 ... 절대적으로. 삭제가 쉽습니다. 시나리오가 발생하면 소프트웨어에서 수정 된 버그로 간주 한 다음 두 행을 삭제하십시오. 그러나 가장 일반적인 경우는 약간 다른 데이터를 가진 동일한 일에 대한 두 개의 레코드이므로 병합해야합니다. 한 레코드에서 열이 비어 있고 다른 레코드에 값이 있으면 선택이 분명하고 자동화 될 수 있습니다. datetimestamp는 종종 자동 병합을 중재하는 데 사용될 수 있습니다. 일부 복제본에서는 비즈니스 규칙에 따라 병합을 완료하고 확인해야합니다.
DocSalvager

1

다른 방법과 마찬가지로이 작업을 수행하면 장단점이 있습니다.

좋은 점 :

  1. 키의 길이는 항상 같습니다 (매우 큰 데이터베이스에는 매우 큰 키가있을 수 있음)

  2. 독창성은 거의 보장됩니다-별도의 시스템에서 생성하거나 데이터베이스에서 마지막 ID를 읽지 않은 경우에도

나쁜 것 :

  1. 위에서 언급했듯이 더 큰 인덱스와 데이터 저장소.

  2. ID로 주문할 수 없으며 다른 것으로 주문해야합니다. 인덱스가 많을수록 효율성이 떨어질 수 있습니다.

  3. 그것들은 사람이 읽을 수 없습니다. 정수는 일반적으로 사람들을 구문 분석, 기억 및 입력하기가 더 쉽습니다. 여러 개의 조인 된 테이블에서 WHERE 절의 GUID를 ID로 사용하면 머리가 녹을 수 있습니다.

모든 경우와 같이 적절하게 사용하고 독단적이지 마십시오. 많은 경우 자동 증가 정수가 더 좋고 때로는 GUID가 좋습니다.


0

예, GUID를 기본 키로 사용할 수 있습니다. 단점은 지수의 크기와 빠른 조각화입니다.

데이터베이스 (예 : 클러스터)에서 고유성이 필요하지 않으면 정수가 선호됩니다.


GUID 생성기는 동일한 GUID를 두 번 이상 생성 할 수 있으며 결함이 있습니다. 그것들의 유무는 세분성, 주로 클럭 틱 사이의 간격에 달려 있습니다. 예를 들어 클럭 기반 생성기는 100ms마다 똑딱 거리면 해당 시스템의 100ms 내에 요청 된 2 개의 GUID가 동일합니다. 대부분의 GUID 생성기는 IP 주소 및 / 또는 MAC 주소와 타임 스탬프에서 완전히 작동하지 않습니다.
jwenting

0

여기 에이 문제에 대한 해결책이 있습니다. 솔루션은 GUID와 int 값 사이의 중간 지점이며 두 가지를 모두 최대한 활용합니다.

이 클래스는 의사 GUID 와 유사한 의사 난수 (시간이 지남에 따라 증가) Id 값을 생성합니다 .

가장 큰 장점은 서버에서 생성 된 자동 증분 값 (왕복이 필요함)을 거의 중복하지 않고 클라이언트에서 Id 값을 생성 할 수 있다는 것입니다.

생성 된 값은 GUID에 16이 아닌 8 바이트 만 사용하며 하나의 특정 데이터베이스 정렬 순서 (예 : GUID 용 Sql 서버)에 의존하지 않습니다 . 부호없는 전체 장거리를 사용하도록 값을 확장 할 수 있지만 부호있는 정수 유형 만있는 데이터베이스 또는 기타 데이터 저장소에 문제가 발생할 수 있습니다.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.