๐Ÿ”’ Redis ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ ์ž ๊ธˆ(Distributed Lock): ๋™์‹œ์„ฑ ๋ฌธ์ œ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊นŒ?

์„ํ˜„ยท2025๋…„ 3์›” 10์ผ

Insight

๋ชฉ๋ก ๋ณด๊ธฐ
27/43
post-thumbnail

๐Ÿค” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์™œ ์ž ๊ธˆ์ด ํ•„์š”ํ• ๊นŒ?

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์„œ๋ฒ„๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ์ž์›(๋ฐ์ดํ„ฐ)์„ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์–ด๋–ค ์ผ์ด ๋ฒŒ์–ด์งˆ๊นŒ์š”? ์˜ˆ๋ฅผ ๋“ค์–ด, ์‡ผํ•‘๋ชฐ์—์„œ ํ•œ์ • ์ˆ˜๋Ÿ‰์˜ ์ƒํ’ˆ์„ ํŒ๋งคํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ์— ๊ตฌ๋งค ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ๊ฐ™์€ ์ƒํ’ˆ์ด ์ค‘๋ณต์œผ๋กœ ํŒ๋งค๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋งŒ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž ๊ธˆ์„ ๊ฑธ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ํ•ด๊ฒฐ์ฑ…: ๋ถ„์‚ฐ ์ž ๊ธˆ(Distributed Lock)

๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹จ์ˆœํ•œ Mutex๋‚˜ Java์˜ synchronized ํ‚ค์›Œ๋“œ๋กœ ๋™๊ธฐํ™”๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์„œ๋ฒ„์—์„œ ๋™์ผํ•œ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ์ œ์–ดํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . ์ด๋•Œ Redis๋ฅผ ์ด์šฉํ•œ ๋ถ„์‚ฐ ์ž ๊ธˆ(Distributed Lock)์„ ์ ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ ์„œ๋ฒ„๊ฐ€ ๋™์ผํ•œ ์ž์›์— ๋™์‹œ์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿš€ Redis๋ฅผ ์ด์šฉํ•œ ๊ธฐ๋ณธ์ ์ธ ๋ถ„์‚ฐ ์ž ๊ธˆ ๊ตฌํ˜„

Redis์˜ SET ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•œ ๋ถ„์‚ฐ ์ž ๊ธˆ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SET resource_lock unique_identifier NX PX 5000
  • NX (Not Exists): ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ๋งŒ ๊ฐ’์„ ์„ค์ •
  • PX 5000: TTL(Time To Live)์„ 5์ดˆ(5000ms)๋กœ ์„ค์ •ํ•˜์—ฌ ์ž๋™์œผ๋กœ ์ž ๊ธˆ ํ•ด์ œ

์ด ๋ฐฉ์‹์˜ ํ•ต์‹ฌ์€ ์„œ๋ฒ„ ์ค‘ ํ•˜๋‚˜๋งŒ **resource_lock**์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๐Ÿ”„ Java ์ฝ”๋“œ ์˜ˆ์ œ

import redis.clients.jedis.Jedis;

public class RedisLockExample {
    private static final String LOCK_KEY = "resource_lock";
    private static final int LOCK_EXPIRE_TIME = 5000; // 5์ดˆ

    public static boolean acquireLock(Jedis jedis, String lockValue) {
        String result = jedis.set(LOCK_KEY, lockValue, "NX", "PX", LOCK_EXPIRE_TIME);
        return "OK".equals(result);
    }

    public static void releaseLock(Jedis jedis, String lockValue) {
        String lock = jedis.get(LOCK_KEY);
        if (lockValue.equals(lock)) {
            jedis.del(LOCK_KEY);
        }
    }

    public static void main(String[] args) {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            String lockValue = "unique_id_123";
            if (acquireLock(jedis, lockValue)) {
                System.out.println("๐Ÿ”’ Lock ํš๋“ ์„ฑ๊ณต!");
                // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
                releaseLock(jedis, lockValue);
                System.out.println("๐Ÿ”“ Lock ํ•ด์ œ ์™„๋ฃŒ!");
            } else {
                System.out.println("๐Ÿšซ Lock ํš๋“ ์‹คํŒจ!");
            }
        }
    }
}

์ด ๋ฐฉ์‹์€ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, Redis ๋งˆ์Šคํ„ฐ ์žฅ์• (Failover)๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž ๊ธˆ์ด ์œ ์‹ค๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ๊ฐ€ ๋‹ค์šด๋˜๋ฉด ์ƒˆ๋กญ๊ฒŒ ์Šน๊ฒฉ๋œ ๋ ˆํ”Œ๋ฆฌ์นด ๋…ธ๋“œ์—์„œ๋Š” ๊ธฐ์กด์˜ ์ž ๊ธˆ ์ •๋ณด๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.


๐Ÿ”ด Redis ๊ธฐ๋ณธ ๋ฝ์˜ ๋ฌธ์ œ์ 

์œ„ ๋ฐฉ์‹์€ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ Failover ์‹œ ์ž ๊ธˆ ์œ ์‹ค ๊ฐ€๋Šฅ์„ฑ

  • Redis ๋งˆ์Šคํ„ฐ๊ฐ€ ์žฅ์• ๋กœ ์ธํ•ด ๋ ˆํ”Œ๋ฆฌ์นด๋กœ ์ „ํ™˜๋  ๊ฒฝ์šฐ, ๊ธฐ์กด์˜ ๋ฝ ์ •๋ณด๊ฐ€ ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ
  • ์ƒˆ ๋งˆ์Šคํ„ฐ์—์„œ๋Š” ์ด์ „ ๋ฝ์„ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ

