Redis의 백업(RDB, AOF) 알아보기

박진형·2022년 10월 26일
2

Redis는 인메모리 데이터 저장소로 읽기 성능이 뛰어나 캐싱, 세션 저장소, RefreshToken 저장소 등 다양한 곳에 많이 사용됩니다.

인메모리 데이터 저장소가 가지는 휘발성의 특성 때문에 종료되는 경우 데이터가 유실되게 됩니다. 이 단점을 Redis에서는 AOF와 RDB 라는 방식으로 백업을 하는 기능을 제공합니다.

AOF와 RDB는 각각 장단점이 있으니 비교하면서 상황에 적절한 방식을 사용할 수 있습니다.

저는 Docker-compose와 Spring 애플리케이션에서 Redis를 사용하고 있습니다.
AOF와 RDB의 차이를 알아보고 실제 어떻게 설정하여 적용하는지 알아봅니다.

현재 글은 제가 사용하고 있는 Redis 7.0.5 버전 중심으로 작성됩니다.

RDB

특정한 시점의 스냅샷으로 백업하는 방식입니다. 비교적 작은 사이즈의 파일로 백업하며 로딩속도가 빠릅니다. 그리고 단일 파일이기 때문에 원거리 데이터 센터 또는 Amazon S3로 전송하기 용이 합니다.

특정 조건이 만족되면 현재의 Redis 상태를 스냅샷으로 백업하므로 스냅샷이 찍히기 전에 Redis가 종료되면 그 사이의 데이터는 복원할 수 없습니다.

RDB 기본 설정

아무런 설정을 하지 않았을 때에는 RDB의 스냅샷 조건이 다음과 같이 지정되어 있습니다.

  • 3600초 안에 1개 이상의 데이터가 변경되면 저장.
  • 300초 안에 100개 이상의 데이터가 변경되면 저장
  • 60초안에 10000개 이상의 데이터가 변경되면 저장

RDB를 사용하지 않을 것이라면 config set save “” 명령을 통해 조건을 삭제 해줍니다.

레디스 종료 전 자동으로 스냅샷을 저장해줍니다.

레디스 재시작 시 RDB의 스냅샷을 통해 복원합니다.

스냅샷은 기본적으로 dump.rdb에 저장됩니다.

복원된 모습, 재시작 후 아무런 쓰기 작업을 하지 않았음에도 스냅샷으로 인해 복원이 됩니다.

AOF

모든 쓰기 명령에 대한 로그를 남깁니다.

AOF는 쓰기 명령에 대해 추가하며 기록되기 때문에 파일 사이즈가 커집니다. 사이즈가 크기 때문에 서버 시작시 로딩속도가 느립니다.

용량이 특정 사이즈보다 크게 되면 rewrite를 통해 사이즈를 줄입니다.

AOF가 활성화 되었다면 Redis의 실행 시 RDB 파일을 읽어오지 않고 AOF파일을 읽어옵니다.

  • 명령 취소 테스트

    공식 문서의 설명에 따르면 AOF 방식은 명령에 대한 로그를 통해 복원하는 방식이므로 실수로 데이터를 모두 날리는 flushall 명령을 실행하더라도 aof파일에서 그 명령어만 삭제해주면 데이터를 복원할 수 있다고 합니다. 실제로 어떤지 눈으로 확인 해보겠습니다.

    redis에 key가 1,2로 두 개가 저장하고 flushall 명령을 통해 모든 키를 제거 해봤습니다.

    편집기를 사용해 aof파일을 열어 flushall 명령어 로그를 삭제하고 재시작 해보겠습니다.

    재시작 후 AOF파일을 읽었다는 로그를 확인할 수 있습니다. key 역시 복원된 것을 확인할 수 있었습니다.

AOF 기본 설정

  • appendonly yes 또는 no : yes는 AOF기능을 사용합니다. no는 사용하지 않습니다.레디스 서버 시작 시 이 값이 yes 일때만 AOF 파일을 읽어들입니다.no 이면 AOF 파일이 있어도 읽어들이지 않습니다.
  • appendfilename "appendonly.aof" : AOF 파일명을 지정합니다.Path는 지정할 수 없습니다. Path는 working directory에 따릅니다.
  • appendfsync : AOF에 기록하는 시점을 정합니다.
    • always : 명령 실행 시 마다 AOF에 기록합니다. 데이터 유실의 염려는 없으나, 성능이 매우 떨어집니다.
    • everysec : 매 초 마다 AOF에 기록합니다. 1초 사이 데이터 유실될 수 있으나, 성능에 거의 영향을 미치지 않으면서 데이터를 보존할 수 있어, 일반적으로 권장합니다. default 값입니다.
    • no : AOF에 기록하는 시점을 OS가 정합니다. 기본적으로 리눅스는 30초 마다 flush하지만 커널의 정확한 조정에 달려있습니다.

rewrite

