
์ฌ๋ฌ ๊ฐ์ ์๋ฒ๊ฐ ๋์์ ๊ฐ์ ์์(๋ฐ์ดํฐ)์ ๋ณ๊ฒฝํ๋ ค๊ณ ํ๋ฉด ์ด๋ค ์ผ์ด ๋ฒ์ด์ง๊น์? ์๋ฅผ ๋ค์ด, ์ผํ๋ชฐ์์ ํ์ ์๋์ ์ํ์ ํ๋งคํ๋ ์ํฉ์ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค. ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๋์์ ๊ตฌ๋งค ๋ฒํผ์ ๋๋ฅด๋ฉด, ๊ฐ์ ์ํ์ด ์ค๋ณต์ผ๋ก ํ๋งค๋ ์ ์์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ํ๋์ ํ๋ก์ธ์ค๋ง ์์์ ์ ๊ทผํ ์ ์๋๋ก ์ ๊ธ์ ๊ฑธ์ด์ผ ํฉ๋๋ค.
๋ถ์ฐ ํ๊ฒฝ์์๋ ๋จ์ํ Mutex๋ Java์ synchronized ํค์๋๋ก ๋๊ธฐํ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์ฌ๋ฌ ์๋ฒ์์ ๋์ผํ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๊ฒ์ ์ ์ดํด์ผ ํ๊ธฐ ๋๋ฌธ์ด์ฃ . ์ด๋ Redis๋ฅผ ์ด์ฉํ ๋ถ์ฐ ์ ๊ธ(Distributed Lock)์ ์ ์ฉํ๋ฉด, ์ฌ๋ฌ ์๋ฒ๊ฐ ๋์ผํ ์์์ ๋์์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ์ ์ดํ ์ ์์ต๋๋ค.
Redis์ SET ๋ช
๋ น์ด๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ๋จํ ๋ถ์ฐ ์ ๊ธ์ ๊ตฌํํ ์ ์์ต๋๋ค.
SET resource_lock unique_identifier NX PX 5000
NX (Not Exists): ํค๊ฐ ์กด์ฌํ์ง ์์ ๋๋ง ๊ฐ์ ์ค์ PX 5000: TTL(Time To Live)์ 5์ด(5000ms)๋ก ์ค์ ํ์ฌ ์๋์ผ๋ก ์ ๊ธ ํด์ ์ด ๋ฐฉ์์ ํต์ฌ์ ์๋ฒ ์ค ํ๋๋ง **resource_lock**์ ํ๋ํ ์ ์๋๋ก ๋ณด์ฅํ๋ ๊ฒ์
๋๋ค.
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)๊ฐ ๋ฐ์ํ๋ฉด ์ ๊ธ์ด ์ ์ค๋ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋ง์คํฐ ๋ ธ๋๊ฐ ๋ค์ด๋๋ฉด ์๋กญ๊ฒ ์น๊ฒฉ๋ ๋ ํ๋ฆฌ์นด ๋ ธ๋์์๋ ๊ธฐ์กด์ ์ ๊ธ ์ ๋ณด๋ฅผ ๋ณด์ฅํ ์ ์์ต๋๋ค.
์ ๋ฐฉ์์ ๊ฐ๋จํ์ง๋ง ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
1๏ธโฃ Failover ์ ์ ๊ธ ์ ์ค ๊ฐ๋ฅ์ฑ
2๏ธโฃ TTL(TIME TO LIVE) ๋ฌธ์
์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด RedLock์ด๋ผ๋ ์๊ณ ๋ฆฌ์ฆ์ด ๋ฑ์ฅํ์ต๋๋ค. RedLock์ Redis์ ์ฌ๋ฌ ๋ ธ๋์ ๊ฑธ์ณ ๊ณผ๋ฐ์ ์ด์์ ๋ ธ๋์์ ์ ๊ธ์ ํ๋ํด์ผ๋ง ์์์ ์ ๊ทผํ ์ ์๋๋ก ํฉ๋๋ค.
1๏ธโฃ ์ฌ๋ฌ ๊ฐ์ ๋
๋ฆฝ์ ์ธ Redis ๋
ธ๋๋ฅผ ์ด์ (์: 5๊ฐ)
2๏ธโฃ ํด๋ผ์ด์ธํธ๋ ๊ณผ๋ฐ์ ์ด์์ Redis ๋
ธ๋(3๊ฐ ์ด์)์์ ๋ฝ์ ํ๋ํด์ผ ํจ
3๏ธโฃ ๋ชจ๋ ๋
ธ๋์์ ๊ฐ์ TTL์ ์ค์ ํ์ฌ ์ผ๊ด์ฑ์ ์ ์ง
4๏ธโฃ ๊ณผ๋ฐ์์ ๋ฝ์ด ์กด์ฌํ ๋๋ง ์์
์ ์ํํ๊ณ , ์์
์ด ๋๋๋ฉด ๋ชจ๋ ๋
ธ๋์์ ๋ฝ์ ํด์
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();
}
}
SET NX ๋ฐฉ์์ ๋ง์คํฐ ์ฅ์ ์ ์์ ํ์ง ์์ผ๋ฏ๋ก, ์์ ์ ์ธ ๋ฝ์ ๊ตฌํํ๋ ค๋ฉด RedLock์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.๐ ์ถ๊ฐ ํ์ต ์๋ฃ:
๐ฅ ์ค๋๋ถํฐ ์ฌ๋ฌ๋ถ์ ์๋น์ค์๋ Redis ๋ฝ์ ์ ์ฉํด๋ณด๋ ๊ฑด ์ด๋จ๊น์? ๐