문맥
분산 응용 프로그램의 데이터를 저장할 데이터베이스 (PostgreSQL 9.6에서)를 설계하고 있습니다. 응용 프로그램의 분산 특성으로 인해 SERIAL
잠재적 인 경쟁 조건으로 인해 자동 증가 정수 ( )를 기본 키로 사용할 수 없습니다 .
자연스러운 솔루션은 UUID 또는 전역 고유 식별자를 사용하는 것입니다. 포스트 그레스가 함께 제공되는 내장 UUID
타입 안성맞춤이다.
UUID와 관련된 문제는 디버깅과 관련이 있습니다. 사람에게 친숙하지 않은 문자열입니다. 식별자 ff53e96d-5fd7-4450-bc99-111b91875ec5
는 나에게 아무것도 말하지 않지만 ACC-f8kJd9xKCd
, 고유하지는 않지만 ACC
객체를 다루고 있다고 알려줍니다 .
프로그래밍 관점에서 볼 때 여러 다른 객체와 관련된 응용 프로그램 쿼리를 디버깅하는 것이 일반적입니다. 프로그래머 ACC
가 ORD
(순서) 테이블 에서 (계정) 오브젝트를 잘못 검색한다고 가정하십시오 . 사람이 읽을 수있는 식별자를 사용하여 프로그래머는 문제를 즉시 식별하고 UUID를 사용하면 무엇이 잘못되었는지 파악하는 데 시간을 소비합니다.
UUID의 "보장 된"고유성이 필요하지 않습니다. 내가 할 충돌없이 키를 생성하기위한 약간의 공간이 필요하지만, UUID는 과잉이다. 또한 최악의 시나리오에서는 충돌이 발생한 경우 (데이터베이스가이를 거부하고 응용 프로그램을 복구 할 수있는 경우) 끝이 아닙니다. 따라서 작지만 인간 친화적 인 식별자가 내 사용 사례에 이상적인 솔루션이라고 생각합니다.
응용 프로그램 객체 식별
내가 찾은 식별자의 형식은 다음과 같습니다 {domain}-{string}
. 여기서 {domain}
개체 도메인 (계정, 주문, 제품)으로 바뀌고 {string}
무작위로 생성 된 문자열입니다. 경우에 {sub-domain}
따라 임의 문자열 앞에 a를 삽입하는 것이 좋습니다. 고유성을 보장하기위한 길이 {domain}
와 {string}
목적을 무시합시다 .
인덱싱 / 쿼리 성능에 도움이되는 경우 형식의 크기가 고정 될 수 있습니다.
문제
그것을 아는 것은:
- 와 같은 형식의 기본 키를 갖고 싶습니다
ACC-f8kJd9xKCd
. - 이 기본 키는 여러 테이블의 일부입니다.
- 이 모든 키는 6NF 데이터베이스의 여러 조인 / 관계에서 사용됩니다.
- 대부분의 테이블은 중간에서 큰 크기 (평균 ~ 1M 행, 최대 ~ 100M 행)를 갖습니다.
성능과 관련하여이 키를 저장하는 가장 좋은 방법은 무엇입니까?
다음은 가능한 네 가지 솔루션이지만 데이터베이스에 대한 경험이 거의 없기 때문에 어느 것이 가장 좋은지 잘 모르겠습니다.
고려되는 솔루션
1. 문자열 ( VARCHAR
) 로 저장
(포스트 그레스 사이에 차이가 없습니다 CHAR(n)
와 VARCHAR(n)
내가 무시 해요, 그래서 CHAR
).
몇 가지 조사를 거친 후 VARCHAR
, 특히 조인 작업에서을 사용한 문자열 비교 가을 사용하는 것보다 느리다 는 것을 알았습니다 INTEGER
. 이것은 의미가 있지만,이 규모에서 걱정해야 할 것이 있습니까?
2. 바이너리 ( bytea
) 로 저장
Postgres와 달리 MySQL에는 기본 UUID
유형 이 없습니다 . BINARY
36 바이트가 아닌 16 바이트 필드를 사용하여 UUID를 저장하는 방법을 설명하는 여러 게시물이 있습니다 VARCHAR
. 이 게시물은 키를 바이너리 ( bytea
Postgres) 로 저장한다는 아이디어를주었습니다 .
이것은 크기를 절약하지만 성능에 더 관심이 있습니다. 바이너리 또는 문자열 비교가 더 빠른 설명을 찾는 것은 운이 없었습니다. 이진 비교가 더 빠르다고 생각합니다. 이러한 경우 다음 bytea
아마도 더 나은보다 VARCHAR
프로그래머가 지금 인 코드 / 디코드에 데이터마다에도 불구하고.
제가 틀릴 수도 있지만, 나는 모두를 생각 bytea
하고 VARCHAR
(문자 또는 문자) 바이트 (평등) 바이트를 비교합니다. 이 단계별 비교를 "건너 뛰고" "전체"를 간단히 비교할 수있는 방법이 있습니까? (나는 그렇게 생각하지 않지만 검사 비용은 들지 않습니다).
나는 저장하는 bytea
것이 최선의 해결책 이라고 생각 하지만, 내가 무시하고있는 다른 대안이 있는지 궁금합니다. 또한 솔루션 1에서 표현한 것과 동일한 우려가 적용됩니다. 비교에 대한 오버 헤드가 충분히 걱정해야합니까?
"크리에이티브"솔루션
나는 작동 할 수있는 두 가지 매우 "창의적 인"솔루션을 생각해 냈습니다. 어느 정도인지 확실하지 않습니다 (즉, 테이블에서 수천 행 이상으로 확장하는 데 문제가있는 경우).
3. UUID
"라벨"이 붙어있는 그대로 보관하십시오.
UUID를 사용하지 않는 주된 이유는 프로그래머가 응용 프로그램을 더 잘 디버깅 할 수 있기 때문입니다. 그러나 두 가지를 모두 사용할 수 있다면 어떻게 될까요? 데이터베이스는 모든 키를 UUID
s로만 저장 하지만 쿼리가 시작되기 전 / 후에 개체를 래핑합니다.
예를 들어, 프로그래머는을 요청 ACC-{UUID}
하고 데이터베이스는 ACC-
부품을 무시 하고 결과를 가져 와서 모두로 반환합니다 {domain}-{UUID}
.
아마도 저장 프로시 저나 함수가있는 일부 해커에서 가능할 수도 있지만 몇 가지 질문이 떠 오릅니다.
- 이것이 (각 쿼리에서 도메인 제거 / 추가) 상당한 오버 헤드입니까?
- 이것이 가능합니까?
전에는 저장 프로시 저나 함수를 사용한 적이 없으므로 이것이 가능한지 확실하지 않습니다. 누군가 빛을 비출 수 있습니까? 프로그래머와 저장된 데이터 사이에 투명 레이어를 추가 할 수 있다면 완벽한 솔루션 인 것 같습니다.
4. (가장 좋아하는) IPv6으로 저장 cidr
네, 잘 읽어보세요. IPv6 주소 형식이 내 문제를 완벽하게 해결하는 것으로 나타났습니다 .
- 처음 몇 옥텟에서 도메인과 하위 도메인을 추가하고 나머지를 임의 문자열로 사용할 수 있습니다.
- 충돌 확률은 OK입니다. (하지만 2 ^ 128을 사용하지는 않지만 여전히 괜찮습니다.)
- 평등 비교는 (희망적으로) 최적화되어 있으므로 간단히 사용하는 것보다 더 나은 성능을 얻을 수 있습니다
bytea
. - 실제로
contains
도메인과 해당 계층 구조가 어떻게 표현되는지에 따라 와 같은 흥미로운 비교를 수행 할 수 있습니다 .
예를 들어 0000
도메인 "제품"을 나타내는 코드 를 사용한다고 가정 합니다. 키 0000:0db8:85a3:0000:0000:8a2e:0370:7334
는 제품을 나타냅니다 0db8:85a3:0000:0000:8a2e:0370:7334
.
여기서 주요 질문은 다음과 같습니다.에 비해 데이터 유형 bytea
사용에 대한 주요 장점 또는 단점이 cidr
있습니까?
varchar
많은 다른 문제 중 하나입니다. 나는 pg의 도메인에 대해 몰랐다. 주어진 쿼리가 올바른 객체를 사용하고 있는지 확인하는 데 도메인이 사용되는 것을 보았지만 여전히 정수가 아닌 인덱스를 사용해야합니다. serial
하나의 잠금 단계없이 여기에 "안전한"사용 방법이 있는지 확실하지 않습니다 .
varchar
. 그에게하고 고려 FK
integer
유형과 이에 대한 조회 테이블을 추가합니다. 이렇게하면 사람이 쉽게 읽을 수 있고 PK
삽입 / 업데이트 이상 (존재하지 않는 도메인을 두는 것)으로부터 컴포지트 를 보호 할 수 있습니다 .
ACC-f8kJd9xKCd
. ← 이것은 좋은 오래된 복합 PRIMARY KEY의 일인 것 같습니다 .