Home > DB > ๐Ÿ’พ[Database] Primary Key ์ƒ์„ฑ ์ „๋žต - ์œ ๋‹ˆํฌ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์ž

๐Ÿ’พ[Database] Primary Key ์ƒ์„ฑ ์ „๋žต - ์œ ๋‹ˆํฌ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์ž
Database Primary Key DBMS

โ€œ๐Ÿ’พ[Database] Primary Key ์ƒ์„ฑ ์ „๋žต - ์œ ๋‹ˆํฌ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์žโ€

๐ŸŽ Intro.

  • PK(Primary Key)๋ฅผ ๋‹จ์ˆœํ•œ AUTO_INCREMENT๊ฐ€ ์•„๋‹Œ, ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด(UUID) ๋˜๋Š” ์ˆซ์ž(Snowflake, Nano ID ๋“ฑ)๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ์ด ๋ฐฉ์‹์€ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ, ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ ์ฒ˜๋ฆฌ, ๋ณด์•ˆ์„ฑ ๊ฐ•ํ™” ๋“ฑ์˜ ์ด์œ ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

โœ…1๏ธโƒฃ โ€œ์œ ๋‹ˆํฌ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์žโ€ PK ๋ฐฉ์‹์ด ํ•„์š”ํ•œ ์ด์œ .

  • ๊ธฐ์กด์˜ AUTO_INCREMENT PK๋Š” ๋‹จ์ˆœํžˆ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์ด๋ฏ€๋กœ:
    • 1. ๋ณด์•ˆ์— ์ทจ์•ฝ โžž ๊ณต๊ฒฉ์ž๊ฐ€ ID๋ฅผ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ์Œ(/articles/1, /articles/2 โ€ฆ).
    • 2. ๋‹ค์ค‘ ์„œ๋ฒ„(์ƒค๋”ฉ) ํ™˜๊ฒฝ์—์„œ ์ถฉ๋Œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ โžž ๊ฐ ์„œ๋ฒ„์—์„œ ๋…๋ฆฝ์ ์ธ ID ์ƒ์„ฑ์ด ์–ด๋ ค์›€.
    • 3. ๋ฐ์ดํ„ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ โžž ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฎ๊ธธ ๋•Œ ID๊ฐ€ ๊ฒน์น  ์ˆ˜ ์žˆ์Œ.
      • ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, UUID, Snowflake, Nano ID ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์ž๋กœ PK๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

โœ…2๏ธโƒฃ ์œ ๋‹ˆํฌ ๋ฌธ์ž์—ด ๋˜๋Š” ์ˆซ์ž๋ฅผ ์ด์šฉํ•œ PK ์ƒ์„ฑ ์ „๋žต.

์ „๋žต ์˜ˆ์ œ ๊ธธ์ด ํŠน์ง•
UUID(Universally Unique Identifier) 550e8400-e29b-41d4-a716-446655440000 36์ž ์ „ ์„ธ๊ณ„์ ์œผ๋กœ ์œ ์ผํ•œ ๋ฌธ์ž์—ด, ์†๋„๊ฐ€ ๋Š๋ฆด ์ˆ˜ ์žˆ์Œ
Snowflake(Twitter ID ๋ฐฉ์‹) 146789123456789012 19์ž ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ, ๊ณ ์œ ์„ฑ ๋ณด์žฅ, ์„ฑ๋Šฅ ์ตœ์ ํ™”
Nano ID V1StGXR8_Z5jdHi6B-myT 21์ž ๋žœ๋ค ๋ฌธ์ž์—ด, URL-safe, ์งง๊ณ  ์ถฉ๋Œ ํ™•๋ฅ ์ด ๋‚ฎ์Œ
KSUID(K-Sortable Unique ID) 0ujsswThIGTUYm2K8FjOOfXtY1K 27์ž ์‹œ๊ฐ„ ์ •๋ ฌ ๊ฐ€๋Šฅ, UUID๋ณด๋‹ค ์งง๊ณ  ์ฝ๊ธฐ ์‰ฌ์›€

