Home > DB > πŸ’Ύ[Database] Primary Key 생성 μ „λž΅ - μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄

πŸ’Ύ[Database] Primary Key 생성 μ „λž΅ - μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄
Database Primary Key DBMS

β€œπŸ’Ύ[Database] Primary Key 생성 μ „λž΅ - μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄β€

🍎 Intro.

  • Primary Key(PK)λ₯Ό 생성할 λ•Œ, 각 IDκ°€ κ³ μœ ν•˜λ©΄μ„œλ„ μƒμ„±λœ μˆœμ„œλŒ€λ‘œ 정렬이 κ°€λŠ₯ν•˜λ„λ‘ λ§Œλ“œλŠ” λ°©μ‹μž…λ‹ˆλ‹€.
  • 이 방식은 UUID처럼 좩돌 μ—†λŠ” κ³ μœ μ„±μ„ μœ μ§€ν•˜λ©΄μ„œλ„, 정렬이 κ°€λŠ₯ν•˜λ„λ‘ μ‹œκ°„ 기반의 μš”μ†Œλ₯Ό ν¬ν•¨ν•˜λŠ” 것이 ν•΅μ‹¬μž…λ‹ˆλ‹€.

βœ…1️⃣ μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄ 방식이 ν•„μš”ν•œ 이유.

  • 기쑴의 PK 생성 λ°©μ‹μ—λŠ” λͺ‡ 가지 문제점이 μžˆμŠ΅λ‹ˆλ‹€.

❌1️⃣ UUID의 문제점.

  • UUIDλŠ” 좩돌 없이 μœ λ‹ˆν¬ν•˜μ§€λ§Œ, 정렬이 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.
  • IDκ°€ λžœλ€ν•˜κ²Œ μƒμ„±λ˜κΈ° λ•Œλ¬Έμ— 졜근 데이터 정렬이 μ–΄λ ΅μŠ΅λ‹ˆλ‹€.
  • λ¬Έμžμ—΄ 길이가 κΈΈκ³  인덱싱 μ„±λŠ₯ μ €ν•˜ κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€.

❌2️⃣ AUTO_INCREMENT(숫자 증가 방식)의 문제점.

  • μ—¬λŸ¬ μ„œλ²„(λΆ„μ‚° ν™˜κ²½)μ—μ„œ μ‚¬μš©ν•˜λ©΄ 쀑볡 λ°œμƒ κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€.
  • IDκ°€ 예츑 κ°€λŠ₯ν•˜μ—¬ λ³΄μ•ˆμ— 취약점이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

βœ… μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄ 방식이 ν•΄κ²°ν•˜λŠ” 문제.

  • UUID처럼 κ³ μœ ν•˜μ§€λ§Œ, μ‹œκ°„μˆœμœΌλ‘œ 정렬이 κ°€λŠ₯ν•©λ‹ˆλ‹€.
  • μ„œλ²„κ°€ μ—¬λŸ¬ κ°œμ—¬λ„ μ€‘λ³΅λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ (λΆ„μ‚° ν™˜κ²½μ—μ„œλ„ μ•ˆμ •μ ).
  • IDκ°€ μΌμ •ν•œ νŒ¨ν„΄μ„ κ°€μ§€λ―€λ‘œ, 인덱싱 μ„±λŠ₯이 더 μ’‹μŠ΅λ‹ˆλ‹€.

βœ…2️⃣ μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄ 방식 μ’…λ₯˜

μ „λž΅ 예제 길이 νŠΉμ§•
KSUID 0ujsswThIGTUYm2K8FjOOfXtY1K 27자 UUID보닀 짧고, μ‹œκ°„ μ •λ ¬ κ°€λŠ₯
ULID 01F8MECHZX3TBXYN5RRTG1X3J6 26자 UUID보닀 짧고, 생성 μˆœμ„œλŒ€λ‘œ μ •λ ¬ κ°€λŠ₯
Sonyflake 404168231342172160 19자 Snowflake와 μœ μ‚¬, λΉ λ₯Έ 속도
Short UUID + Timestamp 20250131235959-8f9c7d3a κ°€λ³€ λ‚ μ§œ 기반으둜 μ •λ ¬ κ°€λŠ₯
  • 각 방식은 μ‹œκ°„ 정보가 ν¬ν•¨λ˜μ–΄ μƒμ„±λœ μˆœμ„œλŒ€λ‘œ 정렬이 κ°€λŠ₯ν•˜λ„λ‘ μ„€κ³„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

βœ…3️⃣ μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄ λ°©μ‹μ˜ 예제 μ½”λ“œ.

πŸ› οΈ1️⃣ ULID(Universally Unique Lexicographically Sortable Identifier)

  • ULIDλŠ” UUID보닀 짧고, 생성 μˆœμ„œλŒ€λ‘œ 정렬이 κ°€λŠ₯ν•œ ID λ°©μ‹μž…λ‹ˆλ‹€.

πŸ“Œ ULID νŠΉμ§•.

  • UUID보닀 10자 정도 짧음 (26자)
  • μƒμ„±λœ μˆœμ„œλŒ€λ‘œ μ •λ ¬ κ°€λŠ₯ (μ‹œκ°„ 정보 포함)
  • URL-safe (νŠΉμˆ˜λ¬Έμžκ°€ μ—†μŒ)
  • λŒ€μ†Œλ¬Έμž ꡬ뢄 (Base32 μ‚¬μš©)

πŸ“ Spring Bootμ—μ„œ ULID μ‚¬μš©ν•˜κΈ°.

  • ULIDλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ ulid 라이브러리λ₯Ό μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

1️⃣ Maven λ˜λŠ” Gradle에 라이브러리 μΆ”κ°€.

