๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ์˜ ๋™์‹œ์„ฑ ๋ฌธ์ œ

1. ๊ฐœ์š” 

๋ฒŒ์จ ๋™์‹œ์„ฑ ๊ด€๋ จ ํฌ์Šคํ„ฐ๋งŒ 4๊ฐœ์งธ์ด๋‹ค...

 

์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„  MySQL Lock์„ ๊ฑธ์–ด ๋™์‹œ์„ฑ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ–ˆ์—ˆ๋Š”๋ฐ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์•Œ๊ฒŒ๋˜์–ด ์†Œ๊ฐœํ•˜๊ณ ์ž ํ•œ๋‹ค.  

 

๋‹จ์ผ ํ™˜๊ฒฝ์—์„  DB์— ๋น„๊ด€์  ๋ฝ ๋“ฑ์„ ์ด์šฉํ•˜์—ฌ ๋™์‹œ์„ฑ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์—ฌ๋Ÿฌ ๋Œ€์˜ DB๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ถ„์‚ฐ DB ํ™˜๊ฒฝ์—์„œ๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋‹ค.

 

์ด๋•Œ ๋ถ„์‚ฐ ๋ฝ์„ ํ™œ์šฉํ•ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค. Redis๋Š” ๋ถ„์‚ฐ๋ฝ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ด๋‹ค. 

 

 

 

 

2. ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚  ๊ฒฝ์šฐ

๋‹ค์Œ์€ ์žฌ๊ณ  ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ๊ณ  ์žฌ๊ณ  ๊ฐ์†Œ๋ฅผ ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.  

 

 

- Entity

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Stock {
    @Id
    @GeneratedValue
    @Column(name = "stock_id")
    private Long id;

    private Long quantity;

    public void decrease(Long quantity) {
        if (this.quantity - quantity < 0) {
            throw new RuntimeException("error");
        }

        this.quantity -= quantity;
    }
}

 

 

- ์„œ๋น„์Šค ์ฝ”๋“œ

@Service
@RequiredArgsConstructor
@Transactional
public class StockService {
    private final StockRepository stockRepository;

    public void decrease(Long stockId, Long val) {
        Stock stock = stockRepository.findById(stockId).get();
        stock.decrease(val);
    }
}

 

 

- ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@SpringBootTest
public class StockTest {
    @Autowired
    private StockRepository stockRepository;
    @Autowired
    private StockService stockService;

    @BeforeEach
    public void insert() {
        Stock stock = new Stock(1L, 100L);
        stockRepository.saveAndFlush(stock);
    }

    @AfterEach
    public void delete() {
        stockRepository.deleteAll();
    }

    @Test
    public void decrease_test() {
        stockService.decrease(1L, 1L);

        Stock stock = stockRepository.findById(1L).orElseThrow();
        // 100 - 1 = 99

        assertEquals(99, stock.getQuantity());
    }
}

 

์ฒ˜์Œ ์ˆ˜๋Ÿ‰์€ 100๊ฐœ์˜€๊ณ  1๊ฐœ๋ฅผ ๊ฐ์†Œ์‹œ์ผœ ๊ฒฐ๊ณผ๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์žฌ๊ณ  ๊ฐ์†Œ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ–ˆ๋‹ค. 

๋ฌด๋ฆฌ ์—†์ด ํ†ต๊ณผํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

ํ•˜์ง€๋งŒ ์ด ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์€ ๋ฌด์—‡์ผ๊นŒ? 

 

์š”์ฒญ์ด ๋™์‹œ์— ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋“ค์–ด์˜จ๋‹ค๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•˜๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

- ๋™์‹œ์„ฑ ํ…Œ์ŠคํŠธ

@Test
public void ๋™์‹œ์—_100๋ช…์ด_์ฃผ๋ฌธ() throws InterruptedException {
    int threadCount = 100;
    ExecutorService executorService = Executors.newFixedThreadPool(32);
    CountDownLatch latch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {
        executorService.submit(() -> {
            try {
                stockService.decrease(1L, 1L);
            } finally {
                latch.countDown();
            }
        });
    }

    latch.await();

    Stock stock = stockRepository.findById(1L).orElseThrow();

    // 100 - (100 * 1) = 0
    assertEquals(0, stock.getQuantity());
}

 

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด ExecutorService๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. newFixedThreadPool(32)๋Š” ์ตœ๋Œ€ 32๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ€์ง„ ์Šค๋ ˆ๋“œ ํ’€์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ๋ฅผ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

for ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ 100๊ฐœ์˜ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค. 

 

CountDownLatch๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 100๊ฐœ ์š”์ฒญ์ด ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋„๋ก ํ•˜์˜€๋‹ค. 

 

์˜ˆ์ƒ๋˜๋Š” ๋ฐ”๋Š” 100๋ฒˆ ๊ฐ์†Œ๋ฅผ ์‹œํ‚ค๋‹ˆ๊นŒ 0์ด ๋˜์•ผํ•œ๋‹ค. 

 

ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

 

