[DB] Primary Key와 성능, 보안

DB를 다룰때 종종 나오는 화두 중 하나가, 기본키의 타입이나 형태를 어떻게 정할지다.

이건 성능이나 DB 구성과 밀접한 연관이 있고, "보안 전문가"들의 주장에 따르면 보안과도 연관이 있다.




Primary Key의 타입

primary key는 일반적으로 정수, 문자열, 특별히 설계된 uuid 타입들로 만들 수 있다.

저장공간의 효율성 측면에서는 정수나 snowflake id 같은 고정크기 ID가 가장 효율적이다. 16바이트로 한정되는 uuid도 나쁘진 않다.

근데 실질적인 성능 차이는 단순히 타입으로만 결정되지는 않는다. 중요한 것은 값이 어떤 패턴으로 들어오는지다.




증분성(incremental)과 성능

실질적으로 값 insert의 성능에 영향을 주는 부분은, 정수인지 문자열인지가 아니라 값이 증가하는지 아닌지다.

대부분의 DB는 인덱스를 Tree 구조로 디스크에 쌓아올린다.
그리고 값이 1, 2, 3처럼 계속해서 증가하는 것이 파일의 끝에 추가하는 식으로 동작하기 쉽고, 리밸런싱도 비교적 덜 일어난다. 때문에 디스크 I/O나 캐시 효율성 측면에서 좋다.

이런 관점에서 완전 랜덤하게 값의 범위가 오락가락하는 uuid v4 같은건 성능적으로 그닥 좋지 않다.

기본적인 auto increment ID나, 랜덤한 성향이 있지만 어느 정도의 incremental함이 보장되는 uuidv7나 mongoDB ObjectID 등이 효율적이라고 할 수 있다.

단, 이건 단일노드 환경에서만 이상적으로 부합한다. 분산환경에서의 가정은 후술한다.




분산 DB와 ID

대부분의 RDB는 자동으로 관리되는 ID 생성 도구를 제공한다. MySQL은 auto increment가 있고, Postgresql은 sequence가 있다.

이건 단일 노드 서버에서는 잘 동작하고 편리하지만, write 노드가 여러개인 멀티노드 클러스터가 되면 전제가 망가지게 된다. ID 상태의 저장을 원자적으로 관리하는 것이 어렵고, 성능상으로도 확장이 어렵기 때문이다.

그래서 멀티노드 서버가 되면 uuid 같은 랜덤 기반의 탈중앙적인 ID 구조를 모색하거나, 아니면 노드의 고유번호를 샤드 키로 구성하는 방식을 선택하게 된다. 자세한 것은 별도 포스트를 참조한다.
https://m.blog.naver.com/sssang97/222998400484




분산 DB와 ID - 증분성 값

그리고 분산 DB 구성에서는 uuidv7 같은 Incremental ID 값이 오히려 성능 저하를 일으킬 가능성이 있다.
분산DB들은 대체로 ID 값을 기준으로 노드에 데이터를 분산하는데, 분산 구성에 따라서는 최신 값에 대한 insert가 특정 핫스팟 노드에만 쏠릴 수 있기 때문이다.

이건 DB 구현마다 달라서 세부 동작을 잘 확인해야 한다.
분산 기준이 되는 파티션키와 샤드 내 정렬키를 명시적으로 지정할 수 있다면, 파티션키에 랜덤성을 부여하는 식으로 해결하는 편이 권장된다. 그리고 증분성 값은 정렬키로 넣는 편히 효율적이다. (CassandraDB-like의 경우)




Primary Key와 예측 가능성

보안 측면에서 단조로운 incremental 키를 쓰지 말아야한다는 주장도 있다. 그냥 1, 2, 3로 증가하는 관리형 auto increment 키 말이다.
사용자가 그 다음 키를 예측할 수 있고, 얼마나 많은 데이터가 있는지 추정할 수 있다는 것이다.

사실 전자의 주장에는 별로 납득이 되진 않는다. 예측하면 뭘 할 수 있는데...? 접근 권한 구조만 견고하게 잡는다면 경험상 별 문제가 되진 않는다.

후자의 경우에는 경우에 따라서 민감할 수도 있다. 총 데이터가 몇개고 특정 기간 동안의 데이터 누적이 몇개인지 추정이 가능하니까.

근데 난 잘 모르겠다. 단순히 보안 때문에 auto increment를 거부하는건 동의가 되지 않는다.




Primary Key의 암호화?

보통 API를 설계할 때, 리소스의 고유한 식별값을 DB의 기본키로 그대로 쓰는 경우가 많다.
"GET /posts/145743" 이런 식으로 말이다.

이건 당장 이 네이버 블로그만 해도 그렇고, 대부분 시스템들의 URI나 API 구조를 훔쳐보면 다 그렇다.

근데 이 기본키를 직접 노출하면 보안적으로 위험하다는 주장이 가끔 보인다. 기본키 값을 암호화해서 노출해야 안전하다나...

내가 볼때 이건 클라이언트에서 패스워드 해싱을 처리하는 것과 다를 바 없는 멍청한 짓이다. 조금만 생각해봐도 알 수 있다.
기본키를 통해서 API를 쓰나, 암호화된 기본키를 통해 API를 쓰나 접근 수준에는 아무런 차이가 없다.

그냥 더 느려지기만 할 뿐이고, 취약점은 마찬가지로 권한구조를 잘못 구성하거나 단순 실수를 하는 지점에서 생긴다.

제발 이런것좀 하지 않았으면 한다.



참조
https://www.reddit.com/r/csharp/comments/rg7xob/is_it_bad_to_expose_primary_key_to_the_user_if/?tl=ko
https://security.stackexchange.com/questions/577/encrypting-a-databases-primary-key-when-sent-to-the-browser
https://maciejwalkowiak.com/blog/postgres-uuid-primary-key/
https://towardsdatascience.com/exploring-the-right-fit-choosing-primary-keys-for-your-database-64f5754f1515/
https://medium.com/google-cloud/understanding-uuidv7-and-its-impact-on-cloud-spanner-b8d1a776b9f7