redis 파일 설정

간단하게 레디스의 설정(파일) 에 대해 알아보자.

레디스는 기본적으로 두가지의 방식을 제공한다.
하나는 RDB 또 다른 한개는 AOF 방식이다.
RDB파일 방식은 특정한 간격마다 메모리에 있는 레디스 전체를 디스크에 쓴다. 백업을 받을 시 용이한 방식이다.
AOF 는 Append Only File 약자로 명령이 실행 될때마다 기록되는 방식이다. 데이터 손실의 거의 없으나 성능에 영향을 미친다.

  • RDB
    특정 시점에 데이터를 바이너리 파일로 저정한다. AOF 파일보다 사이즈가 작아 로딩속다가 보다 빠르다.
    시점은 지정하는 파일은 redis.conf의 save라는 파라미터 이다.
    save 조건은 여러 개를 지정 할 수 있고 어느 한 것이라도 만족한다면 실행된다. 만약 저장하지 않으려면 save를 제거 하거나 주석처리하면 된다.
    명령으로도 RDB 파일을 생성할 수 있다.

RDB 관련 설정

stop-writes-on-bgsave-error : yes or no, default yes
이값이 yes일때 레디스는 RDB 파일을 디스크에 저장하다 실패하면 모든 쓰기 요청을 거부한다. 쓰기에 문제가 발생했으니 빨리 조치 하라는 것이다. default는 yes이다. 만약 no 로 설정 했다면 저장에 실패 하더라도 모든 요청을 정상적으로 처리한다.

rdbcompression : yes or no, default yes
RDB 파일을 쓸때 압축 여부를 정한다. 압축률은 그다지 높지 않다고 한다.

dbfilename dump.rdb
RDB 파일명을 지정하나 Path는 지정할 수 없다. Path 는 working directory 에 따른다.

  • AOF
    AOF 파일은 default로 appendonly.aof 파일에 기록 된다. 입력/수정/삭제 명령이 실행 될 때 마다 기록되며 조회는 기록되지 않는다.
    AOF는 계속 추가 기록이 된다. AOF는 계속 추가 되면 파일 사이즈가 계속 커지면 OS파일 사이즈 제한에 걸러서 기록이 중단 혹은 레디스 서버 시작이 로드 시간이 많이 걸린다. 예를들어 set명령으로 같은 key 값으로 5번 수행했다면 5번 모두 남아 있다. 이걸 해결 하고자 rewrite 설정이 있다. 만약 rewrite 를 수행하면 이전 기록은 모두 사라지고 최종 데이터만 기록된다.

AOF 관련 설정

appendonly : yes or no
AOF 기능을 사용하거나 사용하지 않는다. yes일때만 AOF파일을 읽는다.

appendfilename
AOF파일을 지정한다. Path는 RDB와 같다. 지정할 수 없고 기본을 따른다.

appendfsync
세가지의 방법이 있다.
always : 명령을 실행 할때마다 기록된다. 데이터가 손실 되지는 않으나 성능이 떨어진다.
everysec : 1초마다 AOF에 기록된다. 그 사이에 데이터가 유실 될수 있으나 always보다는 성능이 좋고 데이터도 가능한 많이 보존할 수 있으며 일반적으로 권장하는 방법이다.
no : OS 가 알아서 하는 방식이긴 하나 데이터 유실이 큽니다.

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

auto-aof-rewrite-min-size 64mb
AOF 파일 사이즈가 64MB이하면 rewirte 하지 않는다. 파일이 작을때 rewirte가 자주 발생하는 것을 막는다.
만약 0으로 설정 했다며 rewirte를 하지 않는다.

어느것이 나을까?
AOF 는 기본, RDB는 옵션으로 한다.
AOF설정은 everysec AOF rewirte 는 사용한다.
AOF를 사용해도 성능에 거의 영향을 미치지 않는다고 한다. (위엔 성능이 떨어 진다 했는데…)
Master 노드 이고 Auto-restart 기능을 사용할 때는 AOF를 사용하는 것이 안전하다.
Msster 노드가 자동 재시작했을때 만약 AOF를 사용하지 않아서 데이터가 없다면 slave노드들의 데이터도 날리게 된다.

출처 : http://www.redisgate.com/redis/configuration/persistence.php
이 사이트에 좋은 내용이 많은거 같다.

redis command 를 알아보자

레디스에는 다양한 커멘드를 지원한다.
그 중 가장 많이 쓰는 커멘드를 알아 보겠다.

set

일단 제일 기본적인 set 이다. 말그대로 데이터를 입력하는 것이다.

127.0.0.1:6379> set key hi
OK
127.0.0.1:6379> set key1 wonwoo
OK

get

데이터를 넣었으니 가져와야 한다.

127.0.0.1:6379> get key
"hi"
127.0.0.1:6379> get key1
"wonwoo"