โœ…3๏ธโƒฃ UUID(Universally Unique Identifier)

๐Ÿ“Œ UUID๋ž€?

  • 128๋น„ํŠธ์˜ ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด์„ ์ƒ์„ฑํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜.
  • ์ „ ์„ธ๊ณ„์ ์œผ๋กœ ์œ ์ผํ•œ ๊ฐ’์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ.
  • ๋žœ๋ค์„ฑ์ด ๊ฐ•ํ•˜์—ฌ ์ถฉ๋Œ ํ™•๋ฅ ์ด ๋งค์šฐ ๋‚ฎ์Œ.
  • ๊ธธ์ด๊ฐ€ ๊ธธ์–ด(36์ž) ์ธ๋ฑ์‹ฑ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Œ.

๐Ÿ› ๏ธ UUID๋ฅผ PK๋กœ ์‚ฌ์šฉํ•˜๋Š” MySQL ํ…Œ์ด๋ธ” ์˜ˆ์ œ.

CREATE TABLE article (
    article_id CHAR(36) PRIMARY KEY, -- UUID ์‚ฌ์šฉ
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)

๐Ÿ“ Spring Boot JPA์—์„œ UUID ์ ์šฉ.

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;

import java.util.UUID;

@Getter
@NoArgsConstructor
@Entity
@Table(name = "article")
public class Article {
    @Id
    @GeneratedValue(generator = "UUID")
    @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
    @Column(name = "article_id", updatable = false, nullable = false, length = 36)
    private String articleId;
    
    private String title;
    private String content;
}
  • โœ… UUID๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ PK๋กœ ์‚ฌ์šฉ
  • โœ… JPA์—์„œ @GeneratedValue(generator = โ€œUUIDโ€)๋ฅผ ์‚ฌ์šฉ
  • โœ… DB์—๋Š” CHAR(36)๋กœ ์ €์žฅ

โœ…4๏ธโƒฃ Snowflake(Twitter์—์„œ ๊ฐœ๋ฐœํ•œ ๋ถ„์‚ฐ ID)

๐Ÿ“Œ Snowflake๋ž€?

  • Twitter์—์„œ ๊ณ ์œ ํ•œ ์ˆซ์ž ID๋ฅผ ๋น ๋ฅด๊ฒŒ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜.
  • 64๋น„ํŠธ ์ •์ˆ˜ (19์ž๋ฆฌ)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ๊ฐ„ ์ •๋ ฌ ๊ฐ€๋Šฅ.
  • ์„ฑ๋Šฅ ์ตœ์ ํ™” & ID ์ถฉ๋Œ ์—†์Œ.
  • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ(์—ฌ๋Ÿฌ ์„œ๋ฒ„)์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.

๐Ÿ› ๏ธ MySQL ํ…Œ์ด๋ธ” ์˜ˆ์ œ(Snowflake).