์œ„์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š” ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ๋™์‹œ์„ฑ ๋ฌธ์ œ ๋•Œ๋ฌธ์ด๋‹ค. 

 

๋ฌธ์ œ๋Š” Stock ์—”ํ‹ฐํ‹ฐ์˜ decrease() ๋ฉ”์„œ๋“œ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค.

ํ˜„์žฌ ๊ตฌํ˜„์—์„œ๋Š” ์žฌ๊ณ  ์ˆ˜๋Ÿ‰์„ ๊ฐ์†Œ์‹œํ‚ค๊ธฐ ์ „์— ์ˆ˜๋Ÿ‰์ด ์Œ์ˆ˜๊ฐ€ ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์Œ์ˆ˜๊ฐ€ ๋˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ, ์ฒซ ๋ฒˆ์งธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ˆ˜๋Ÿ‰์„ ํ™•์ธํ•œ ํ›„ ๊ฐ์†Œํ•˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ˆ˜๋Ÿ‰์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

 

 

 

 

3. ์ง์ ‘ Update ์ฟผ๋ฆฌ๋ฌธ์„ ๋‚ ๋ ค ํ•ด๊ฒฐ

update ํ•˜๋Š” ๊ณผ์ •์„ ์ž˜ ๋ถ„์„ํ•ด ๋ณด๋ฉด JPA์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚˜๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋ณ€๊ฒฝ ๊ฐ์ง€์˜ ๊ฒฝ์šฐ ๋ฐ”๋กœ๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ปค๋ฐ‹ ํ•˜๋Š” ์‹œ์ ์— flush๊ฐ€ ์ผ์–ด๋‚˜ ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ์•„๊ฐ€๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ select์™€ update ํ•˜๋Š” ์‚ฌ์ด์— ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋™์‹œ์— select ํ•˜๊ณ  update ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐฑ์‹ค ๋ถ„์‹ค์ด ์ผ์–ด๋‚œ๋‹ค.

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ํฌ๊ธฐํ•˜๊ณ  ์ง์ ‘ update ๋ฌธ์„ ๋‚ ๋ ค์ฃผ์–ด์„œ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถฐ์ค„ ์ˆ˜๋„ ์žˆ๋‹ค. update ๋ฌธ์˜ ๊ฒฝ์šฐ x lock์ด ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ถ€๋ถ„์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์ด ์ผ์–ด๋‚˜ ๋™์‹œ์„ฑ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

 

 

@Modifying
@Query("update Stock s set s.quantity = s.quantity - :val where s.id = :stockId")
int decrease(@Param("stockId") Long stockId, @Param("val") Long val);

 

 

 

 

 

 

 

4. synchronized๋ฅผ ํ™œ์šฉํ•œ ํ•ด๊ฒฐ

- ์„œ๋น„์Šค ์ฝ”๋“œ

@Service
@RequiredArgsConstructor
@Transactional
public class StockService {
    private final StockRepository stockRepository;

    public synchronized void decrease(Long stockId, Long val) {
        Stock stock = stockRepository.findById(stockId).get();
        stock.decrease(val);
    }
}

 

 

ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค. ์ด์œ ๋Š” ๋ญ˜๊นŒ?

 

 

๊ทธ ์ด์œ ๋Š” ์Šคํ”„๋ง์˜ @Transactional ๋™์ž‘ ๋ฐฉ์‹ ๋•Œ๋ฌธ์ด๋‹ค.

 

์Šคํ”„๋ง์€ @Transactional์ด ๋ถ™์€ ๊ฒฝ์šฐ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•œ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์€ decrease ๋ฉ”์„œ๋“œ์— ์ ์šฉ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ๋กœ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋Š” decrease ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ์—์„œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์‹คํ–‰๋˜๊ฒŒ ๋œ๋‹ค.

 

์ฆ‰, ๋ฉ”์„œ๋“œ ์ž์ฒด์— ๋Œ€ํ•ด์„œ๋Š” ๋™๊ธฐํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•œ๋‹ค๋ฉด ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๋Š” decrease ๋ณ€๊ฒฝ์ด ๋ฐ˜์˜๋˜๊ธฐ ์ „ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

 

 

 

5. ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค Lock์„ ํ™œ์šฉํ•œ ํ•ด๊ฒฐ

Lock์— ๋Œ€ํ•ด์„  ์˜ˆ์ „์— ์ž์„ธํžˆ ๋‹ค๋ฃฌ ํฌ์Šคํ„ฐ๊ฐ€ ์žˆ์–ด ๋งํฌ ๋‚จ๊ฒจ๋‘๊ฒ ๋‹ค

 

 

 

 

 

6. Redis๋ฅผ ํ™œ์šฉํ•œ ํ•ด๊ฒฐ

Redis๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด MySQL์˜ Lock์„ ๊ฑธ์–ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์—์„œ๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

1. Lettuce ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ด์šฉ

- setnx ๋ช…๋ น์–ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ถ„์‚ฐ ๋ฝ ๊ตฌํ˜„ (spin lock ๋ฐฉ์‹) 