del

데이터를 지우는 연산이다.

127.0.0.1:6379> get key
"hi"
127.0.0.1:6379> del key
(integer) 1
127.0.0.1:6379> get key
(nil)

incr

숫자를 증가 시키는 연산이다.

127.0.0.1:6379> set count 10
OK
127.0.0.1:6379> incr count
(integer) 11
127.0.0.1:6379> incr count
(integer) 12

만약 숫자가 아닌 문자를 했을 경우에는 에러가 난다.

incrby

숫자만큼 더해주는 연산이다.

127.0.0.1:6379> set count 100
OK
127.0.0.1:6379> incrby count 40
(integer) 140

lpush

list의 자료구조 연산이다. 왼쪽부터 차례대로 넣는다.

127.0.0.1:6379> lpush mylist hi
(integer) 1
127.0.0.1:6379> lpush mylist wonwoo
(integer) 2
127.0.0.1:6379> lpush mylist hello
(integer) 3
127.0.0.1:6379> lpush mylist world
(integer) 4

lpop

넣었으니 빼야 된다. 여기서 주의할점은 빼는거다 가져오는것이 아니라 정확히는 가져와서 빼는거지…

127.0.0.1:6379> lpop mylist
"world"
127.0.0.1:6379> lpop mylist
"hello"

lrange

list의 값을 읽을수 있는 연산이다. 파라미터 2개가 주어지는데 첫번째는 시작점 두번째는 끝점이다.
만약 -(마이너스) 일경우에는 한바퀴도는 셈이다. 한번 보자

127.0.0.1:6379> lpush mylist hi wonwoo hello world hello
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
3) "hello"
4) "wonwoo"
5) "hi"

127.0.0.1:6379> LRANGE mylist 2 3
1) "hello"
2) "wonwoo"

127.0.0.1:6379> LRANGE mylist 0 -3
1) "hello"
2) "world"
3) "hello"

한번 해보면 더 이해가 갈 것이다.

rpush

lpush와 마찬가지 이지만 자료구조 오른쪽에 넣는 것이다.

127.0.0.1:6379> rpush mylist right
(integer) 6
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
3) "hello"
4) "wonwoo"
5) "hi"
6) "right"

rpop은 있는데 rrange은 없는 듯하다.

sadd

자료 구조 Set으로 이해하면 되겠다. 중복을 허용하지 않는다.

127.0.0.1:6379> sadd myset 1 10 222
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "10"
3) "222"
127.0.0.1:6379> sadd myset 1
(integer) 0

smembers

Set의 데이터를 가져오는 연산이다.

127.0.0.1:6379> smembers myset
1) "1"
2) "10"
3) "222"

spop

Set의 값을 꺼내오는 듯하다. 하지만 Set은 순서를 보장하지 않기에 순서가 자기 마음 대로다.

127.0.0.1:6379> sadd myset 1 2 3
(integer) 3
127.0.0.1:6379> spop myset
"1"
127.0.0.1:6379> spop myset
"3"
127.0.0.1:6379> spop myset
"2"
127.0.0.1:6379> sadd myset 1 2 3
(integer) 3
127.0.0.1:6379> spop myset
"3"
127.0.0.1:6379> spop myset
"2"
127.0.0.1:6379> spop myset
"1"

hmset

해쉬형태의 자료구조이다 key value로 입력가능하다.

127.0.0.1:6379> hmset myhash key1 hello key2 world
OK

hget

해쉬형태의 값을 가져오는 연산이다.

127.0.0.1:6379> hget myhash key1
"hello"

hdel

해쉬값을 지우는 연산이다.

127.0.0.1:6379> hdel myhash key1
(integer) 1
127.0.0.1:6379> hget myhash key1
(nil)

일단 자주 쓰는 듯한 것을 알아 봤다.
더 많이 알아보고 싶다면 여기 자세히 나와 있다.

spring boot redis pub sub

Spring Boot의 redis pub sub을 알아보자.
저번에는 레디스 캐싱에 대해 알아 봤는데..약간 부족한 면이 있다 나중에 좀더 살펴 봐야 겠다.
pub sub 시스템은 채널에 구독 신청을 한 모든 sub에게 메시지를 전달한다.
한마디로 구독한 채널에 대해 메시지를 전달하는 시스템을 말하는거다.

Spring boot를 사용하면 좀더 쉽게 셋팅하고 구현할 수 있다.
일단 레디스가 설치 되어 있어야 한다.
레디스 설치는 인터넷에서 참고하기 바란다.
바로 소스를 보자
일단 maven으로 하기 때문에 pom xml 파일을 보자.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.3</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

일단 boot의 redis를 사용하기 위해 starter-data-redis 와 json형태로 전달하기 위해 jackson을 넣어 두었다.

public class JsonRedisTemplate<V> extends RedisTemplate<String, V> {