AOF 파일이 특정 시점에 특정 사이즈가 넘어 간다면 Redis의 현재 메모리 정보를 다시 AOF 파일에 쓰게 됩니다.

예를들어 key 1에 대한 수정 작업이 100번 일어났다면 rewrite 후에는 현재 메모리에 저장되어 있는 key 1의 상태만 AOF파일에 저장됩니다.

AOF rewrite 설정

  • auto-aof-rewrite-percentage 100 : AOF 파일 사이즈가 100% 이상 커지면 rewrite합니다.
    처음에는 레디스 서버가 시작할 시점의 AOF 파일 사이즈를 기준으로 합니다. Rewrite를 이 후에는 rewrite 후 파일 사이즈를 기준으로 계산합니다.

    즉, 이전의 AOF 파일 사이즈가 1MB 였다면 2MB가 되었을 때 rewrite를 합니다.
  • auto-aof-rewrite-min-size 64mb : AOF 파일 사이즈가 64mb 이하면 rewrite를 하지 않습니다.파일이 작을때 rewrite가 자주 발생하는 것을 막아줍니다. 만약 이 설정 값이 0이라면 rewrite를 수행하지 않습니다.

  • rewrite가 발생하면 다음과 같이 base.rdb 파일에 현재 정보를 저장하고 이후의 새로운 쓰기 명령들은 incr.aof파일에 기록됩니다.

    auto-aof-rewrite-min-size를 5000으로 설정하고 쓰기명령의 반복을 통해 aof파일의 용량을 늘려봅니다.

    용량이 5000이 넘어가면 현재 상태가 base.rdb에 저장되고 incr.aof 파일이 초기화 되었습니다.
    용량을 확인해보면 매우 compact해진 것을 확인할 수 있습니다.

좀 더 자세한 설명은..

레디스의 공식 문서에서 더 세세한 설명을 볼 수 있습니다.

Redis persistence

Redis AOF 설정 해보기 (Spring & Docker)

redis.conf 작성

redis 설정을 위해 redis.conf라는 이름으로 파일을 만들어줍니다. 저는 /etc/redis/redis.conf 라는 경로에 생성했지만 나중에 볼륨으로 추가할 것이기 때문에 어느 곳에 작성해도 문제 없어보입니다.

sudo vim /etc/redis/redis.conf
  • redis.conf
  appendonly yes #aof를 사용합니다.

  requirepass 1234 #redis의 비밀번호를 설정합니다.

그 외 추가적인 설정은 입맛대로 추가하면 될 것 같습니다.

docker-compose.yml 볼륨/커맨드 설정 추가

docker compose를 통해 레디스를 사용하고 있습니다. 볼륨 설정을 통해 로컬의 config 파일을 도커에서 사용할 수 있도록 해줍니다.

  • volumes:
    • ${volume_name}:${mount를 원하는 파일의 path}
pjh_redis:
  image: redis
  container_name: pjh_redis
  ports:
    - 6379:6379
  volumes:
    - redis.conf:/etc/redis/redis.conf
  command: redis-server redis.conf

비밀 번호를 설정하게 되면 redis-cli 에서는 auth ${비밀번호} 를 통해 인증을 해주어야합니다.

RedisConfig password 설정

기존에는 단순히 host, port만 지정해줬는데 이렇게 한다면 보안상 좋지 않습니다.
그리고 redis.conf를 작성하여 사용하면 별도의 인증이 필요하게 되어서 패스워드를 지정해주었습니다.

application.yaml

spring:
	redis:
	  host: ${REDIS_HOST:localhost}
	  port: ${REDIS_PORT:6379}
	  password: ${REDIS_PASSWORD:1234}

RedisConfigProperties.java

@ConstructorBinding
@ConfigurationProperties(prefix = "spring.redis")
public record RedisConfigProperties(
	String host,
	int port,
	String password
) {
}

RedisConfig

@Configuration
@EnableConfigurationProperties({RedisConfigProperties.class})
@EnableRedisRepositories
public class RedisConfig {

	private final RedisConfigProperties redisConfigProperties;

	public RedisConfig(RedisConfigProperties redisConfigProperties) {
		this.redisConfigProperties = redisConfigProperties;
	}

	@Bean
	public RedisConnectionFactory redisConnectionFactory() {
		RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisConfigProperties.host(), redisConfigProperties.port());
		redisStandaloneConfiguration.setPassword(RedisPassword.of(redisConfigProperties.password()));
		return new LettuceConnectionFactory(redisStandaloneConfiguration);
	}

	@Bean
	public RedisTemplate<String, String> redisTemplate() {
		RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new StringRedisSerializer());
		redisTemplate.setConnectionFactory(redisConnectionFactory());
		redisTemplate.setEnableTransactionSupport(true);

		return redisTemplate;
	}
}

이제 Redis AOF를 사용할 수 있고 레디스 서버가 비정상적으로 종료가 되어도 데이터 손실을 최소화 할 수 있습니다.

0개의 댓글