Redis에서 사용하는 언어
MySQL이 SQL을 실행하듯, Redis에서는 Lua Script를 실행한다.
Redis에서는
@Transactional을 사용할 수 없다. (@Transactional은 RDB를 기준으로 작동)
@Service
@RequiredArgsConstructor
public class UnsafeStockService {
private final StringRedisTemplate redisTemplate;
public boolean decrease(
Long productId
) {
String key = "stock:" + productId;
String value = redisTemplate.opsForValue().get(key);
int stock = Integer.parseInt(value);
if (stock <= 0) {
return false;
}
// 조회 이후 다른 요청이 재고를 차감했어도, 읽어 둔 stock 값은 그대로입니다.
redisTemplate.opsForValue().decrement(key);
return true;
}
}
@Service
@RequiredArgsConstructor
public class StockService {
// 재고를 확인한 뒤 차감하는 과정을 Redis 안에서 한 번에 실행합니다.
private static final DefaultRedisScript<Long> DECREASE_SCRIPT = new DefaultRedisScript<>("""
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock <= 0 then
return 0
end
redis.call('DECR', KEYS[1])
return 1
""", Long.class);
private final StringRedisTemplate redisTemplate;
public boolean decrease(
Long productId
) {
String key = "stock:" + productId;
// key는 스크립트의 KEYS[1]로 전달됩니다.
Long result = redisTemplate.execute(DECREASE_SCRIPT, List.of(key));
// 스크립트가 반환한 1은 차감 성공, 0은 재고 부족입니다.
return result == 1L;
}
}

DB 단에서 실행되는 코드는 원자적이다.
MySQL에서 하나의 SQL 쿼리는 원자적으로 처리되고, Redis에서 하나의 Lua Script는 원자적으로 처리된다.