MD5 필드에 가장 적합한 데이터 유형은 무엇입니까?


35

우리는 읽기 / 무거운 것으로 알려진 시스템을 설계하고 있습니다 (분당 수만 번 읽기).

  • names일종의 중앙 레지스트리 역할을 하는 테이블 이 있습니다. 각 행에는 해당 text필드 의 MD5 해시 인 representation고유 한 필드 keyrepresentation있습니다. 1 이 표는 현재 수천만 건의 레코드를 보유하고 있으며 애플리케이션 수명 기간 동안 수십억 건으로 증가 할 것으로 예상됩니다.
  • 테이블을 참조하는 수십 개의 다른 테이블 (매우 다양한 스키마 및 레코드 수)이 names있습니다. 이러한 테이블 중 하나에 지정된 레코드 name_key는 기능적으로 names테이블 의 외래 키인을 갖습니다.

1 : 예상대로이 테이블의 레코드는 한 번 쓴 후에는 변경할 수 없습니다.

테이블 이외의 지정된 테이블 names에 대해 가장 일반적인 쿼리는 다음 패턴을 따릅니다.

SELECT list, of, fields 
FROM table 
WHERE name_key IN (md5a, md5b, md5c...);

읽기 성능을 최적화하고 싶습니다. 나는 첫 번째로 지수의 크기를 최소화해야한다고 생각합니다.

질문 : 및 열에
대한 최적의 데이터 유형은 무엇입니까 ? 이상 사용할 이유가 있습니까? 또는 ?keyname_key
hex(32)bit(128)BTREEGIN

답변:


41

데이터 유형 uuid은 작업에 완벽하게 적합합니다. varchar또는 text표현을 위해 RAM에서 37 바이트가 아닌 16 바이트 만 차지합니다 . (또는 디스크의 33 바이트이지만 홀수는 40 바이트를 효과적으로 만들기 위해 패딩이 필요합니다 .) 그리고이 uuid유형에는 몇 가지 장점이 있습니다.

예:

SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash

세부 사항 및 추가 설명 :

md5의 암호화 구성 요소가 필요하지 않으면 다른 (저렴한) 해싱 함수를 고려할 수 있지만 사용 사례 (대부분 읽기 전용)에 md5를 사용합니다.

경고 단어 : 귀하의 경우 ( immutable once written) 기능에 의존하는 (의사-자연) PK 가 좋습니다. 그러나 업데이트 가 가능한 고통도 마찬가지입니다 text. 오타 수정 : PK 및 모든 종속 인덱스, FK 열 dozens of other tables및 기타 참조도 변경해야합니다. 테이블 및 인덱스 팽창, 잠금 문제, 느린 업데이트, 손실 된 참조 ...

경우 text정상 작동 변경할 수 있습니다하는 대리의 PK가 더 나은 선택이 될 것입니다. 나는 bigserial열 (범위 -9223372036854775808 to +9223372036854775807- 구 quintillion 이백 이십 삼십 삼 삼백 삼십 삼십 삼십 육십 억 무엇인가 )에 대해 다른 값을 제안한다 billions of rows. 에서 좋은 아이디어가 될 수 있는 경우 : 8 대신 16 ! FK 컬럼과 인덱스 수십 바이트). 아니면 랜덤 UUID 에 대한 훨씬 더 큰 카디널리티 또는 분산 시스템. 당신은 항상 상점은 MD5 (로 말했다 수 있습니다 uuid) 추가로 신속하게 원래의 텍스트에서 기본 테이블에서 행을 찾을 수 있습니다. 관련 :

귀하의 쿼리에 관해서 :


@Daniel의 주석 을 처리하려면 : 하이픈이없는 표현을 선호하는 경우 표시 할 하이픈을 제거하십시오.

SELECT replace('90b7525e-84f6-4850-c2ef-b407fae3f271', '-', '')

그러나 나는 귀찮게하지 않을 것입니다. 기본 표현은 괜찮습니다. 그리고 문제는 실제로 여기에 대한 표현이 아닙니다.

다른 당사자가 다른 접근 방식을 사용해야하고 하이픈이없는 문자열을 믹스에 넣으면 문제가되지 않습니다. Postgres는에 대한 입력으로 몇 가지 합리적인 텍스트 표현을 허용합니다 uuid. 설명서 :

PostgreSQL은 다음과 같은 대체 입력 형식도 허용합니다. 대문자 숫자 사용, 중괄호로 묶은 표준 형식, 일부 또는 모든 하이픈을 생략하고 4 자리 그룹 뒤에 하이픈을 추가합니다. 예를 들면 다음과 같습니다.

A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}

