SW 개발

데이터베이스 PK로 TSID 사용하기

지단로보트 2024. 3. 19. 09:28

개요

  • TSIDTime-Sorted Unique Identifier의 약자로 기존의 UUID를 대체할 목적으로 탄생했다. 이름이 의미하듯이 최소 크기의 랜덤 문자열로서 생성 시간 순으로 정렬 가능한 것이 특징이다. 작은 공간을 차지하면서 동시에 생성 시간순으로 정렬이 가능하기에 데이터베이스의 AUTO INCREMENT ID에 대한 대체제로 적합하다.

TSID 특징

  • 64 bit 정수(=18자 문자열) 또는 13자의 URL-safe한 Base 32 문자열로 표현할 수 있다. (나는 53 bit 이상의 정수를 처리할 수 없는 JavaScript 기반의 클라이언트를 고려하여 후자를 선호한다. UUID 대비 65%을 절약할 수 있다.)
  • 값의 생성 주체가 애플리케이션이기 때문에 데이터가 저장되기 전에 값을 미리 알 수 있다.
  • 1/1000초 단위의 시간 정보와 무작위 정보로 조합되어 고유성이 보장된다. 따라서 데이터베이스의 수평 확장 및 여러 지역에서의 동시 전개가 용이하다.
  • 무작위 정보가 포함되어 연속적 관계를 파악하기 힘들다. 반면에 생성 일시 순으로 정렬이 가능하여 B+Tree 인덱스의 성능 이점을 누릴 수 있다. 값 자체에서 생성 일시를 획득하는 것도 가능하다.

build.gradle.kts

  • 프로젝트 루트의 build.gradle.kts에 아래 내용을 추가한다.
dependencies {
    implementation("io.hypersistence:hypersistence-utils-hibernate-60:3.7.3")
}

TSID 생성

  • TSID는 아래와 같이 생성할 수 있다.
// TSID 생성
// 실행 시점1/1000초 단위로 저장
val tsid = TSID.fast()

// Long 타입으로 출력 (64비트 고정 길이)
// 556028620837202474
tsid.toLong()

// String 타입의 대문자로 출력 (13자 고정 길이)
0FDV92PPZ7AHA
tsid.toString()

// String 타입의 소문자로 출력 (13자 고정 길이)
// 0fdv92ppz7aha
tsid.toLowerCase()

// 저장된 Instant 오브젝트 획득
// 2024-03-14T08:19:13.719Z
tsid.instant

// TSID 문자열로 부터 TSID 오브젝트를 복원
TSID.from(tsid.toString())

JPA 엔티티에 TSID 적용

  • JPA 엔티티는 아래와 같이 PKTSID가 자동 생성되도록 설계할 수 있다.
import io.hypersistence.utils.hibernate.id.Tsid
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import java.io.Serializable

@Entity
@Table(name = "foo")
class Foo : Serializable {

    // [방법 1] 64 bit 정수 TSID 생성
    @Id
    @Tsid
    @Column
    var id: Long? = null

    // [방법 2] 13자 Base 32 문자열 TSID 생성
    @Id
    @Tsid
    @Column(columnDefinition = "CHAR(13)")
    var id: String? = null
    ...
}
  • String 타입의 TSID를 사용할 경우 DDL로 표현하면 아래와 같다.
CREATE TABLE `foo` (
  `id` CHAR(13) NOT NULL,
  ...
  PRIMARY KEY (`id`) USING BTREE
)
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_0900_ai_ci;
  • 실제 데이터를 생성해보면 아래와 같이 생성되어 시간 순으로 조회 가능한 것을 확인할 수 있다.
#id, TSID.from(id).instant
0FGA6Z3X7BRPW, 2024-03-22T00:15:00.072115Z
0FGA6Z3TFBRQR, 2024-03-22T00:15:00.050725Z
0FGA6Z3RKBRPP, 2024-03-22T00:15:00.035553Z
0FGA6Z3PZBRPH, 2024-03-22T00:15:00.022884Z
0FGA6Z3NQBRQQ, 2024-03-22T00:15:00.011920Z

TSID 생성 스크립트 제작

  • 리눅스 콘솔에서 급하게 TSID 생성이 필요할 수 있다. 평소 아래와 같이 스크립트를 만들어두면 편리하다.
# SDKMAN으로 kotlin, kscript 설치
$ sdk install kotlin
$ sdk install kscript

$ nano tsid.kts
#!/usr/bin/env kscript
@file:DependsOn("io.hypersistence:hypersistence-utils-hibernate-60:3.7.3")

import io.hypersistence.tsid.TSID

val tsid = TSID.fast()
println(tsid.toLong())
println(tsid.toString())
println(tsid.toLowerCase())
println(tsid.instant)

$ chmod +x tsid.kts

$ ./tsid.kts
558185822630215770
0FFRK1HE8H02T
0ffrk1he8h02t
2024-03-20T07:11:10.706Z

참고 글