Redis에서 DEL 명령으로 대용량 객체를 삭제하면 해당 키에 담긴 모든 데이터를 한 번에 정리해야 하므로 서버가 블로킹(blocking) 상태에 빠지고 응답 지연이 발생할 수 있습니다. Redis에서 대용량 객체 삭제가 느린 이유에 대한 자세한 내용은 관련 개요 문서를 참고하세요.
Redis에서 대용량 해시를 안전하게 삭제하려면 아래 두 단계를 따르는 것이 좋습니다.
-
키 이름 변경(Rename): 먼저 해당 키를 고유한 네임스페이스를 가진 새 키로 이름을 변경합니다. 이렇게 하면 다른 Redis 클라이언트 입장에서는 해시가 즉시 "삭제된" 것처럼 보입니다.
-
점진적 삭제: 해시가 완전히 비워질 때까지 필드를 작은 배치(batch) 단위로 나누어 조금씩 삭제합니다. 삭제 명령 하나의 크기를 제한함으로써 서버가 오랫동안 블로킹되는 상황을 방지할 수 있습니다.
주의 사항
아래 예제 코드는 Redis 연결 실패 등의 장애 상황을 세밀하게 처리하지 않습니다. 실행 중 Redis 명령이 실패하여 예외가 발생하면, 남겨진 임시 키는 직접 수동으로 정리해 주어야 합니다.
의사 코드(Pseudo-code)
# 키 이름 변경
newkey = "gc:hashes:" + redis.INCR("gc:index")
redis.RENAME("my.hash.key", newkey)
# 해시 필드를 100개씩 배치로 삭제
cursor = 0
loop
cursor, hash_keys = redis.HSCAN(newkey, cursor, "COUNT", 100)
if hash_keys count > 0
redis.HDEL(newkey, hash_keys)
end
if cursor == 0
break
end
end
Ruby 구현 예제
$redis = Redis.new
def delete_hash(key)
# 키 이름 변경
newkey = "gc:hashes:#{$redis.incr("gc:index")}"
$redis.rename(key, newkey)
# 해시 필드를 100개씩 배치로 삭제
cursor = "0"
loop do
cursor, fields = $redis.hscan(newkey, cursor, count: 100)
hkeys = fields.map { |pair| pair[0] }
$redis.hdel(newkey, hkeys) if hkeys.size > 0
break if cursor == "0"
end
end
# 사용 예:
#
# delete_hash("my.large.hash")
백그라운드 잡으로 처리하기
대용량 해시 삭제 작업은 시간이 다소 걸릴 수 있으므로, 요청 처리 흐름과 분리하여 백그라운드 잡(background job)으로 실행하는 것이 좋습니다. Ruby 진영에서는 아래와 같은 잡 큐 라이브러리를 활용해 위 로직을 손쉽게 구현할 수 있습니다.
- Resque
- Sidekiq
참고: Redis 4.0 이상이라면 UNLINK 활용
Redis 4.0부터 도입된 UNLINK 명령을 사용하면 키 삭제 작업을 백그라운드 스레드에서 비동기적으로 수행할 수 있어, 별도의 배치 삭제 로직 없이도 블로킹 없이 대용량 키를 제거할 수 있습니다. 다만 클러스터 환경이나 삭제 속도를 세밀하게 조절해야 하는 경우에는 여전히 위의 배치 삭제 방식이 유용합니다.
← "Redis에서 대용량 객체 삭제하기"로 돌아가기