게다가는 md5()함수가 반환은 text, 당신이 사용하는 것이 decode()로 변환 할 bytea및 기본 표현 이다 :

SELECT decode(md5('Store hash for long string, maybe for index?'), 'hex')

\220\267R^\204\366HP\302\357\264\007\372\343\362q

encode()원래 텍스트 표현 을 다시 가져와야합니다.

SELECT encode(my_md5_as_bytea, 'hex');

또한 내부 오버 헤드 로 인해 byteaRAM에 20 바이트 (및 디스크에 17 바이트, 패딩이있는 24 바이트)를 차지하는 것처럼 저장된 값 은 특히 ​​간단한 인덱스의 크기와 성능에 바람직하지 않습니다.varlena

모든 것이uuid 여기 에 유리 합니다.


1
이 "uuid"에 대한 합법입니까? 내가 너무 비판적 인 경우 실례하지만, 내가보고있는 것은 "uuid"데이터 유형이 길이가 16 옥텟 인 숫자를 이진 형식으로 저장하는 데 지향적이라고 생각합니다. 그러나 "uuid"라는 용어는 특정 생성 / 해싱 알고리즘뿐만 아니라 대시로 구분 된 16 진수 문자의 5 개 블록으로 된 기존의 텍스트 표현을 의미합니다. 이 유형 이름이 UUID / GUID 생성을 강력하게 제안하는 경우 프로그래머가 최소한이 유형을 사용하여 해시를 저장하는 것은 약간 잘못된 것입니까?
Andrew Wolfe

2
@AndrewWolfe : 완전히 합법적이며 IMO. 이름으로 쫓겨나 지 마십시오 . 제공된 유형 캐스트 ​​및 입력 / 출력 로직의 편리한 세트가있는 16 바이트 엔티티입니다. 현재로서는 실제로 "고유 식별자"가 필요합니다. text"텍스트"가 아니더라도 모든 종류의 문자 데이터를 열에 저장할 수 있습니다 .
Erwin Brandstetter

MD5 해시가 기본 64로 변환되면 어떻게 저장
합니까

2
@PirateApp, 먼저 해독하십시오 : SELECT encode(decode('tZmffOd5Tbh8yXaVlZfRJQ==', 'base64'), 'hex')::uuid;.
nyov

1
@nyov : uuid는 160-512 비트 사이에서 생성되는 SHA 알고리즘의 결과를 저장할 수없는 16 바이트 유형입니다. Postgres의 표준 배포판에 맞는 유사한 유형은 없습니다. 하나를 만들 수 있습니다 ... 실패하면 기본값 byteapg_crypto같습니다 .
Erwin Brandstetter

2

MD5를 text또는 varchar열에 저장합니다 . 다양한 문자 데이터 유형간에 성능 차이는 없습니다. varchar(xxx)md5 값이 특정 길이를 초과하지 않도록함으로써 md5 값 의 길이를 제한 할 수 있습니다 .

큰 IN 목록은 일반적으로 빠르지 않으므로 다음과 같이하는 것이 좋습니다.

with md5vals (md5) as (
  values ('one'), ('two'), ('three')
)
select t.*
from the_table t
  join md5vals m on t.name_key  = m.md5;

때때로 더 빠른 다른 옵션은 배열을 사용하는 것입니다.

select t.*
from the_table t
where name_key = ANY (array['one', 'two', 'three']);

평등을 비교할 때 정기적 인 BTree 지수가 좋습니다. 두 쿼리 모두 이러한 인덱스를 사용할 수 있어야합니다 (특히 행 중 일부만 선택하는 경우).


bit (128) 또는 hex (32)를 사용하지 않는 특별한 이유는 무엇입니까? 이러한 필드에 값이 깔끔하게 맞도록 보장되며 잘못된 값이 할당되지 않도록 보호하고 싶습니다.
bobocopy

3
@bobocopy : Postgres에는 "16 진"데이터 형식이 없습니다. 나는 bit타입을 사용한 적이 없으므로 그것에 대해 언급 할 수 없습니다. 행 당신의 예상 수를 감안할 때, 어윈의 제안은 UUID로이 저장으로 얻을 절약 나은 때문에 공간이 될 것으로 보인다
a_horse_with_no_name

-1

다른 옵션은 4 INTEGER 또는 2 BIGINT 컬럼을 사용하는 것입니다.


2
스토리지 크기 측면에서 두 옵션 중 어느 것이나 적합하지만 작업하기가 얼마나 편리합니까? 아마도 당신은 예를 보여주기 위해 답을 넓힐 수도 있고 그렇지 않으면 설명 할 수도 있습니다.
Andriy M
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.