분산환경에서 Tsid 사용하기
대부분 데이터를 구분하기 위해서는 고유한 ID(Primary Key)를 사용하는데,
전통적인 RDB에서는 AUTO_INCREMENT 같은 단일 시퀀스로도 충분했었다.
하지만, 분산환경으로 가면서 서버가 여러 대이고, DB도 여러 개(샤딩)으로 나뉘었을 때
“어떻게 충돌 없이, 순서를 유지하는 고유 ID를 만들 수 있는가”에 대한 내용으로 등장한 것이 TSID 이다.
기존 방식의 한계
1. AUTO_INCREMENT (DB 시퀀스)
- DB가 여러대로 구성되어 있는 경우 ID 충돌 위험이 있음
- 샤딩 후에는 각 노드가 ID 범위를 나눠서 사용해야함 -> 관리 복잡
2. UUID
충돌은 거의 없지만,
- 순서가 보장되지 않는다 -> 인덱스가 파편화 된다
- 읽기 어렵다 (ex. 550e8400-e29b-41d4-a716-446655440000)
특히, 대규모 트래픽이 예상될 때, INSERT 시 마다 디스크 쓰기의 성능이 급격하게 떨어진다.
2. TSID
TSID(Time-Sortable ID)는 “타임스탬프 기반으로 생성되는, 시간 순으로 정렬 가능한 고유 ID” 이다.
내부구조는 Timestamp(밀리초 단위 시간) + Node ID(서버 또는 샤드 식별자) + Sequence(같은 밀리초 내에서 중복방지) => Long 타입으로 표현 한 형태이다.
- 시간 순으로 생성되므로 정렬이 쉽다.
- B-Tree 인덱스 split 최소화 -> INSERT 성능 향상
- 시간 순서가 중요한 데이터에 적합(특히 이벤트, 메시지, 로그 등)
- 각 노드(서버/샤드)가 자신의 Node ID로 구분되므로 ID 충돌이 없다
Spring Boot + JPA 에 적용 방법
- build.gradle.kts 에 추가
dependencies { implementation("io.hypersistence:hypersistence-utils-hibernate-60:3.5.1") } - Entity 에
@Tsid추가@Entity @Table class User( val email: String, val nickname: String, ... ) : Audit() { @Id @Tsid // 추가 var id: Long? = null protected set ... } -
실제로 Entity 저장시

-
생성된 Tsid
768729030917065909을 기준으로 2진수로 표현하면
0000 1010 1010 1011 0001 0010 1111 1011 0001 0000 0101 0111 1000 1100 1011 0101
이 되고, 이걸 오른쪽부터 나눠서 보면 아래와 같다.구간 비트 수 2진수 10진수 의미 하위 12비트 12 1100 1011 01013253 Sequence 그 위 10비트 10 0101 1110 1000376 Node ID 나머지 42비트 42 0000 1010 1010 1011 0001 0010 1111 1011 0001 0000 0101183,279,283,265 Timestamp (2020-01-01T00:00:00Z 이후 ms) - 참고로 Node ID의 생성 전략에는
- 설정하지 않으면 랜덤으로 생성(default)이 되므지만, 충돌 가능성을 낮추기 위해서 default 로 사용하지는 말자.
- Node ID를 지정하는 방법에는 아래와 같은 것들이 있다.
- 시스템 프로퍼티 또는 환경변수로 지정
// JVM 인자. node bit수를 자동으로 7bit(100개에 피 ㄹ요한 최소 비트)로 줄여주고, 남은 비트는 시퀀스에 더 사용할 수 있도록 설정. JAVA_TOOL_OPTIONS="-Dtsid.node=7 -Dtsid.node.count=100" // 환경변수로 설정 TSID_NODE=123 TSID_NODE_COUNT=100 - 프로그램적으로 제어
@Configuration class TsidConfig( @Value("\${tsid.node:-1}") private val node: Int, @Value("\${tsid.node-bits:10}") private val nodeBits: Int, ) { @Bean fun tsidFactory(): TSID.Factory { return TSID.Factory.builder() .apply { if (nodeBits in 0..20) withNodeBits(nodeBits) } .apply { if (node >= 0) withNode(node) } .build() } }
더 자세한 내용은 https://github.com/vladmihalcea/hypersistence-tsid 를 참고하자
- 시스템 프로퍼티 또는 환경변수로 지정