Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Redis에서 대용량 해시(Hash) 안전하게 삭제하는 방법

Redis에서 DEL 명령으로 대용량 객체를 삭제하면 해당 키에 담긴 모든 데이터를 한 번에 정리해야 하므로 서버가 블로킹(blocking) 상태에 빠지고 응답 지연이 발생할 수 있습니다. Redis에서 대용량 객체 삭제가 느린 이유에 대한 자세한 내용은 관련 개요 문서를 참고하세요.

Redis에서 대용량 해시를 안전하게 삭제하려면 아래 두 단계를 따르는 것이 좋습니다.

  1. 키 이름 변경(Rename): 먼저 해당 키를 고유한 네임스페이스를 가진 새 키로 이름을 변경합니다. 이렇게 하면 다른 Redis 클라이언트 입장에서는 해시가 즉시 "삭제된" 것처럼 보입니다.

  2. 점진적 삭제: 해시가 완전히 비워질 때까지 필드를 작은 배치(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에서 대용량 객체 삭제하기"로 돌아가기