This commit is contained in:
@@ -0,0 +1,538 @@
|
||||
# 게시판 글 ID 및 피드백 성능 설계
|
||||
|
||||
게시판 글 ID(Primary Key 및 식별자)는 조회 성능, 보안, 분산 환경 지원 여부, URL 가독성 등에 따라 여러 방식으로 설계할 수 있음.
|
||||
|
||||
## 1. 자동 증가 정수형
|
||||
|
||||
### Auto Increment / Sequence
|
||||
|
||||
MySQL의 `AUTO_INCREMENT`, PostgreSQL의 `BIGSERIAL` 또는 `IDENTITY`를 사용하는 전통적인 방식.
|
||||
|
||||
| 구분 | 내용 |
|
||||
|---|---|
|
||||
| 형태 | `1`, `2`, `3`, `4`, ... |
|
||||
| 저장 방식 | `INT` 또는 `BIGINT` |
|
||||
| 구현 난이도 | 가장 낮음 |
|
||||
| 인덱스 성능 | 매우 좋음 |
|
||||
|
||||
### 장점
|
||||
|
||||
- 구현이 가장 쉽고 직관적임
|
||||
- 저장 용량이 적음
|
||||
- B-Tree 인덱스 정렬 성능이 좋음
|
||||
- 최신 글 순서대로 자동 정렬하기 쉬움
|
||||
|
||||
### 단점
|
||||
|
||||
- 다음 글 번호를 쉽게 추측할 수 있음
|
||||
- 크롤링이나 글 수량 파악에 취약할 수 있음
|
||||
- 분산 DB 또는 멀티 마스터 환경에서 충돌 없이 발급하기 어려움
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- 사내 인트라넷
|
||||
- 관리자 전용 게시판
|
||||
- 단일 DB 인스턴스 환경
|
||||
|
||||
## 2. UUID v4
|
||||
|
||||
### 완전 랜덤 128비트 식별자
|
||||
|
||||
전 세계에서 고유한 값을 무작위로 생성하는 표준 방식.
|
||||
|
||||
| 구분 | 내용 |
|
||||
|---|---|
|
||||
| 형태 | `c9a646d3-9c61-4cc9-bc19-482386a37365` |
|
||||
| 저장 방식 | UUID 또는 `CHAR(36)` 등 |
|
||||
| 생성 방식 | 완전 랜덤 |
|
||||
| 분산 환경 | 지원 |
|
||||
|
||||
### 장점
|
||||
|
||||
- 중복 가능성이 매우 낮음
|
||||
- 중앙 조율 없이 생성 가능함
|
||||
- 다음 ID를 추측하기 어려움
|
||||
- 분산 서버 환경에 적합함
|
||||
|
||||
### 단점
|
||||
|
||||
- 36자 문자열이라 URL이 길고 복잡함
|
||||
- 완전 랜덤 값이라 B-Tree 인덱스 페이지 분할이 자주 발생할 수 있음
|
||||
- 대량 입력 환경에서 정수형보다 쓰기 성능이 낮을 수 있음
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- 외부 API 통신 식별자
|
||||
- 추측 방지가 중요한 비즈니스 데이터
|
||||
- 분산 시스템
|
||||
|
||||
## 3. 시간 순 정렬 가능 고유 식별자
|
||||
|
||||
### UUID v7 / ULID
|
||||
|
||||
UUID의 고유성과 Auto Increment의 시간 정렬 장점을 결합한 방식.
|
||||
|
||||
| 방식 | 예시 |
|
||||
|---|---|
|
||||
| UUID v7 | `018d3b2f-7a54-7c3a-8b1e-9d2a4e5b6c7d` |
|
||||
| ULID | `01ARZ3NDEKTSV4RRFFQ69G5FAV` |
|
||||
|
||||
### 장점
|
||||
|
||||
- 생성된 시간 순으로 정렬 가능함
|
||||
- B-Tree 인덱스에 순차적으로 적재되어 쓰기 성능이 좋음
|
||||
- 분산 환경에서 충돌 없이 생성 가능함
|
||||
- 다음 ID를 추측하기 어려움
|
||||
|
||||
### 단점
|
||||
|
||||
- 정수형보다 저장 공간을 많이 사용함
|
||||
- UUID v4보다 생성 규칙이 복잡함
|
||||
- ID만 보고 생성 시점을 일부 추정할 수 있음
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- 대용량 트래픽 게시판
|
||||
- 분산 서비스
|
||||
- 최신 웹 애플리케이션
|
||||
|
||||
## 4. 트위터 스노우플레이크
|
||||
|
||||
### Snowflake
|
||||
|
||||
대량의 데이터를 분산 환경에서 생성하기 위해 고안된 64비트 정수 기반 방식.
|
||||
|
||||
### 기본 구조
|
||||
|
||||
```text
|
||||
타임스탬프 41비트 + 데이터센터/머신 ID 10비트 + 시퀀스 번호 12비트
|
||||
```
|
||||
|
||||
### 예시
|
||||
|
||||
```text
|
||||
1542893478912349184
|
||||
```
|
||||
|
||||
### 장점
|
||||
|
||||
- 64비트 정수라 인덱싱 성능이 좋음
|
||||
- 시간 순 정렬이 가능함
|
||||
- 분산 서버 간 ID 충돌을 방지할 수 있음
|
||||
- 대규모 시스템에서 높은 처리량을 지원함
|
||||
|
||||
### 단점
|
||||
|
||||
- 별도의 ID 생성기 또는 Worker 관리가 필요함
|
||||
- 데이터센터 및 머신 ID 관리가 필요함
|
||||
- JavaScript에서 일반 숫자로 처리하면 정밀도 손실이 발생할 수 있음
|
||||
- Auto Increment보다 운영 복잡도가 높음
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- 대규모 분산 SNS
|
||||
- 초대형 커뮤니티
|
||||
- 초당 수천 건 이상의 글이 등록되는 서비스
|
||||
|
||||
## 5. 해시 기반 난독화
|
||||
|
||||
### Sqids / Hashids
|
||||
|
||||
DB에는 정수형 ID를 저장하고, URL이나 외부 화면에서는 별도의 문자열로 변환하여 표시하는 방식.
|
||||
|
||||
| 구분 | 예시 |
|
||||
|---|---|
|
||||
| 내부 DB ID | `1204` |
|
||||
| 외부 URL ID | `b9Xq` |
|
||||
|
||||
### 장점
|
||||
|
||||
- DB에서는 정수형 인덱스 성능을 유지할 수 있음
|
||||
- 짧고 깔끔한 URL을 만들 수 있음
|
||||
- 외부에서 원래 순번을 바로 유추하기 어려움
|
||||
- 기존 정수형 DB 구조를 유지하면서 외부 노출만 변경할 수 있음
|
||||
|
||||
### 단점
|
||||
|
||||
- 내부 ID와 외부 표시 ID가 달라짐
|
||||
- 암호화가 아니라 인코딩에 가까움
|
||||
- 시크릿 솔트가 노출되면 원래 정수값을 역산할 수 있음
|
||||
- 운영자가 같은 글을 서로 다른 ID로 인식할 수 있음
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- 외부 URL을 짧게 만들고 싶은 서비스
|
||||
- 기존 정수형 DB를 유지하면서 외부 노출만 바꾸려는 서비스
|
||||
- 짧은 URL 식별자가 필요한 서비스
|
||||
|
||||
## 6. 비즈니스 접두사 결합형
|
||||
|
||||
### Stripe 스타일 ID
|
||||
|
||||
글의 종류나 도메인을 식별할 수 있도록 ID 앞에 접두사를 붙이는 방식.
|
||||
|
||||
### 예시
|
||||
|
||||
```text
|
||||
post_01ARZ3NDEKTSV4RRFFQ69G5FAV
|
||||
qna_01ARZ3NDEKTSV4RRFFQ69G5FAV
|
||||
fb_202609_a8f3d
|
||||
```
|
||||
|
||||
### 장점
|
||||
|
||||
- ID만 보고도 데이터 종류를 구분할 수 있음
|
||||
- 로그나 알림에서 식별이 쉬움
|
||||
- API 중심 서비스에 적합함
|
||||
- 디버깅과 모니터링이 편리함
|
||||
|
||||
### 단점
|
||||
|
||||
- 숫자만 사용하는 방식보다 ID가 길어짐
|
||||
- 글 종류가 변경되면 접두사 정책도 고려해야 함
|
||||
- 단순한 게시판에서는 다소 복잡할 수 있음
|
||||
|
||||
### 적합한 사용처
|
||||
|
||||
- API 중심 B2B SaaS
|
||||
- 여러 유형의 게시물이 함께 존재하는 시스템
|
||||
- 로그와 웹훅에서 데이터 유형을 즉시 구분해야 하는 서비스
|
||||
|
||||
## 방식별 비교
|
||||
|
||||
| 방식 | 저장 타입 | URL 길이 | 분산 환경 | 인덱스 성능 | 추측 방지 | 적합한 상황 |
|
||||
|---|---|---:|:---:|:---:|:---:|---|
|
||||
| Auto Increment | `INT` / `BIGINT` | 매우 짧음 | 낮음 | 매우 좋음 | 낮음 | 내부 관리용, 소규모 서비스 |
|
||||
| UUID v4 | UUID / `CHAR` | 김 | 높음 | 보통 | 높음 | 보안이 중요한 데이터 |
|
||||
| UUID v7 / ULID | UUID / 16바이트 등 | 중간 | 높음 | 좋음 | 높음 | 대용량·분산 웹 서비스 |
|
||||
| Snowflake | `BIGINT` | 짧음 | 높음 | 매우 좋음 | 보통 | 대규모 분산 시스템 |
|
||||
| Sqids / Hashids | DB는 정수형 | 외부는 짧음 | 낮음 | 매우 좋음 | 보통 | URL 난독화가 필요한 경우 |
|
||||
| 접두사 결합형 | 문자열 또는 조합형 | 중간~김 | 방식에 따라 다름 | 방식에 따라 다름 | 방식에 따라 다름 | API·로그 식별이 중요한 경우 |
|
||||
|
||||
## 검토할 때 확인할 기준
|
||||
|
||||
ID 방식을 선택할 때는 다음 항목을 함께 확인해야 함.
|
||||
|
||||
1. 화면에 표시되는 ID와 DB의 실제 ID를 같게 유지할 것인지
|
||||
2. 프로젝트마다 번호를 새로 시작할 것인지
|
||||
3. 프로젝트를 넘어선 전체 글 번호가 필요한지
|
||||
4. 피드백과 이슈가 같은 번호 체계를 사용할 것인지
|
||||
5. 외부 Q&A 원본 ID와 관리페이지 ID를 어떻게 연결할 것인지
|
||||
6. URL에서 ID를 직접 노출할 것인지
|
||||
7. 단일 서버인지 분산 서버인지
|
||||
8. 향후 글 수가 얼마나 증가할지
|
||||
|
||||
특히 내부 DB ID와 화면 표시 ID를 별도로 운영하면, 같은 글을 서로 다른 번호로 인식할 수 있으므로 표시 정책을 먼저 확정해야 함.
|
||||
|
||||
---
|
||||
|
||||
# 피드백 테이블 성능 및 캐시 설계
|
||||
|
||||
## 1. 현재 테이블 구조
|
||||
|
||||
### 기본 구성
|
||||
|
||||
- 데이터베이스 엔진: MySQL 8.0.46
|
||||
- 스토리지 엔진: InnoDB
|
||||
- 기본 컬럼
|
||||
- `id`: 기본 키
|
||||
- `created_at`: 등록 일시
|
||||
- `updated_at`: 수정 일시
|
||||
- `deleted_at`: 소프트 삭제 일시
|
||||
- `channel_id`: 채널 ID
|
||||
- `data`: 제목, 내용, 카테고리 등을 저장하는 JSON 또는 TEXT 컬럼
|
||||
- `admin_first_read_at`: 관리자가 처음 확인한 일시
|
||||
- `admin_first_read_by`: 처음 확인한 관리자
|
||||
|
||||
### 주요 조회 패턴
|
||||
|
||||
1. 특정 채널의 삭제되지 않은 피드백을 최신순으로 조회
|
||||
2. 특정 채널에서 아직 확인하지 않은 피드백만 조회
|
||||
3. `data` 내부의 `category` 값으로 피드백 필터링
|
||||
4. 목록을 페이지 단위로 조회
|
||||
|
||||
## 2. 인덱스 설계
|
||||
|
||||
### 2.1 채널별 피드백 목록 조회
|
||||
|
||||
목록 조회는 다음과 같은 조건과 정렬을 사용함.
|
||||
|
||||
```sql
|
||||
WHERE channel_id = ?
|
||||
AND deleted_at IS NULL
|
||||
ORDER BY id DESC
|
||||
```
|
||||
|
||||
`channel_id`, `deleted_at`, `id`를 하나의 복합 인덱스로 구성하면 채널과 활성 상태로 범위를 좁힌 뒤 최신순으로 바로 조회할 수 있음.
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_feedbacks_channel_active_list
|
||||
ON feedbacks (channel_id, deleted_at, id DESC);
|
||||
```
|
||||
|
||||
`id`가 `AUTO_INCREMENT`라면 `created_at` 대신 `id`를 정렬 기준으로 사용할 수 있음. 일반적으로 ID 증가 순서와 등록 순서가 일치하므로 정렬 비용을 줄이기 쉬움.
|
||||
|
||||
### 2.2 미확인 피드백 조회
|
||||
|
||||
관리자가 아직 확인하지 않은 피드백을 자주 조회한다면 다음 인덱스를 사용할 수 있음.
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_feedbacks_unread
|
||||
ON feedbacks (channel_id, admin_first_read_at, deleted_at);
|
||||
```
|
||||
|
||||
이 인덱스는 특정 채널에서 `admin_first_read_at IS NULL` 조건을 사용하는 조회의 전체 테이블 스캔을 줄이는 데 도움을 줌.
|
||||
|
||||
### 2.3 JSON 내부 값 조회
|
||||
|
||||
`category`처럼 `data` 내부의 값을 조건으로 자주 조회한다면 생성 컬럼을 추가해 인덱스를 구성할 수 있음.
|
||||
|
||||
```sql
|
||||
ALTER TABLE feedbacks
|
||||
ADD COLUMN category VARCHAR(50)
|
||||
GENERATED ALWAYS AS (data->>'$.category') VIRTUAL;
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_feedbacks_channel_category
|
||||
ON feedbacks (channel_id, category, id DESC);
|
||||
```
|
||||
|
||||
생성 컬럼을 `VIRTUAL`로 만들면 별도의 값을 테이블에 저장하지 않고 조회 시 계산할 수 있음. 해당 값의 검색 빈도와 데이터 분포를 확인한 뒤 적용해야 함.
|
||||
|
||||
## 3. 5만 건 기준 데이터 및 메모리 규모
|
||||
|
||||
현재 523행의 데이터 용량이 약 `528.0 KiB`라면 행 하나의 평균 크기는 약 `1 KiB` 수준임.
|
||||
|
||||
| 항목 | 예상 크기 |
|
||||
|---|---:|
|
||||
| 5만 건 데이터 | 약 50~70MB |
|
||||
| 복합 인덱스 2~3개 | 약 4~6MB |
|
||||
| 데이터와 인덱스 합계 | 약 80MB 미만 |
|
||||
|
||||
실제 크기는 JSON 또는 TEXT 데이터의 길이, 인덱스 컬럼의 자료형, 인덱스 페이지 여유 공간에 따라 달라질 수 있음.
|
||||
|
||||
InnoDB Buffer Pool이 데이터와 인덱스보다 충분히 크다면 자주 조회되는 데이터가 메모리에 상주할 수 있음. 이 경우 반복 조회에서 디스크 I/O가 줄어들고 인덱스 기반 조회의 응답 시간이 짧아짐.
|
||||
|
||||
다만 실제 성능은 다음 조건에 따라 달라짐.
|
||||
|
||||
- 서버의 가용 메모리
|
||||
- `innodb_buffer_pool_size` 설정
|
||||
- 동시 접속자 수
|
||||
- 쿼리의 실제 실행 계획
|
||||
- JSON 또는 TEXT 컬럼의 평균 크기
|
||||
- 페이지 번호가 뒤로 갈수록 커지는 OFFSET 사용 여부
|
||||
|
||||
## 4. 캐시 구성
|
||||
|
||||
캐시는 MySQL 내부 캐시와 애플리케이션 외부 캐시로 나눌 수 있음.
|
||||
|
||||
```text
|
||||
[클라이언트 요청]
|
||||
│
|
||||
▼
|
||||
┌────────────────────────┐
|
||||
│ Redis 애플리케이션 캐시 │
|
||||
└──────────┬─────────────┘
|
||||
│
|
||||
HIT ──┤ 캐시된 목록 즉시 반환
|
||||
│
|
||||
MISS ▼
|
||||
┌─────────────────────────┐
|
||||
│ MySQL InnoDB Buffer Pool │
|
||||
└──────────┬──────────────┘
|
||||
│
|
||||
▼
|
||||
DB 조회 후 Redis 갱신
|
||||
```
|
||||
|
||||
### 4.1 InnoDB Buffer Pool
|
||||
|
||||
InnoDB Buffer Pool은 테이블 데이터와 인덱스 페이지를 메모리에 보관함.
|
||||
|
||||
- 반복 조회 시 디스크 접근을 줄일 수 있음
|
||||
- 복합 인덱스가 있으면 필요한 범위만 빠르게 탐색할 수 있음
|
||||
- 데이터와 인덱스 전체 크기보다 충분히 큰 Buffer Pool이 필요함
|
||||
- Buffer Pool 크기는 서버의 다른 프로세스가 사용할 메모리를 고려해 설정해야 함
|
||||
|
||||
### 4.2 Redis 애플리케이션 캐시
|
||||
|
||||
동시 접속자가 많거나 동일한 목록을 반복 조회하는 경우 Redis를 추가할 수 있음.
|
||||
|
||||
#### 우선 캐싱할 대상
|
||||
|
||||
- 채널별 최신 피드백 목록
|
||||
- 채널별 첫 페이지 목록
|
||||
- 전체 피드백 수 또는 상태별 개수
|
||||
- 답변 대기, 미확인 등 자주 표시되는 요약 정보
|
||||
|
||||
#### 캐시 키 예시
|
||||
|
||||
```text
|
||||
feedback:channel:{channel_id}:page:1
|
||||
```
|
||||
|
||||
실제 키에는 프로젝트, 채널, 페이지 크기, 정렬 조건, 검색 조건 등 결과에 영향을 주는 값을 포함해야 함.
|
||||
|
||||
#### 캐시 만료 및 무효화
|
||||
|
||||
새 피드백이 등록되거나 기존 피드백이 수정·삭제되면 해당 채널의 목록 캐시를 갱신해야 함.
|
||||
|
||||
| 방식 | 설명 |
|
||||
|---|---|
|
||||
| 즉시 삭제 | 등록·수정·삭제 시 관련 캐시를 바로 삭제함 |
|
||||
| 짧은 TTL | 캐시 만료 시간을 30초~1분 정도로 설정함 |
|
||||
| 조합 | 중요 목록은 즉시 삭제하고 나머지는 짧은 TTL을 사용함 |
|
||||
|
||||
첫 페이지는 새 글 등록의 영향을 가장 많이 받으므로 우선적으로 삭제하거나 갱신해야 함.
|
||||
|
||||
## 5. 조회 성능 점검 방법
|
||||
|
||||
인덱스를 추가하기 전에 실제 쿼리의 실행 계획을 확인해야 함.
|
||||
|
||||
```sql
|
||||
EXPLAIN ANALYZE
|
||||
SELECT *
|
||||
FROM feedbacks
|
||||
WHERE channel_id = 1
|
||||
AND deleted_at IS NULL
|
||||
ORDER BY id DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
다음 항목을 확인함.
|
||||
|
||||
- 사용할 인덱스가 예상대로 선택되는지
|
||||
- `type`이 불필요한 전체 테이블 스캔인지
|
||||
- `key`에 복합 인덱스가 표시되는지
|
||||
- `rows`가 과도하게 많지 않은지
|
||||
- `Extra`에 `Using filesort` 또는 `Using temporary`가 발생하는지
|
||||
|
||||
## 6. 적용 시 주의사항
|
||||
|
||||
- 인덱스는 조회 속도를 높이지만 등록·수정·삭제 시 추가 비용이 발생함
|
||||
- 사용하지 않는 인덱스는 저장 공간과 쓰기 성능을 낭비함
|
||||
- `deleted_at`의 값 분포가 대부분 `NULL`이면 인덱스 효과가 제한될 수 있음
|
||||
- JSON 필드는 자주 검색하는 값만 생성 컬럼으로 분리하는 것이 좋음
|
||||
- `OFFSET`이 큰 페이지에서는 커서 기반 조회를 검토할 수 있음
|
||||
- 인덱스 적용 전후에 실제 운영 쿼리의 실행 계획과 응답 시간을 비교해야 함
|
||||
|
||||
---
|
||||
|
||||
# 식별자 체계 7대 조합의 트레이드오프
|
||||
|
||||
식별자 체계는 식별자 크기, B-Tree 인덱스와 페이지 분할, 메모리 버퍼 풀 상주율, 분산 환경에서의 생성 여부, 화면 가독성, 예상 병목 시점을 함께 고려해야 함.
|
||||
|
||||
## 1. 7대 조합 비교
|
||||
|
||||
| 번호 | 조합 방식 | 물리 저장 스펙 | 1,000만 건 인덱스 크기 | 장점 | 단점 및 리스크 | 적합한 환경 |
|
||||
|---:|---|---|---:|---|---|---|
|
||||
| 1 | **INT Auto-Increment 단독** | `INT` (4B) | 약 200MB | 구현 난이도 최저, 인덱스 용량 최소, 메모리 캐시 효율이 좋음, JavaScript 숫자 변환 이슈가 없음 | 최대 약 21억 번호 한계, 화면에서 프로젝트 구분이 어려움, 번호 추측에 취약함 | 5만 건 이하의 소규모 내부 인트라넷 |
|
||||
| 2 | **BIGINT Auto-Increment 단독** | `BIGINT` (8B) | 약 400MB | 매우 큰 번호 범위, 순차 적재로 페이지 분할이 적음, 구현 복잡도가 낮음 | 단순 숫자만 보여 프로젝트 식별성이 낮음, 멀티 마스터·샤딩 환경에서는 중앙 채번 병목 가능 | 단일 DB 기반 대용량 내부 시스템 |
|
||||
| 3 | **BIGINT PK + 복합 코드 투트랙** | `BIGINT` (8B) + 화면 조합 | 약 400MB | DB 내부는 8바이트 정수로 유지, 화면과 알림에는 `ABC-WEB-104`처럼 식별성 있는 코드 사용, 별도 채번 락이 필요 없음 | 접두사 결합 로직 필요, ID 검색 시 코드 파싱 또는 검색 규칙 고려 필요 | 성능과 화면 식별성을 함께 요구하는 시스템 |
|
||||
| 4 | **비즈니스 문자열 복합키 직접 저장** | `VARCHAR(30)` | 약 800MB~1.2GB | DB만 확인해도 `ABC-WEB-104`처럼 내용을 바로 파악 가능, 별도 변환 로직이 없음 | `MAX(번호)+1` 방식에서 동시성 락 병목 가능, 외래키 인덱스 용량 증가, 문자열 비교 비용 증가 | 트래픽이 적고 화면 가독성이 최우선인 관리자 도구 |
|
||||
| 5 | **ULID / UUID v7 단독** | `BINARY(16)` 또는 `VARCHAR(26)` | 약 800MB~1GB | 시간순 정렬로 페이지 분할을 줄임, 분산 서버에서 별도 중앙 인프라 없이 생성 가능, 단일 컬럼으로 외부 노출 가능 | 8바이트 정수보다 인덱스가 큼, 사람이 구두로 식별하기 어려움, 문자열 저장 시 용량 증가 | 분산 MSA 환경, 오픈 API 중심 서비스 |
|
||||
| 6 | **Snowflake** | `BIGINT` (8B) | 약 400MB | 8바이트 정수의 인덱스 효율 유지, 다중 서버에서 높은 동시 생성량 지원 | Worker ID 관리와 서버 시계 동기화 필요, 단일 DB 환경에서는 운영 복잡도가 높음 | 초당 수만 건의 쓰기가 발생하는 글로벌 분산 플랫폼 |
|
||||
| 7 | **UUID v4 완전 난수** | `VARCHAR(36)` | 약 2GB 이상 | 널리 사용되는 방식, 예측이 어려운 식별자 | 랜덤 삽입에 따른 B-Tree 페이지 분할, 인덱스 단편화, 버퍼 풀 효율 저하, 대규모 게시판의 PK로는 부적합할 수 있음 | 보안상 예측 방지가 가장 중요한 분산 데이터 |
|
||||
|
||||
> 인덱스 크기는 자료형, 인덱스 구성, 페이지 여유 공간, 보조 인덱스와 외래키 수에 따라 달라지는 대략적인 값임.
|
||||
|
||||
## 2. 플랫폼에 맞는 3가지 케이스
|
||||
|
||||
### Case 1. BIGINT PK + 프로젝트 코드 가상 조합
|
||||
|
||||
#### 구성
|
||||
|
||||
- DB 저장: `id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY`
|
||||
- 화면 및 웹훅: 프로젝트 약어, 채널 약어, `id`를 결합
|
||||
- 표시 예시: `ABC-WEB-10492`
|
||||
|
||||
#### 장점
|
||||
|
||||
- 1,000만 건 기준 PK 인덱스 용량을 약 400MB 수준으로 유지할 수 있음
|
||||
- InnoDB Buffer Pool에 데이터와 인덱스를 상주시키기 쉬움
|
||||
- 복잡한 채번 테이블이나 별도 락 없이 DB 내장 시퀀스로 생성 가능함
|
||||
- 댓글과 히스토리 등 자식 데이터가 많아져도 번호 범위가 충분함
|
||||
|
||||
#### 단점
|
||||
|
||||
- 프로젝트별 번호가 1번부터 다시 시작하지 않음
|
||||
- 화면과 알림에 프로젝트·채널 코드를 결합하는 로직이 필요함
|
||||
|
||||
#### 적합한 상황
|
||||
|
||||
- 단일 DB 중심 구조
|
||||
- 운영 오버헤드를 줄이면서 성능과 화면 식별성을 함께 확보해야 하는 경우
|
||||
- 전역 번호를 사용해도 업무상 문제가 없는 경우
|
||||
|
||||
### Case 2. ULID 또는 UUID v7 단독 PK
|
||||
|
||||
#### 구성
|
||||
|
||||
- DB 저장: `id BINARY(16) PRIMARY KEY`
|
||||
- 외부 표시: ULID 문자열 또는 접두사를 결합한 형태
|
||||
- 표시 예시: `01ARZ3NDEKTSV4RRFFQ69G5FAV`, `fb_01ARZ3...`
|
||||
|
||||
#### 장점
|
||||
|
||||
- 외부 시스템이나 클라이언트가 중앙 DB에 의존하지 않고 ID를 미리 생성할 수 있음
|
||||
- 시간순 정렬이 가능해 UUID v4보다 페이지 분할을 줄일 수 있음
|
||||
- 향후 DB 샤딩이나 MSA 분리에 유연함
|
||||
- 외부 API와 웹훅의 식별자로 사용하기 좋음
|
||||
|
||||
#### 단점
|
||||
|
||||
- BIGINT보다 인덱스 용량이 큼
|
||||
- 사람이 구두로 전달하거나 화면에서 빠르게 확인하기 어려움
|
||||
- 서버 메모리와 Buffer Pool 요구량이 커질 수 있음
|
||||
|
||||
#### 적합한 상황
|
||||
|
||||
- 외부 시스템 연동이 많음
|
||||
- 여러 서버와 서비스가 독립적으로 글을 생성함
|
||||
- 향후 MSA나 샤딩을 고려함
|
||||
|
||||
### Case 3. BIGINT PK + 프로젝트·채널별 채번 격리
|
||||
|
||||
#### 구성
|
||||
|
||||
- 내부 PK: `id BIGINT UNSIGNED AUTO_INCREMENT`
|
||||
- 업무 번호: `channel_id INT`, `issue_no INT`
|
||||
- 유일성: `UNIQUE (channel_id, issue_no)`
|
||||
- 화면 및 웹훅: `ABC-WEB-1`
|
||||
|
||||
#### 장점
|
||||
|
||||
- 프로젝트와 채널마다 1번부터 시작하는 직관적인 번호 제공
|
||||
- 사용자와 관리자 모두 번호를 쉽게 기억하고 전달할 수 있음
|
||||
- 내부 조인과 외래키는 8바이트 정수로 처리해 성능을 유지할 수 있음
|
||||
|
||||
#### 단점
|
||||
|
||||
- 새 글 등록 시 채널별 마지막 번호를 안전하게 발급해야 함
|
||||
- 동시에 여러 글이 등록되면 채번 락 경합이 발생할 수 있음
|
||||
- Redis `INCR` 또는 별도 채번 테이블 등 보조 구조가 필요할 수 있음
|
||||
|
||||
#### 적합한 상황
|
||||
|
||||
- Jira와 같이 프로젝트별 번호가 반드시 필요한 경우
|
||||
- 사용자 경험과 업무 소통에서 번호의 직관성이 가장 중요한 경우
|
||||
- 채널별 등록량이 많지 않고 채번 관리가 가능한 경우
|
||||
|
||||
## 3. 선택 기준
|
||||
|
||||
| 우선순위 | 적합한 케이스 |
|
||||
|---|---|
|
||||
| 운영 복잡도를 줄이고 성능과 화면 식별성을 함께 확보 | Case 1 |
|
||||
| 외부 연동, 분산 생성, 향후 MSA 확장성 | Case 2 |
|
||||
| 프로젝트·채널별 1번부터 시작하는 업무 번호 | Case 3 |
|
||||
|
||||
선택 전에 다음 사항을 확정해야 함.
|
||||
|
||||
1. 화면의 글 ID와 DB의 실제 식별자를 동일하게 표시할지 여부
|
||||
2. 전체 프로젝트에서 하나의 번호를 사용할지 여부
|
||||
3. 프로젝트 또는 채널마다 번호를 새로 시작할지 여부
|
||||
4. 피드백과 이슈가 같은 번호 체계를 사용할지 여부
|
||||
5. 외부 Q&A 원본 ID와 관리페이지 ID의 연결 방식
|
||||
6. URL과 웹훅에 어떤 ID를 사용할지 여부
|
||||
7. 향후 분산 생성 또는 샤딩이 필요한지 여부
|
||||
Reference in New Issue
Block a user