<dependency>
    <groupId>de.huxhorn.sulky</groupId>
    <artifactId>de.huxhorn.sulky.ulid</artifactId>
    <version>8.3.0</version>
</dependency>

2️⃣ ULID 기반 ID 생성.

import de.huxhorn.sulky.ulid.ULID;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@Entity
@Table(name = "article")
public class Article {
    @Id
    @Column(name = "article_id", updatable = false, nullable = false, length = 26)
    private String articleId;

    private String title;
    private String content;

    @PrePersist
    public void generateId() {
        ULID ulid = new ULID();
        this.articleId = ulid.nextULID(); // ULID 생성
    }
}

βœ… ULID의 μ£Όμš” μž₯점.

  • nextULID()λ₯Ό ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄, μ •λ ¬ κ°€λŠ₯ν•œ 고유 IDκ°€ 생성됨.
  • μ‹œκ°„ 정렬이 κ°€λŠ₯ν•˜μ—¬ ORDER BY article_id ASC둜 졜근 데이터λ₯Ό μ‰½κ²Œ 쑰회 κ°€λŠ₯.

πŸ› οΈ2️⃣ KSUID(K-Sortable Unique ID)

  • KSUIDλŠ” ULID와 λΉ„μŠ·ν•˜μ§€λ§Œ, 더 κΈ΄ ID(27자)λ₯Ό μ‚¬μš©ν•˜λ©° UUID보닀 정렬이 μ‰½μŠ΅λ‹ˆλ‹€.

πŸ“Œ KSUID의 νŠΉμ§•.

  • μ‹œκ°„ 기반 μ •λ ¬ κΈ°λŠ₯.
  • UUID보닀 짧고 읽기 쉬움.
  • κ³ μœ μ„±μ΄ 보μž₯됨,

πŸ“ Spring Bootμ—μ„œ KSUID 적용.

1️⃣ Maven λ˜λŠ” Gradle에 라이브러리 μΆ”κ°€.

<dependency>
    <groupId>com.github.ksuid</groupId>
    <artifactId>ksuid</artifactId>
    <version>1.0.0</version>
</dependency>

2️⃣ KSUID 기반 ID 생성.

import com.github.ksuid.Ksuid;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@Entity
@Table(name = "article")
public class Article {
    @Id
    @Column(name = "article_id", updatable = false, nullable = false, length = 27)
    private String articleId;

    private String title;
    private String content;

    @PrePersist
    public void generateId() {
        this.articleId = Ksuid.newKsuid().toString(); // KSUID 생성
    }
}

βœ… KSUID의 μ£Όμš” μž₯점

  • newKsuid().toString()을 ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄, μ •λ ¬ κ°€λŠ₯ν•œ 고유 IDκ°€ 생성됨.
  • UUID보닀 짧고, 더 빠름.
  • 정렬이 κ°€λŠ₯ν•˜μ—¬ 졜근 데이터 μ‘°νšŒκ°€ 쉽닀.

πŸ› οΈ3️⃣ Short UUID + Timestamp (μ»€μŠ€ν…€ 방식)

  • λ§Œμ•½ 직접 UUIDλ₯Ό 짧게 λ³€ν˜•ν•˜κ³ , μ‹œκ°„ 정보와 μ‘°ν•©ν•˜μ—¬ κ³ μœ ν•œ μ •λ ¬ λ¬Έμžμ—΄μ„ λ§Œλ“€ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

πŸ“Œ Short UUID + Timestamp의 νŠΉμ§•.

  • λ‚ μ§œ 기반으둜 μ •λ ¬ κ°€λŠ₯
  • UUID보닀 짧고, 가독성이 μ’‹μŒ
  • νŠΉμ • λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ— 맞게 μ»€μŠ€ν…€ κ°€λŠ₯

πŸ“ Javaμ—μ„œ Short UUID + Timestamp 적용

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

public class CustomIdGenerator {

    public static String generateId() {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String shortUUID = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
        return timestamp + "-" + shortUUID;
    }

    public static void main(String[] args) {
        System.out.println(generateId()); // 예: 20250201093045-8f9c7d3a
    }
}

βœ… 이 λ°©μ‹μ˜ μž₯점

  • μ •λ ¬ κΈ°λŠ₯ (λ‚ μ§œ 기반)
  • UUID보닀 짧고 가독성이 μ’‹μŒ
  • UUID의 좩돌 방지 κΈ°λŠ₯을 μœ μ§€

βœ…4️⃣ μœ λ‹ˆν¬ μ •λ ¬ λ¬Έμžμ—΄ λ°©μ‹μ˜ 비ꡐ

μ „λž΅ 예제 길이 νŠΉμ§•
ULID 01F8MECHZX3TBXYN5RRTG1X3J6 26자 UUID보닀 짧고, 생성 μˆœμ„œλŒ€λ‘œ μ •λ ¬ κ°€λŠ₯
KSUID 0ujsswThIGTUYm2K8FjOOfXtY1K 27자 UUID보닀 짧고, μ‹œκ°„ μ •λ ¬ κ°€λŠ₯
Short UUID + Timestame 20250201093045-8f9c7d3a κ°€λ³€ λ‚ μ§œ 기반으둜 μ •λ ¬ κ°€λŠ₯

βœ…5️⃣ κ²°λ‘ .

  • UUIDλŠ” 정렬이 μ–΄λ ΅κ³  κΈΈλ‹€.
  • ULID, KSUID 같은 방식은 μœ λ‹ˆν¬ν•˜λ©΄μ„œλ„ μƒμ„±λœ μˆœμ„œλŒ€λ‘œ μ •λ ¬ κ°€λŠ₯.
  • Short UUID + TimestampλŠ” 직접 μ»€μŠ€ν…€ν•˜μ—¬ μ‚¬μš©ν•  μˆ˜λ„ 있음.