나는 일반적으로 데이터베이스에서 자동 증분 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 편집*
나는 가이드가 나의 요구조건보다 더 적합하다고 믿게 되었다. 나는 요즘 CQRS를 더 많이 사용하고 있고 GUID가 더 잘 맞습니다.
일부 개발자는 GUID를 도메인 모델의 문자열로 모델링한다(예: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering).도메인/AggregatesModel/BuyerAggregate/Buyer.cs - 이 경우: IdentityGuid는 문자열로 모델링된 GUID입니다. 여기에 명시된 것 외에 이것을 해야 할 이유가 있나요: https://softwareengineering.stackexchange.com/questions/239220/use-a-custom-value-object-or-a-guid-as-an-entity-identifier-in-a-distributed-sys. GUID를 문자열로 모델링하는 것이 "정상"입니까, 아니면 모델과 데이터베이스에서 GUID로 모델링해야 합니까?
GUID는 정의상 " 인용구이다.전역 고유 IDentifier". 자바어로 UUIDs "라고 불리는 유사하지만 약간 다른 개념이 있다.범용 고유 IDentifier". 그 이름들은 실용적으로 사용될 수 있다.
GUID는 마이크로소프트가 데이터베이스 클러스터링을 구상하는 방법의 중심이며, 때때로 연결된 소스의 데이터를 통합해야 하는 경우 데이터 충돌을 방지하는 데 큰 도움이 됩니다.
일부 친GUID 사실:
GUID를 사용한 일부 못생김
GUID는 인덱스를 더 크게 만들어 열을 인덱싱하는 데 드는 디스크 공간 비용을 높입니다. 임의 GUID는 인덱스를 조각냅니다.
다른 네트워크의 데이터를 동기화하지 않을 경우 GUID는 가치보다 더 많은 오버헤드를 전달할 수 있습니다.
연결된 클라이언트에서 데이터를 수집해야 하는 경우 클라이언트의 시퀀스 범위 설정에 의존하는 것보다 주요 충돌을 방지하는 데 훨씬 더 강력할 수 있습니다.
이것이 항상 유일할까요?
항상?항상 그런 것은 아니다; 그것은 유한한 비트들의 순서이다.
GUID를 기본 키로 하는 수백만 개의 행을 포함하는 데이터베이스가 있다고 가정해 보십시오.
수백만이요, 당신은 아마 안전할 거예요 백만 번이면 충돌 가능성이 커진다. 하지만 좋은 소식은, 그 일이 일어날 때쯤이면 이미 디스크 공간이 부족하다는 것이다.
그냥 이렇게 하면 되나요?
할 수 있다; 그것은 완전히 좋은 생각은 아니다. 도메인 모델은 일반적으로 난수를 생성해서는 안 되며 모델에 대한 입력이어야 합니다.
또한 중복 메시지가 표시될 수 있는 신뢰할 수 없는 네트워크를 처리할 때 결정적으로 생성된 UUID가 중복 엔티티를 갖지 못하도록 보호합니다. 그러나 각각에 새로운 무작위 번호를 할당하면 중복을 식별하기 위해 더 많은 작업을 수행해야 합니다.
RFC 4122에서 이름 기반 UUID에 대한 설명을 참조하십시오.
GUID를 문자열로 모델링하는 것이 "정상"인지 아니면 모델 및 데이터베이스에서 GUID로 모델링해야 하는지 여부
저는 그것이 그다지 중요하다고 생각하지 않습니다. 도메인 모델의 대부분은 _identifier_이며, 다른 식별자와 동일한지 여부를 묻는 유일한 쿼리입니다. 당신의 도메인 모델 win't는 보통 식별자의 메모리 내 표현을 보고 있다.
도메인 불가지론 설정에서 GUID가 "primative type"으로 사용 가능한 경우, 이를 사용합니다. 이를 통해 지원 컨텍스트에서 사용 가능한 적절한 최적화를 선택할 수 있습니다.
그러나 메모리와 스토리지 모두에서 식별자의 표현은 구현에서 내리는 결정이므로 해당 결정과 연결된 코드의 풋프린트가 작아야 합니다(Parnas 1972. 참조).
GUID 또는 UUID은 생성 방법 때문에 고유일 가능성이 매우 높으며, 중앙 기관과 통신할 필요 없이 고유성을 보장하는 안전한 방법을 제공한다.
기본 키로서 GUID의 이점:
제공한 예에서는 다음을 수행합니다.
Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);
삽입 시간 전에 GUID를 지정하면 연속 하위 레코드를 삽입할 때 데이터베이스에 대한 왕복 이동을 저장하고 동일한 트랜잭션에서 해당 하위 레코드를 커밋할 수 있습니다.
Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);
기본 키로 GUID에 대한 제한:
애플리케이션에 샤딩이나 클러스터링이 필요하지 않다면 int 또는 bigint와 같은 더 작고 단순한 데이터 유형을 사용하는 것이 가장 좋습니다.
많은 데이터베이스에는 GUID's로 인한 스토리지 문제를 완화하기 위한 자체 내부 구현체가 있으며 SQL Server에는 UUID's의 순서를 지정하는 기능newsequenticid이 있으며 일반적으로 더 나은 성능 특성을 가지고 있습니다.
또한 애플리케이션으로 작업하는 테스터, 사용자 또는 개발자의 관점에서 GUID를 통해 ID를 사용하면 통신이 크게 개선됩니다. 전화로 GUID를 읽어야 한다고 상상해 보십시오.
결국 대규모 클러스터링이나 URL 난독화가 요구되지 않는 한 자동증가 ID를 고수하는 것이 더 실용적이다.
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);
이것은 훨씬 가장 중요한 이유에 대한 Guid 를 사용.
는 사실을 만들 수 있습니다 고유 id 없이 당신의 코드에 대해 알고나와 통신을 당신의 지층은 아주 유용하다고 생각합니다.
확신할 수 있는 사람 당신은 단지 생성되는 서버에서,pc,전화,휴대용 퍼스널 컴퓨터,장치 또는 오프라인 어떤 이유에서 모든 서버는 세계의 모든 그러나 분산됩니다.
스틱 수 있습니다 그것이 어떤 종류의 데이터베이스 rdb 또는 sql 파일로 보내는 모든 webservice 거나 그것을 던져으로 즉시 uneeded
없는 충돌이 발생합니다.
예를 삽입할 수 있다 약간 느린으로 지수해야 할 수도 있습 바이올린다.
네,그것은 보다 더 큰 int.
-집니다. 했을 촬영을 마무리하기 전에이다.
많은 사람들을 강하게 느끼는 자동차에 대한 inc.의 수 및 이 논란 주제로 Dba
그러나 나는 정말 못 국가 충분히 강하게 어떻게 우수한 guid 니다. 해야 합 guid 를 사용하여default에서 어플리케이션에 적합합니다.
자동 inc.의 수가 많은 많은 결함
-를 사용하 No-Sql 분산 db. 당신은 단순히 이야기를 다른 모든 경우 무엇을 찾기 위해 다음 수 있다.
-사용하는 메시지 큐 시스템입니다. 일요 Id 기 전에 그들은 db
-당신은 당신을 만드는 여러 항목과 편집 저장하기 전에. 각각의 요구 id 기 전에 당신은 히트 db
-당신은 당신을 삭제하고 다시 삽입하는 행이 있습니다. 위't count 자동 inc id 및 실행!
-당신이 원하는 노출하지 않는 얼마나 많은 주문 당신이 이해하는 모든 사용자
-이동하려는 익명으로 데이터를 생산에서 테스트하고 유지하는 관계를 그대로 유지됩니다. 하지만 삭제하지 않는 기존의 모든 테스트 데이터입니다.
-을 병합하려는 귀하의 단일 테넌트 제품으로 멀티 tenanted 데이터베이스 그러나 모든 사람이 주문 56.
-개체를 만드는 지속되지만 임시이다. (불완전 주문)에 다시 사용하지 말라 모든 수는 물건을 더 이상 존재하지 않습니다.
이 목록은 무한하고 그들은 모두 진짜 문제는 발생하는 모든 사람들의 시간입니다. 과는 달리 실행하기 때문에 디스크 공간의는 약간 큰 FK cols
마지막으로 거대한 문제점으로 수입당신의 그들을!!! 인 이론에서 당신은,부하가 있다. 그러나 실제로는 당신 때문에 사람들이't 치료를 그들과 같은 임의의 숫자를 가진다. 그들이 같은 것들
-oh I don't 원하는 고객 생각하고 우리는 새로운 기능입니다. 에서 시작 10,000
-을 가져오는 부하의 데이터 그래서 나는 그냥을 올렸으며 씨앗을 1m 그래서 우리는 무엇을 알고 가져온
우리는 카테고리's 의 데이터입니다. 모든 기간에서 시작 다 만 그래서 우리가 사용할 수 있는 첫 번째 자리로 마법수
-I 삭제하고 다시 가져온 모든 데이터와 함께 다시 새로운 id 입니다. 네도 감사를 기록합니다.
-이 숫자를 사용하여,복합 열쇠로,id 의 다른 일
나는'd 말,don't Guid 를 사용하으로 기본 키가 있습니다. 나는'm 실제로 다루는 같은 DB 이제,그리고 그들은 하나의 기본 원인의 성능 문제가 발생합니다.
추가 12 바이트까지 추가 빠르게 기억하는,가장 PKs 될 것입니다 FKs 에서 다른 테이블,및 단지 세 FKs 테이블에서 당신은 지금 48 바이트에 대한 추가 모든 행이 있습니다. 는 추가에서 테이블에 인덱스입니다. 그것도 추가에서 디스크 I/O. 그 12 바이트를 읽어야 하고 기록됩니다.
당'다시 사용하지 않을 순차적 guid 및 Pk 클러스터(는 무슨 일이 기본적으로),SQL 시간을 이동 전체 페이지의 데이터 주변을 짜세상으로 오른"spot."에 대한 높은 트랜잭션 데이터베이스의 많은 삽입 업데이트 및 삭제,일렁 빠르다.
해야 하는 경우 몇 가지 종류의 독특한 식별자를 위한 동기화하거나 무언가를 추가 guid 를 열에 있습니다. 지't 을 제공합니다.
I 깨닫는 이러한 Guid 를 식별하는 데 사용됩니다에 있는 객체는 응용 프로그램 수준입니다. 그들은 또한 저장된 기본 키로 데이터베이스에서 수준이다.
는's,중지해야 옳은가 있고,재고.
귀하의 데이터베이스의 기본 키지 않아야 하는 사업을 의미합니다. 그것이 있어야에 의해 정의합니다.
그래서 추가 GUID 으로 귀하의 비즈니스 키를 정상적인 기본 키(일반적으로 긴 int)데이터베이스의 기본 핵심이다. 어 있습니다 독특한 색인에 GUID 고유성을 보장하기 위해.
는's 말하는 데이터베이스의 이론은 물론이지만,그's 이 좋습니다. 나는've 처리 데이터베이스의 기본 키가 있었 비즈니스 의미(하나의 고객을 생각했을 저장하는 일부를 데이터베이스에 자원을 사용하여 그들로 직원 번호,고객의 숫자,등등. etc. 예를 들어)그리고 항상 리드하는 문제입니다.
는 항상 사용하여 데이터베이스를 생성,자동증가 기본 키(Pk).
사용하는 이유는 자동증가 대신 GUID/UUID?
-GUID(UUID)들을 방지하지 않 키 충돌하지 않기 때문에 독특한 방법이 없다는 것입하여 고유하게 만들기 때문에서 생성되어 수많은 소스입니다. -Guid 도움이 되지 않으로 병합으로 그들이 크게 증가를 이미 시간이 많이 걸리 병합 프로세스와 함께 매우 긴,non-정수 PK 및 FK 는 열을 거래하는 시간의 과정입니다. 을 기억하는 대부분의 PKs,있을 것입니다 적어도 1 개의 기타 테이블에 적어도 2 개의 열쇠의 동일한 크기:it's own PK 및 FK 첫 테이블. 모두 해결해야에서 병합합니다.
하지만 어떻게 다음을 처리할 파편,클러스터,etc.?
-Create 멀티 열 PKs 별도의 열을 식별 각 분/클러스터 데이터베이스//무엇이든 그것 관리's 자신의 자동차-증가하는 키입니다. 예를 들어...
3 열 PK 클러스터 테이블 수 있습...
DB | SH | KEY |
----|----|---------|
01 | 01 | 1234567 |
그러나요?
-여의 데이터베이스--대부분의 응용 프로그램을 필요가 없을 고유하게 식별할 기록이 생성되지's 로 삽입된 이후 데이터베이스는 스레드/session/아이만 작동에 있습니다. 는 경우 응용 프로그램 정말 필요한 이 능력,응용 프로그램을 사용하여 생성된 임시 PK하지 않는 데이터베이스로 전송. 자 데이터베이스에 넣어's 자신의 자동차 증가 PK 에 행할 때's 삽입됩니다. 삽입이 사용됩 임시 PK 동안 업데이트 및 삭제를 사용하여 영구 PK 할당하는 데이터베이스입니다.
-성능을 컴퓨터 처리할 수 있는 간단한 정수보다 훨씬 빨리 다른 것 때문에 크게 큰 도메인 가능한 경우 값당 요소에 GUID(37)대 integer(10). 기도는 각각의 캐릭터에 GUID 으로 전환되어야 합 숫자를 조작하여 CPU.
일반적인 오용의 기본 키 PKs 는 단 하나의 목적을 절대적으로 고유하게 식별할 줄 표입니다. 아무거나 다른 모든 너무 일반적인 사용할 수도 있다.
을 검출하 기록
-레코드를 누락할 수 없습 감지하여 PKs. 축복 QA 상을 보장하기 위해 시도하는 데이터 품질을 보장할 수 있습니다. 그러나,그들과 프로그래머는's 의 부족의 이해를 어떻게 키에 현대적인 데이터베이스 시스템은 종종 할당들을 이끌 misbelief 는 누락에서 번호를 자동증가 PK 수단 데이터가 누락되었습니다. 그것은 않지 않음**기 때문에... -에 대한 성능,데이터베이스 시스템의 블록 할당 번호를'순'(일괄 범위)최소화하 여 실제 데이터베이스에 저장합니다. 의 크기는 이러한 시퀀스의 숫자가 종종의 통제하에 DBA 그러나 튜닝할 수 없습 테이블 기준. -키 테이크 아웃...사용하지 않는 숫자에서 이러한 순은 반환되지 않는 데이터베이스가있다 그래서 항상 격차에 PK 숫자입니다. -이유가 있을 사용하지 않는 번호를 물어? 기 때문에 다양한 데이터베이스 유지보수 작업을 수 있습생 시퀀스를 버려서는 안됩니다. 이 같은 것들이 다시 시작,대량 재충전의 테이블,일부 형태의 복원에서 백업하고 일부는 다른 작업입니다.
분류
-정렬하여 PK 은 아주 오류가 발생하기 쉬운 이후 대부분의 사람들이 생각하는 것입니다 그것은 목록에서 행하기 위해 만들어진 과 그에 해당하는 시간입니다. 대부분이지만,necessarilly. -데이터베이스 엔진에는 최적화되어 최대한의 성과와 의미할 수 있습을 연기를 삽입의 결과의 오래 실행되는 복잡한 트랜잭션에 삽입하기 위하여 짧은 간단한 사람,"out-of-turn"그래서를 말한다.
처럼,아무것도 있다는 장점과 단점을 이렇게:
좋은:
귀하의 키는 항상 같은 길이(형 데이터베이스의 경우 매우 큰 키)
독창성은 꽤 많상도할 때'다시 생성하는 별도의 시스템,및/또는't 읽는 마지막 데이터베이스에서 ID
나쁜:
으로 언급 위 큰 인덱스 및 데이터 저장소입니다.
할 수 있't order by ID,당신은 당신을 위해 뭔가. 더 많은 인덱스,아마 미만 효율적입니다.
그들이'다시 적은 인간이 읽을 수 있습니다. 정수는 일반적으로 쉽게 분석하고 기억하고 유형에 대한 사람들이다. Guid 를 사용으로 Id 에서는 절이 여러 가입 테이블을 만들 수 있다는 당신의 머리를 녹입니다.
처럼 모든 것을 사용하여,적절한 경우,don't 교리에 많은 상황 자동증가 정수는 더 나은,때때로 Guid 니다.
여기's 을 내고 이 문제에 대한 솔루션입니다 중간에 집 사 GUID 및 int 값을 가지고,모두의.
클래스를 생성하는 의사 무작위(하지만 시간이 흐르면서 증가합니다)Id 값과 유사한빗 가이드.
키를 이용할 수 있는 Id 값을 생성에서 클라이언트,사용하기보다는 자동차 증가 값을 서버에서 생성됩니다(필요한 왕복)거의 제로 위험의 중복되는 값입니다.
생성된 값을 사용 8 바이트가 아닌 16a GUID 를 의존하지 않는 한 특정 데이터베이스를 정렬 순서(예를 들어에 대한 Sql Server Guid). 값을 확인할 수 있게 되었습니다면 전체를 사용하는 사용되지만,이 문제가 발생이 어떤 데이터베이스나 다른 데이터 저장소만을 체결했다 정수 유형입니다.
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;
}
}