CREATE TABLE article (
    article_id BIGINT PRIMARY KEY, -- Snowflake ID ์‚ฌ์šฉ
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

๐Ÿ“ Spring Boot์—์„œ Snowflake ์ ์šฉ.

import java.time.Instant;

public class Snowflake {
    private static final Long EPOCH = 1640995200000L; // 2022-01-01 ๊ธฐ์ค€
    private static final Long MACHINE_ID = 1L;
    private static final Long SEQUENCE_BITS = 12L;
    
    private Long lastTimestamp = -1L;
    private Long sequence = 0L;
    
    public synchronized Long nextId() {
        Long timestamp = Instant.now().toEpochMilli();
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & ((1 << SEQUENCE_BITS) - 1);
            if (sequence == 0) {
                while (timestamp <= lastTimestamp) {
                    timestamp = Instant.now().toEpochMilli();
                }
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        return ((timestamp - EPOCH)) << (64 - 41) | (MACHINE_ID << (64 - 41 - 10)) | sequence;
    }
}
  • โœ… UUID๋ณด๋‹ค ์งง์€ ์ˆซ์ž(BIGINT)๋กœ ๊ณ ์œ ํ•œ ID ์ƒ์„ฑ.
  • โœ… ๋‚ ์งœ ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ ์ •๋ ฌ ๊ฐ€๋Šฅ (์ตœ๊ทผ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹œ ์œ ๋ฆฌ).
  • โœ… ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์—ฌ๋„ ์ค‘๋ณต๋˜์ง€ ์•Š์Œ.

โœ…5๏ธโƒฃ Nano ID (๊ฐ€๋ณ๊ณ  ์งง์€ ๋žœ๋ค ๋ฌธ์ž์—ด)

๐Ÿ“Œ Nano ID๋ž€?

  • UUID๋ณด๋‹ค ์งง๊ณ  ์„ฑ๋Šฅ์ด ๋น ๋ฅธ ID ์ƒ์„ฑ๊ธฐ.
  • URL-safe (URL์— ์‚ฌ์šฉ ๊ฐ€๋Šฅ).
  • ๋žœ๋ค์„ฑ์œผ๋กœ ์ธํ•ด ์ถฉ๋Œ ํ™•๋ฅ ์ด ๋‚ฎ์Œ.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.

๐Ÿ› ๏ธ MySQL ํ…Œ์ด๋ธ” ์˜ˆ์ œ (Nano ID)

CREATE TABLE article (
    article_id VARCHAR(21) PRIMARY KEY, -- Nano ID ์‚ฌ์šฉ
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL.
    created_at TIME
)
  • VARCHAR(21) โžž Nano ID๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 21์ž ๊ธธ์ด๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ ํ…Œ์ด๋ธ”์—์„œ๋„ VARCHAR(21)๋กœ ์„ค์ •.

๐Ÿ“ Spring Boot์—์„œ Nano ID ์ ์šฉ.

  • Java์—์„œ๋Š” Nano ID ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(com.aventrix.jnanoid)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Nano ID๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ Maven ๋˜๋Š” Gradle์— Nano ID ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

  • Maven ์‚ฌ์šฉ ์‹œ
<dependency>
    <groupId>com.aventrix.jnanoid</groupId>
    <artifactId>jnanoid</artifactId>
    <version>2.0.0</version>
</dependency>
  • Gradle ์‚ฌ์šฉ ์‹œ
implementation 'com.aventrix.jnanoid:jnanoid:2.0.0'

2๏ธโƒฃ Entity์—์„œ Nano ID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Primary Key ์„ค์ •.

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;

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

    private String title;
    private String content;

    @PrePersist
    public void generateId() {
        this.articleId = NanoIdUtils.randomNanoId(); // Nano ID ์ƒ์„ฑ
    }
}

โœ… Nano ID ๋ฐฉ์‹์˜ ์žฅ์ .

  • 1. UUID๋ณด๋‹ค ์งง์Œ(21์ž) โžž ์„ฑ๋Šฅ ์ตœ์ ํ™”
  • 2. ๋žœ๋คํ•œ ๊ฐ’์ด๋ฏ€๋กœ ๋ณด์•ˆ์„ฑ ์šฐ์ˆ˜ (ID ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅ)
  • 3. URL-safe โžž URL์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • 4. ์„œ๋ฒ„ ๋ถ€๋‹ด์ด ์ ๊ณ , ๋น ๋ฅธ ์†๋„๋กœ ์ƒ์„ฑ ๊ฐ€๋Šฅ

โŒ Nano ID ๋ฐฉ์‹์˜ ๋‹จ์ .

  • 1. UUID๋ณด๋‹ค ์ถฉ๋Œ ํ™•๋ฅ ์ด ๋†’์ง€๋งŒ, ์ถฉ๋ถ„ํžˆ ๋‚ฎ์€ ์ˆ˜์ค€.
  • 2. ์ •๋ ฌ์ด ์–ด๋ ค์›€ โžž ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜์ด ์•„๋Š๋ฏ€๋กœ ์ตœ๊ทผ ๋ฐ์ดํ„ฐ ์ •๋ ฌ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ ์ ํ•ฉํ•˜์ง€ ์•Š์Œ.