  public JsonRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper, Class valueType) {
    RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    super.setKeySerializer(stringSerializer);
    super.setHashKeySerializer(stringSerializer);
    super.setHashValueSerializer(stringSerializer);
    Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(valueType);
    jsonRedisSerializer.setObjectMapper(objectMapper);
    super.setValueSerializer(jsonRedisSerializer);
    super.setConnectionFactory(connectionFactory);
    super.afterPropertiesSet();
  }
}

Json으로 전달을 하기 위해서 JsonRedisTemplate만들어 setValueSerializer에 json Serializer를 셋팅 하였다.

이번엔 redis에 필요한 빈 설정들을 보자

  @Bean
  RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                          MessageListenerAdapter listenerAdapter) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.addMessageListener(listenerAdapter, new PatternTopic("sendMessage"));
    container.addMessageListener(topicMessageListener(), new PatternTopic("sendMessage"));
    return container;
  }

  @Bean
  public TopicMessageListener topicMessageListener() {
    return new TopicMessageListener();
  }

  @Bean
  MessageListenerAdapter listenerAdapter(MethodMessageListener methodMessageListener) {
    return new MessageListenerAdapter(methodMessageListener, "sendMessage");
  }

  @Bean
  MethodMessageListener methodMessageListener() {
    return new MethodMessageListener();
  }

  @Autowired
  private JsonRedisTemplate jsonRedisTemplate;

  @Bean
  public ObjectMapper objectMapper(){
    return new ObjectMapper();
  }

  @Bean
  public JsonRedisTemplate jsonRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper){
    return new JsonRedisTemplate<>(connectionFactory,objectMapper,Object.class);
  }

우리는 두가지 방법으로 설정 해보겠다.
실제 메소드를 이용해보는 방식이 하나 있고 다음은 토픽의 키 값을 이용해 보는 방식으로 해보겠다.
사실은 두개의 인터페이스는 같다.

첫번째에 있는 빈은 메시지 리스너를 등록하는 빈이다. 우리는 두가지의 방법으로 해본다고 했으니 두개를 등록 시켜놨다.
다음에 있는 TopicMessageListener 은 MessageListener 인터페이스를 구현한 구현체이다.
그 다음은 메소드 방식으로 설정 하기 위해 빈으로 등록했다.
어댑터의 첫번째 파라미터는 sub의 클래스이다. 두번째는 메소드 명으로 등록하면 된다.
그리고 아까 만든 JsonRedisTemplate을 빈으로 등록 했다.

메소드 방식의 구현체인 MethodMessageListener 소스이다.

@Slf4j
public class MethodMessageListener {

  public void sendMessage(String message) {
    log.info(message);
  }
}

그냥 간단하게 출력 정도만 한다. 다음으로는 토픽의 키 값으로 하는 방식이다.

@Slf4j
public class TopicMessageListener implements MessageListener {

  private RedisTemplate redisTemplate;

  @Autowired
  public void setRedisTemplate(RedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  @Override
  public void onMessage(Message message, byte[] bytes) {
    byte[] body = message.getBody();
    String str = (String) redisTemplate.getStringSerializer().deserialize(body);
    log.info("key : {}, message : {}", new String(bytes), str);
  }
}

이것또한 출력만 하는 형식으로 만들었다.

아까 본 MessageListenerAdapter 클래스도 MessageListener의 구현체로 만들어져 있다.
그럼 테스트를 해보자.

  @Autowired
  private JsonRedisTemplate jsonRedisTemplate;

  @Test
  public void pubsub() {
    SendMessage sendMessage = new SendMessage();
    sendMessage.setName("wonwoo");
    sendMessage.setEmail("wonwoo@test.com");
    jsonRedisTemplate.convertAndSend("sendMessage", sendMessage);
  }

이렇게 테스트를 해보면 다음과 같이 로그가 남겨질 것이다.

2016-04-23 20:32:25.050  INFO 1151 --- [    container-2] me.wonwoo.redis.MethodMessageListener    : {"name":"wonwoo","email":"wonwoo@test.com"}
2016-04-23 20:32:25.050  INFO 1151 --- [    container-3] me.wonwoo.redis.TopicMessageListener     : key : sendMessage, message : {"name":"wonwoo","email":"wonwoo@test.com"}

MethodMessageListener클래스에서 한번, TopicMessageListener 클래스에서 한번 로그가 출력 될 것이다.

이번엔 redis-cli을 실행 시켜 보자.
접속후에 다음과 같이 입력해보자

SUBSCRIBE sendMessage

그리고 테스트를 다시 돌려보면

"{\"name\":\"wonwoo\",\"email\":\"wonwoo@test.com\"}"

이런 형태의 스트링 Json이 출력 될 것이다.

이상으로 Spring의 redis pub sub를 알아 봤다!
간단하다!
github에 올려 놔야겠당.