Redis에서 대용량 객체 삭제가 느린 이유가 궁금하다면, 먼저 관련 개요 문서를 참고해 보세요.
Redis는 단일 스레드로 동작하기 때문에 수백만 개의 멤버를 가진 큰 정렬 집합(Sorted Set)을 DEL 명령으로 한 번에 삭제하면 서버 전체가 블로킹되어 다른 모든 클라이언트의 요청 처리가 멈출 수 있습니다. 이를 방지하려면 아래와 같은 점진적 삭제 전략을 사용해야 합니다.
삭제 절차
키 이름 변경(Rename): 먼저 해당 키를 고유한 네임스페이스를 가진 새 키로 변경합니다. 이렇게 하면 다른 Redis 클라이언트 입장에서는 원래 키가 즉시 "삭제된" 것처럼 보입니다.
점진적 배치 삭제: 정렬 집합의 멤버를 작은 단위(예: 100개씩)로 나누어 반복적으로 삭제하고, 집합이 완전히 비워질 때까지 진행합니다. 삭제 명령의 크기를 제한함으로써 서버가 오랫동안 블로킹되지 않도록 보장할 수 있습니다.
주의: 아래 코드는 Redis 연결 실패 상황을 우아하게 처리하지 않습니다. Redis 명령 실행 중 예외가 발생하면 남은 잔여 데이터를 직접 수동으로 정리해야 합니다.
의사 코드(Pseudo-code)
# 키 이름 변경
newkey = "gc:hashes:" + redis.INCR("gc:index")
redis.RENAME("my.zset.key", newkey)
# 100개씩 배치로 멤버 삭제
while redis.ZCARD(newkey) > 0
redis.ZREMRANGEBYRANK(newkey, 0, 99)
end
Ruby 구현 예제
$redis = Redis.new
def delete_sorted_set(key)
# 키 이름 변경
newkey = "gc:zsets:#{$redis.incr("gc:index")}"
$redis.rename(key, newkey)
# 100개씩 배치로 멤버 삭제
while $redis.zcard(newkey) > 0
$redis.zremrangebyrank(newkey, 0, 99)
end
end
# 사용 예:
#
# delete_sorted_set("my.large.set")
백그라운드 잡 활용
대용량 삭제 작업은 시간이 걸릴 수 있으므로, 애플리케이션의 응답성을 유지하려면 백그라운드 잡으로 실행하는 것이 좋습니다. Ruby 진영에서 널리 쓰이는 다음 잡 큐 라이브러리들을 활용한 구현 예제를 참고할 수 있습니다.
- Resque — Redis 기반 백그라운드 잡 큐
- Sidekiq — 고성능 스레드 기반 잡 프로세서
이 방식의 핵심은 ZREMRANGEBYRANK를 이용해 한 번에 소량의 멤버만 삭제하므로, 각 명령이 마이크로초~밀리초 수준으로 빠르게 완료된다는 점입니다. 그 결과 서버 지연 없이 수백만 개의 멤버도 안전하게 제거할 수 있습니다.
← "Redis에서 대용량 객체 삭제하기"로 돌아가기