Redis는 인메모리 데이터 저장소로 읽기 성능이 뛰어나 캐싱, 세션 저장소, RefreshToken 저장소 등 다양한 곳에 많이 사용됩니다.
인메모리 데이터 저장소가 가지는 휘발성의 특성 때문에 종료되는 경우 데이터가 유실되게 됩니다. 이 단점을 Redis에서는 AOF와 RDB 라는 방식으로 백업을 하는 기능을 제공합니다.
AOF와 RDB는 각각 장단점이 있으니 비교하면서 상황에 적절한 방식을 사용할 수 있습니다.
저는 Docker-compose와 Spring 애플리케이션에서 Redis를 사용하고 있습니다.
AOF와 RDB의 차이를 알아보고 실제 어떻게 설정하여 적용하는지 알아봅니다.
현재 글은 제가 사용하고 있는 Redis 7.0.5 버전 중심으로 작성됩니다.
특정한 시점의 스냅샷으로 백업하는 방식입니다. 비교적 작은 사이즈의 파일로 백업하며 로딩속도가 빠릅니다. 그리고 단일 파일이기 때문에 원거리 데이터 센터 또는 Amazon S3로 전송하기 용이 합니다.
특정 조건이 만족되면 현재의 Redis 상태를 스냅샷으로 백업하므로 스냅샷이 찍히기 전에 Redis가 종료되면 그 사이의 데이터는 복원할 수 없습니다.
아무런 설정을 하지 않았을 때에는 RDB의 스냅샷 조건이 다음과 같이 지정되어 있습니다.
RDB를 사용하지 않을 것이라면 config set save “” 명령을 통해 조건을 삭제 해줍니다.
레디스 종료 전 자동으로 스냅샷을 저장해줍니다.
레디스 재시작 시 RDB의 스냅샷을 통해 복원합니다.
스냅샷은 기본적으로 dump.rdb에 저장됩니다.
복원된 모습, 재시작 후 아무런 쓰기 작업을 하지 않았음에도 스냅샷으로 인해 복원이 됩니다.
모든 쓰기 명령에 대한 로그를 남깁니다.
AOF는 쓰기 명령에 대해 추가하며 기록되기 때문에 파일 사이즈가 커집니다. 사이즈가 크기 때문에 서버 시작시 로딩속도가 느립니다.
용량이 특정 사이즈보다 크게 되면 rewrite를 통해 사이즈를 줄입니다.
AOF가 활성화 되었다면 Redis의 실행 시 RDB 파일을 읽어오지 않고 AOF파일을 읽어옵니다.
명령 취소 테스트
공식 문서의 설명에 따르면 AOF 방식은 명령에 대한 로그를 통해 복원하는 방식이므로 실수로 데이터를 모두 날리는 flushall 명령을 실행하더라도 aof파일에서 그 명령어만 삭제해주면 데이터를 복원할 수 있다고 합니다. 실제로 어떤지 눈으로 확인 해보겠습니다.
redis에 key가 1,2로 두 개가 저장하고 flushall 명령을 통해 모든 키를 제거 해봤습니다.
편집기를 사용해 aof파일을 열어 flushall 명령어 로그를 삭제하고 재시작 해보겠습니다.
재시작 후 AOF파일을 읽었다는 로그를 확인할 수 있습니다. key 역시 복원된 것을 확인할 수 있었습니다.
AOF 파일이 특정 시점에 특정 사이즈가 넘어 간다면 Redis의 현재 메모리 정보를 다시 AOF 파일에 쓰게 됩니다.
예를들어 key 1에 대한 수정 작업이 100번 일어났다면 rewrite 후에는 현재 메모리에 저장되어 있는 key 1의 상태만 AOF파일에 저장됩니다.
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 설정을 위해 redis.conf라는 이름으로 파일을 만들어줍니다. 저는 /etc/redis/redis.conf 라는 경로에 생성했지만 나중에 볼륨으로 추가할 것이기 때문에 어느 곳에 작성해도 문제 없어보입니다.
sudo vim /etc/redis/redis.conf
appendonly yes #aof를 사용합니다.
requirepass 1234 #redis의 비밀번호를 설정합니다.
그 외 추가적인 설정은 입맛대로 추가하면 될 것 같습니다.
docker compose를 통해 레디스를 사용하고 있습니다. 볼륨 설정을 통해 로컬의 config 파일을 도커에서 사용할 수 있도록 해줍니다.
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 ${비밀번호} 를 통해 인증을 해주어야합니다.
기존에는 단순히 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를 사용할 수 있고 레디스 서버가 비정상적으로 종료가 되어도 데이터 손실을 최소화 할 수 있습니다.