2๏ธโƒฃ TTL(TIME TO LIVE) ๋ฌธ์ œ

  • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž‘์—…์„ ์™„๋ฃŒํ•˜์ง€ ๋ชปํ–ˆ๋Š”๋ฐ TTL์ด ๋งŒ๋ฃŒ๋˜๋ฉด ์ž ๊ธˆ์ด ํ•ด์ œ๋จ
  • ๋ฐ˜๋Œ€๋กœ, ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋น„์ •์ƒ ์ข…๋ฃŒ๋˜๋ฉด TTL์ด ๋งŒ๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์„œ๋ฒ„๊ฐ€ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•จ

๐Ÿ”ฅ RedLock: ๋” ์•ˆ์ „ํ•œ ๋ถ„์‚ฐ ์ž ๊ธˆ ์•Œ๊ณ ๋ฆฌ์ฆ˜

์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด RedLock์ด๋ผ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. RedLock์€ Redis์˜ ์—ฌ๋Ÿฌ ๋…ธ๋“œ์— ๊ฑธ์ณ ๊ณผ๋ฐ˜์ˆ˜ ์ด์ƒ์˜ ๋…ธ๋“œ์—์„œ ์ž ๊ธˆ์„ ํš๋“ํ•ด์•ผ๋งŒ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ— RedLock ๋ฐฉ์‹์˜ ํ•ต์‹ฌ ์›๋ฆฌ

1๏ธโƒฃ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ Redis ๋…ธ๋“œ๋ฅผ ์šด์˜ (์˜ˆ: 5๊ฐœ)
2๏ธโƒฃ ํด๋ผ์ด์–ธํŠธ๋Š” ๊ณผ๋ฐ˜์ˆ˜ ์ด์ƒ์˜ Redis ๋…ธ๋“œ(3๊ฐœ ์ด์ƒ)์—์„œ ๋ฝ์„ ํš๋“ํ•ด์•ผ ํ•จ
3๏ธโƒฃ ๋ชจ๋“  ๋…ธ๋“œ์—์„œ ๊ฐ™์€ TTL์„ ์„ค์ •ํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ์œ ์ง€
4๏ธโƒฃ ๊ณผ๋ฐ˜์ˆ˜์˜ ๋ฝ์ด ์กด์žฌํ•  ๋•Œ๋งŒ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์ž‘์—…์ด ๋๋‚˜๋ฉด ๋ชจ๋“  ๋…ธ๋“œ์—์„œ ๋ฝ์„ ํ•ด์ œ

๐Ÿ“Œ RedLock Java ์ฝ”๋“œ ์˜ˆ์ œ (Lettuce ์‚ฌ์šฉ)

import io.lettuce.core.RedisClient;
import io.lettuce.core.api.sync.RedisCommands;

public class RedisRedLockExample {
    private static final String LOCK_KEY = "resource_lock";
    private static final int LOCK_EXPIRE_TIME = 5000;

    public static boolean acquireLock(RedisCommands<String, String> redis, String lockValue) {
        String result = redis.set(LOCK_KEY, lockValue, "NX", "PX", LOCK_EXPIRE_TIME);
        return "OK".equals(result);
    }

    public static void releaseLock(RedisCommands<String, String> redis, String lockValue) {
        String lock = redis.get(LOCK_KEY);
        if (lockValue.equals(lock)) {
            redis.del(LOCK_KEY);
        }
    }

    public static void main(String[] args) {
        RedisClient redisClient = RedisClient.create("redis://localhost:6379");
        RedisCommands<String, String> redis = redisClient.connect().sync();
        
        String lockValue = "unique_id_123";
        if (acquireLock(redis, lockValue)) {
            System.out.println("๐Ÿ”’ RedLock ํš๋“ ์„ฑ๊ณต!");
            // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
            releaseLock(redis, lockValue);
            System.out.println("๐Ÿ”“ RedLock ํ•ด์ œ ์™„๋ฃŒ!");
        } else {
            System.out.println("๐Ÿšซ RedLock ํš๋“ ์‹คํŒจ!");
        }
        
        redisClient.shutdown();
    }
}

๐Ÿš€ ๋งˆ๋ฌด๋ฆฌ

  • ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ์˜ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด Redis ๊ธฐ๋ฐ˜์˜ ๋ถ„์‚ฐ ๋ฝ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • ๋‹จ์ˆœํ•œ SET NX ๋ฐฉ์‹์€ ๋งˆ์Šคํ„ฐ ์žฅ์•  ์‹œ ์•ˆ์ „ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์•ˆ์ •์ ์ธ ๋ฝ์„ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด RedLock์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Redis๋ฅผ ํ™œ์šฉํ•œ ๋ถ„์‚ฐ ๋ฝ์€ ๊ธˆ์œต ์‹œ์Šคํ…œ, ์˜ˆ์•ฝ ์‹œ์Šคํ…œ, ๊ฒฐ์ œ ์‹œ์Šคํ…œ ๋“ฑ์—์„œ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”— ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ:

๐Ÿ”ฅ ์˜ค๋Š˜๋ถ€ํ„ฐ ์—ฌ๋Ÿฌ๋ถ„์˜ ์„œ๋น„์Šค์—๋„ Redis ๋ฝ์„ ์ ์šฉํ•ด๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”? ๐Ÿš€

0๊ฐœ์˜ ๋Œ“๊ธ€