EVAL/EVALSHA로 Lua 스크립트를 서버에서 원자적으로 실행하는 기능WATCH 재시도 없이 원자성 확보EVALSHA로 단일 RTT, 서버 측 처리로 데이터 이동 최소화EVAL script numkeys key [key ...] arg [arg ...]numkeys로 키 개수를 알려주고, 키는 KEYS[n], 나머지 인자는 ARGV[n]로 접근 (1-indexed)EVAL "return redis.call('GET', KEYS[1])" 1 stock:evt_123
SCRIPT LOAD로 미리 캐시에 올리고 해시를 받아 EVALSHA로 실행SCRIPT LOAD "<script>" # → sha1 반환
EVALSHA <sha1> 1 stock:evt_123 3 # numkeys=1, KEYS[1]=stock:evt_123, ARGV[1]=3
NOSCRIPT 에러 → 클라이언트가 EVAL로 폴백SCRIPT EXISTS로 캐시 여부 확인, SCRIPT FLUSH로 캐시 비움KEYS: 스크립트가 접근하는 키, ARGV: 키가 아닌 인자(값·수량 등)KEYS로 넘겨야 함
WATCH·재시도 불필요)-- KEYS[1]=재고 키, ARGV[1]=차감 수량
local left = tonumber(redis.call('GET', KEYS[1]))
if left == nil then return -2 end -- 키 없음
if left < tonumber(ARGV[1]) then return -1 end -- 재고 부족
return redis.call('DECRBY', KEYS[1], ARGV[1]) -- 차감 후 잔량 반환
redis.call(...): 명령 실행, 에러 나면 스크립트 전체가 중단되고 에러 전파redis.pcall(...): 보호 호출, 에러를 Lua 테이블로 반환해 직접 처리 가능lua-time-limit(기본 5초) 초과 시 다른 요청에 BUSY 응답 → 쓰기 전이면 SCRIPT KILL, 아니면 SHUTDOWN NOSAVE로만 회복EVAL_RO/EVALSHA_RO(7.0+)로 복제본에서 실행 가능FUNCTION LOAD로 함수를 등록하는 신형 방식 — 스크립트와 달리 서버에 영속되고 재시작·복제에도 유지EVALSHA처럼 매번 로드/폴백을 신경 쓸 필요가 줄어듦| 기준 | Transaction (WATCH/MULTI) |
Lua Script |
|---|---|---|
| 원자성 확보 | 낙관적 — 감시 키 변경 시 취소 → 재시도 | 스크립트 전체를 단일 실행 단위로 강제 |
| read-modify-write | WATCH로 우회(읽기는 트랜잭션 밖) |
스크립트 안에서 직접 처리 |
| 재시도 | 고경합 시 필요 (retry storm) | 불필요 |
| 읽기 복제본 분산 | 가능 | 쓰기 스크립트는 마스터 전용(불가) |
| 블로킹 | 짧음 | 긴 스크립트는 서버 전체 블로킹 |
| 네트워크 | 명령들 왕복(파이프라인) | EVALSHA로 단일 RTT |
| 가독성·디버깅 | 명령 단위로 명확 | 별도 Lua라 디버깅·테스트 어려움 |
| 롤백 | 없음 | 없음 (중간 실패해도 이전 effect 유지) |
| 적합 상황 | 단순 CAS, 저경합 | 복잡한 다단계 원자 연산, 고경합 |