- retry ๋กœ์ง์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ตฌํ˜„ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. 

 

SETNX ๋ช…๋ น์–ด๋Š” "SET if Not eXists"์˜ ์•ฝ์ž๋กœ, ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ๋งŒ ํŠน์ • ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ช…๋ น์–ด์ด๋‹ค.

 

์ด ๋ฐฉ์‹์€ mysql์„ ์‚ฌ์šฉํ•  ๋•Œ์˜ NamedLock๊ณผ ๋น„์Šทํ•œ ๋ฐฉ์‹์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

๋จผ์ € ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ ˆ๋””์Šค ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ ๋‹ค. 

@Component
@RequiredArgsConstructor
public class RedisLockRepository {

    private final RedisTemplate<String, String> redisTemplate;

    public Boolean lock(Long key) {
        return redisTemplate
                .opsForValue()
                .setIfAbsent(generateKey(key), "lock", Duration.ofMillis(3_000));
    }

    public Boolean unlock(Long key) {
        return redisTemplate.delete(generateKey(key));
    }

    private String generateKey(Long key) {
        return key.toString();
    }
}

 

 

- Facade ํด๋ž˜์Šค ์ƒ์„ฑ

@Component
@RequiredArgsConstructor
public class LettuceLockStockFacade {

    private final RedisLockRepository redisLockRepository;

    private final StockService stockService;

    public void decrease(Long key, Long quantity) throws InterruptedException {
        while (!redisLockRepository.lock(key)) {
            Thread.sleep(10);
        }

        try {
            stockService.decrease(key, quantity);
        } finally {
            redisLockRepository.unlock(key);
        }
    }
}

 

 

 

 

2. Redisson ์‚ฌ์šฉ

Redisson์€ pub-sub ๊ธฐ๋ฐ˜์˜ lock ๊ตฌํ˜„์„ ์ œ๊ณตํ•œ๋‹ค.

pub-sub ๊ธฐ๋ฐ˜์€ ์ฑ„๋„์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  lock์„ ์ ์œ ์ค‘์ธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋‹ค์Œ lock์„ ์ ์œ ํ•˜๋ ค๋Š” ์“ฐ๋ ˆ๋“œ์—๊ฒŒ ์ ์œ ๊ฐ€ ๋๋‚ฌ์Œ์„ ์•Œ๋ ค์ฃผ๋ฉด์„œ lock์„ ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ฐฉ์‹์ด๋‹ค.

 

 

 

- ์˜์กด์„ฑ ์ถ”๊ฐ€

dependencies {
    // Redisson
    implementation 'org.redisson:redisson-spring-boot-starter:3.17.4'
}

 

 

- Facade ํด๋ž˜์Šค ์ƒ์„ฑ

@Component
@RequiredArgsConstructor
public class RedissonLockStockFacade {

    private final RedissonClient redissonClient;

    private final StockService stockService;

    public void decrease(Long key, Long quantity) {
        RLock lock = redissonClient.getLock(key.toString());

        try {
            boolean available = lock.tryLock(10, 1, TimeUnit.SECONDS);

            if (!available) {
                System.out.println("lock ํš๋“ ์‹คํŒจ");
                return;
            }

            stockService.decrease(key, quantity);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

 

 

Lettuce ์™€ ๋‹ค๋ฅด๊ฒŒ Redisson ์€ ๊ณ„์† ๋ฝ ํš๋“์„ ๊ณ„์† ์‹œ๋„ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Redis์˜ ๋ถ€ํ•˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

 

 

 

 

7. ๊ฒฐ๋ก 

์ฒ˜์Œ ์ด ํฌ์Šคํ„ฐ๋ฅผ ์ž‘์„ฑํ•œ ์ด์œ ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ง์ ‘ Lock์„ ๊ฑฐ๋Š” ๊ฒƒ ๋ณด๋‹ค ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ Redis๋ฅผ ์‚ฌ์šฉํ•ด์„œ Lock์„ ์ฒ˜๋ฆฌํ•ด ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ค๊ณ ์ž ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

ํ•˜์ง€๋งŒ ๋‹จ์ผ์„œ๋ฒ„, ๋‹จ์ผDB๋ฅผ ์‚ฌ์šฉํ•  ๋• Redis์˜ ์„ฑ๋Šฅ์ด ๋” ๋‚˜์˜ค์ง€ ์•Š์•˜๋‹ค.

 

Redis๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Lock์„ ๊ฑธ์–ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์„ ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜์˜€๊ณ , TTL์„ ์„ค์ •ํ•ด ๋ฐ๋“œ๋ฝ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

 

๋‚˜๋Š” ๋‹จ์ผ ์„œ๋ฒ„ & ๋‹จ์ผ DB๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๊ณ , ์ถ”ํ›„์— ์„œ๋ฒ„๊ฐ€ ํ™•์žฅ๋œ๋‹ค๋ฉด ๊ทธ๋•Œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•ด๋„ ํฐ ๋ฌธ์ œ ์—†์„ ๊ฒƒ ๊ฐ™์•„ ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ MySQL Lock์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.