티스토리 뷰

반응형

Redis에 대한 기초 개념부터 심화 내용(여러 서버 클러스터링)까지 정리해보려 한다. 아래의 내용을 참고하면, 원하는 구현까지는 어렵지 않게 가능하다고 본다. 추가로 spring에서의 연결도 진행해 보자.

 

 

 

노파심에 서두에 적는다. 타 블로그를 보면 레디스 버전 3에 대한 이야기가 많다. 그러나 3 설치하지말고 최신 버전을 설치하자. 관련 내용은 아래에 설치 부분을 참고하자.

 

Redis ( Remote Dictionary Server )

오픈소스로 key-value 기반의 인-메모리 데이터 저장소이다. 따라서 key-value 기반이라 쿼리를 날릴 필요 없이 결과를 바로 가져올 수 있고 디스크에 데이터를 쓰는 구조가 아니라 메모리에서 데이터를 처리하기 때문에 속도가 매우 빠르다. (DB를 조회하는 것보다 빠르다.)

 

유명가수의 티케팅을 웹에서 한다고 가정해보자. 티켓팅이 열리는 순간 엄청난 양의 데이터가 DB에 쓰기 위해 몰리게 된다. 이때 DB 에러가 발생하여 결제가 되지 않는다면 엄청난 손해를 발생할 수 있다. 이때 해결책으로 레디스의 캐시를 사용한다. 하나씩 로직을 보자.

 

Look aside cache(조회 로직)을 보면, 클라이언트가 웹서버에 요청을 하면 웹서버는 먼저 Cache에서 데이터 존재 유무를 확인하다. 이때 Cache에 데이터가 있으면 바로 꺼내주고, 없다면 DB를 확인해서 Cache에 먼저 자장한 후에 클라이언트에게 데이터를 돌려준다.

 

Write Back(저장 로직)을 보면 클라이언트가 웹서버에 요청을 하면, 웹서버는 DB에 바로 저장을 하는 것이 아니라 Cache에 결과를 쓰고 바로 클라이언트에게 결과 값을 리턴해 준다. 그 후에 Cache 서버에 있는 데이터들을 Worker(워커) 서버들이 데이터를 가져와서 작업을 수행하고 DB에 작성한다. 이렇게 되면 DB는 순차적으로 Transaction을 처리할 수 있게 된다. 정리하면, 데이터를 캐시에 먼저 전부 저장해 둔 후에 특정 시점마다 캐시의 데이터를 DB에 insert 하는 방식이다. insert를 1개씩 500번 하는 것보다 500개를 한 번에 insert 하는 빠르고 성능이 좋다는 것을 알 수 있을 것이다. 

 

캐시 서버는 속도를 위해서 주로 메모리를 사용하기 때문에 서버에 장애가 나면, 메모리가 날아가서 데이터 손실이 발생할 수 있다. 그래서 replication(복제 서버)을 구성하거나 disk를 사용해서 고가용성을 확보하기도 하지만, 비용적 문제가 크게 발생하거나 속도가 어느 정도 낮아질 수 있다는 문제점이 생긴다. 아래에서부터는 Cache 서버로 널리 사용되고 있는 redis에 대해서 알아보자.

 

 

 

Redis 설치

기본적으로 레디스는 윈도우와 리눅스 설치로 나뉜다. 리눅스 설치는 매우 쉽다.

최신 버전은 https://redis.io/download로 설치를 진행한다.

 

// root로 들어가서 아래의 C++을 우선 설치하자.
$ yum -y install gcc-c++


// 다운로드 (6.2.4버전을 받았다)
$ wget https://download.redis.io/releases/redis-6.2.4.tar.gz 
// 압축풀기
$ tar xzf redis-6.2.4.tar.gz
// 압축 푼 파일로 들어가기

//+ 필요시 아래와 같이 이름을 변경해도 된다.(생략가능)
$ mv redis-6.2.4 redis (생략가능)

$ cd redis-6.2.4
// 소스 컴파일(= 소스 파일을 실행 가능한 형태로 만들어 준다)
$ make
//  /usr/local/bin에 redis-server, redis-cli 등의 실행파일을 복사한다
$ sudo make install
// redis 서버 실행 (오류 없이 데리스 모양이 뜨면 성공이다.)
$ src/redis-server redis.conf

// 레디스 모양이 뜨면 서버를 닫고 다시 접속을 한다. 그리고 아래의 명령어로 레디스 접속을하자.
$ redis-cli                or               $ src/redis-cli

 

 

만약 C++ 에러 발생 시 centos 환경인 경우는 아래와 같이 명령어를 치자.

 

// centos 버전 확인
$ rpm --query centos-release

// centos가 7.0 이전 버전 인 경우
$ yum -y install jemalloc
// centos가 7.0 이전 버전 인 경우
$ yum -y install epel-release
$ yum -y install varnish

// 이래도 C언어 관련 에러가나면 아래의 명령어를 치자
$ deps
$ make hiredis jemalloc linenoise lua
$ cd ..
$ make install

 

혹시나 path 설정이 안 됐다면 설치한 위치의 src 파일로 아래와 같이 path를 맞춰준다.

 

$ export PATH=$PATH:/설치위치/redis-6.2.4/src

 

 

 

Redis 윈도우 설치

https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100에서 zip이 아닌 msi 파일을 받아서 실행시켜서 설치를 해주면 된다. 매우 간단하다. 그리고 현재 버전 3까지만 나와있다. 높은 버전의 레디스를 사용하기 위해서는 docker를 사용하자. 설치해서 사용해 보면 아래와 같이 서버가 나온다. (설치시 path 관련은 체크해준다.)

 

 

 

 

레디스 설정(redis.conf)

기본적으로 리눅스는 설치한 redis 폴더 안에서 redis.conf 파일을 변경해주면 된다.(자세한 내용은 아래쪽의 설명을 참고)

 

// redis.conf가 있는 위치로 이동하자.
$ sudo vi redis.conf

// 설정을 한 후에 :wq!로 저장한다음 redis 서버를 껐다가 다시 켜 준다.
// 데이터 모두 초기화
127.0.0.1:6379> flushall
OK
// 레디스 서버 종료
127.0.0.1:6379> shutdown
// 서버 실행
$ src/redis_server redis.conf

 

 

윈도우 서버 실행법은 설치한 위치로 들어가서 아래와 같이 하면 된다.

 

C:\Program Files\Redis>redis-server redis.windows.conf
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 3.2.100 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 14500
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

[14500] 19 Jul 13:48:15.546 # Server started, Redis version 3.2.100
[14500] 19 Jul 13:48:15.549 * DB loaded from disk: 0.004 seconds
[14500] 19 Jul 13:48:15.549 * The server is now ready to accept connections on port 6379

 

그리고 cmd 창을 다시 열어주면 된다.

 

 

그리고 window 같은 경우는 아래와 같이 설치한 폴더에 들어가서 redis.conf를 메모장으로 열어서 수정을 해주면 된다. (클러스터 설정은 아래에서 다시 할 예정이다.)

 

 

 

 

외부 포트 허용 설정

> 외부 포트를 열어줘야 기본적으로 접근이 가능하다. (redis.conf)

// 변경전
bind 127.0.0.1 -1::1

// 변경후
bind 0.0.0.0


// 변경 후에는 서버를 위의 내용들을 참고해서 껐다가 다시 켜주면 된다.

 

외부 cmd창을 열고 아래의 명령어를 통해 접속을 해보자.

$ redis-cli -h 127.0.0.1 -p 6379

 

 

포트 변경

> redis.conf를 변경해 준다. port 뒤에 9090을 넣으면 9090 포트로 변경 된다.

port 9090

 

 

암호 설정

> redis.conf에서 requirepass 뒤에 사용하고자 하는 암호를 넣어주면 된다. 

requirepass 암호

사용 시에는 redis 접속 후에 아래와 같이 암호를 치면 접근이 가능하다.

auth 암호

 

 

외부에서 접속

// redis-cli -h 접속ip주소 -p 포트번호
$ redis-cli -h 10.190.140.90 -p 6379

 

 

 

 

클러스터 만들기

> https://redis.io/topics/cluster-tutorial : 공식문서

기본적으로 클러스터의 암호는 연결이 끝난 이후에 연결을 한다.

 

클러스터를 만들기 이전에 기본적인 명령어는 다음과 같다.

 

# 클러스터 정보
$ cluster info
- cluster_state:fail   // 할당이 안되어 있는 fail 상태
- cluster_state:ok   // 할당이 되어있는 상태
- cluster_slots_assigned:16384   // 할당된 슬롯 수
- cluster_known_nodes:6   // 마스터 슬레이브 관계없이 클러스터에 연결된 노드수
- cluster_size:0   // 슬롯이 할당된 마스터 서버 수



# 클러스터 초기화
$ cluster RESET hard

# 클러스터 역할 확인
$ info replication

 

 

 

Redis 구조

 아래와 같이 다양한 데이터 구조를 지원하기 때문에 캐시 데이터 저장, 인증 토큰 저장 등 다양하게 사용 가능하다. 

  • Strings : 기본적인 key-value 매핑 구조
    •  [method] opsForValue : String을 Serializable/Deserialize 해주는 interface
  • Lists : Array 형식의 데이터 구조(처음과 끝의 데이터 변경은 빠르지만 중간 데이터 수정은 어렵다.)
    •  [method] opsForList : List을 Serializable/Deserialize 해주는 interface
  • Sets : 순서가 없는 Stings 데이터 집합으로 중복 데이터는 제거된다.
    •  [method] opsForSet : Set을 Serializable/Deserialize 해주는 interface
  • Sorted Sets : Sets와 같은 구조지만, Score를 통해 순서를 정할 수 있다.
    • [method] opsForZSet : ZSet을 Serializable/Deserialize 해주는 interface
  • Hashes : Strings 구조를 여러 개 포함한 object를 저장하기 좋다.
    •  [method] opsForHash : Hash을 Serializable/Deserialize 해주는 interface

 

 

Strings 명령어 

  • keys * - 현재 key값들을 확인
  • get key - key에 해당하는 value
  • set key value - key에 value 저장
  • del key - key 삭제

 

List(l, r) 명령어

  • lpush key value - List의 index 0(head)으로 데이터 push
  • rpush key value - List의 index last로 데이터 push
  • lpop key - List의 index 0 데이터 pop
  • rpop key - List의 index last 데이터 pop

 

Set(s) 명령어

  • sadd key member - set에 value 하나 추가
  • srem key member - set에서 value 삭제
  • smembers key - set에 속해있는 모든 member 조회
  • scard key - set에 속해있는 member 개수 구함
  • spop key - set에서 무작위로 member 가져옴

 

Sorted Set(z) 명령어

> 가중치(score) 에 따른 정렬된 순서를 가짐

  • zadd key score member 집합에 score, value 추가
  • zcard key 집합에 있는 member 개수 조회
  • zrange key start stop index를 기준으로 start, stop을 범위로 하여 조회
  • zrangebyscore ky min max score를 기준으로 min, max 범위로 하여 조회

 

Hash(h) 명령어

  • hset key field value key에 field와 value 쌍으로 지정
  • hget key field key에서 field로 value 가져옴
  • hdel key field key에서 field 삭제
  • hlen key field의 개수
  • hgetAll key field와 value를 모두 반환
  • hkeys key 모든 field 반환
  • hvals key 모든 value 반환

 

 

expire 명령어

> 지정한 시간 이후 ket를 자동 삭제하는 명령어

  • expire key member second - key에 TTL(Time To Live) 설정
  • ttl key - 남은 ttl을 초단위로 확인

 

encoding 설정 명령어

redis-cli set ker "한글"

 

 

Redis 관리

Redis는 Single threaded로 한 번에 하나의 명령어만 실행 가능하다. 따라서 느리다고 생각할 수 있지만, Get/Set명령어를 초당 10만 개 정도 처리할 정도로 빠르다. 그러나 전체 key를 불어오는 것과 같이 처리시간이 긴 명령어를 실행하면 그 뒤에 Get/Set 명령어들은 타임아웃이 나서 요청 실패할 수도 있다.

 

 

메모리 관리

Redis 서버의 메모리는 maxmemory 값으로 설정 가능하다. 설정한 메모리가 다 차면 max memory policy에 따라서 추가 메모리 확보가 진행된다. 관련 내용은 maxmemory-policy 설정에 대해 공부를 더 해보면 된다.

 

https://brunch.co.kr/@jehovah/20

 

Redis 기본 정리

캐시를 알아야 하는 순간! | 캐시를 접하게 되는 순간 서비스를 처음 운영할 때는 WEB-WAS-DB의 전형적인 3티어 구조를 취하는 편이 보통입니다. 사용자가 몇 명 되지 않는 서비스의 경우에는 3티어

brunch.co.kr

 

 

Redis Replication

Redis는 Master/Slave 구조로 구성하여 읽기 분산과 데이터 이중화를 한다. Master 노드는 read/write를 수행하고 Slave는 Read만 가능하다. 이러한 것이 가능하려면 Slave에서 Master의 데이터를 전부 가지고 있어야 한다. 이렇게 하기 위해서는 Master의 데이터를 복제해서 Slave로 옮기는 작업을 해야 하는데 이를 Replication이라고 한다.

 

 

 

Redis Cluster

Redis Cluster는 failover를 위한 대표적인 구성 방식 중 하나이다. 여러 노드가 Hash 기반의 Slot를 나눠가지고 클러스터를 구성하여 사용하는 방식이다. 전체 slot는 16384이고 hash 알고리즘은 CRC16을 사용한다. key를 CRC16으로 해시한 후에 이를 16384로 나누면 해당 key가 저장될 slot이 결정된다.

 

Cluster는 master 노드로 구성되고 각 노드는 특정 slot range를 가진다. 그리고 데이터를 이중화하기 위해서 slave노드를 가진다고 할 수 있다. 정리하면 하나의 cluster가 여러 master 노드로 구성 가능하고 하나의 master 노드가 여러 slave를 가질 수 있다. 이때 특정 master 노드가 죽으면, 해당 노드의 slave 중 하나가 master로 바뀌어 역할을 수행한다.

 


 

Spring과 Radis 연동

spring 관련 공식문서를 참고해보자.

 

//설치 확인
C:\Users>netstat -an|findstr 6379
  TCP    127.0.0.1:6379         0.0.0.0:0              LISTENING
  
  
C:\Users\aaakc>redis-cli
127.0.0.1:6379> ping
PONG

127.0.0.1:6379> set key value
OK

127.0.0.1:6379> get key
"value"

 

1. Dependency

Gradle project (build.gradle)

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-redis')
}

-------------------------or------------------------------
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}


//	implementation 'org.springframework.session:spring-session-data-redis'

 

Maven project(pom.xml)

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

 

2. Configuration

> config라는 패키지를 만들고 RedisConfig라는 클래스를 만들어서 아래와 같이 코드를 넣어주자.

RedisConfig.class

package com.jsoftware.platform.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.ConfigureRedisAction;

@Configuration
public class RedisConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.redis")
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }

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

    @Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }
}

 

Redis에 Connection 하기 위해 RedisConnectionFactory와 RedisTemplate를 생성해 주자. Redis를 사용할 일이 있다면, RedisTemplate를 사용한다고 할 수 있다. 여기까지 하면 연결을 위한 준비는 끝이 났다고 할 수 있다. 아래는 관련된 코드 설명을 적었다.

 

 

RedisConnectionFactory

  • Redis 서버와의 통신을 위한 low-level 추상화를 제공
  • 설정에 따라서 새로운 RedisConnection 또는 이미 존재하는 RedisConnection을 리턴.
  • RedisConnection 은 Redis 서버와의 통신 추상화를 제공하며, exception 발생 시 Spring DataAccessException으로 전환.
  • RedisConnection 은 binary value를 인자로 받고 결과를 리턴하는 low-level method를 리턴.
Java의 Redis Client는 크게 Jedis와 Lettuce로 2가지로 나눠진다. 
Lettuce가 TPS/CPU/응답속도 등에서 우위를 차지한다.
Jedus에 비해 몇 배 이상의 성능과 하드웨어 자원 절약이 가능하다.

 

RedisTemplate

  • Redis 서버에 Redis Command를 수행하기 위한 high-level 추상화를 제공
  • 오브젝트 serialization과 connection management를 수행
  • Redis 서버에 데이터 CRUD를 위한 Key Type Operations와 Key Bound Operations 인터페이스를 제공
  • 오브젝트 serialization과 connection management를 수행
  • thread-safe 하며, 재 사용이 가능
  • 대부분의 기능에 RedisSerializer 인터페이스를 사용. StringRedisSerializer, JdkSerializationRedisSerializer, JacksonJsonRedisSerializer, Jackson2JsonRedisSerializer, GenericJackson2JsonRedisSerializer, OxmSerializer를 사용할 수 있음.
  • Redis에 저장된 키와 값이 java.lang.String이 되도록 하는 것이 일반적이므로 StringRedisTemplate 확장 기능을 제공.
  • StringRedisSerializer를 사용하며, 저장된 키와 값은 사람이 읽을 수 있음.

 

 

application.properties

spring.redis.host와 prign.redis.port를 지정하지 않을 시 default로 127.0.0.1(로컬 호스트)과 6379(redis default port)로 설정이 된다.

# session redis
spring.session.store-type=redis
spring.redis.host=localhost
spring.redis.password=password
spring.redis.port=6379

 

 

 

RedisController.java

> controller 패키지 내부에 RedisController 클래스를 만들었다.

package com.jsoftware.platform.controller;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {

    private final RedisTemplate<String, String> redisTemplate;

    public RedisController(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @PostMapping("/redisTest")
    public ResponseEntity<?> addRedisKey() {
        ValueOperations<String, String> vop = redisTemplate.opsForValue();
        vop.set("yellow", "banana");
        vop.set("red", "apple");
        vop.set("green", "watermelon");
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @GetMapping("/redisTest/{key}")
    public ResponseEntity<?> getRedisKey(@PathVariable String key) {
        ValueOperations<String, String> vop = redisTemplate.opsForValue();
        String value = vop.get(key);
        return new ResponseEntity<>(value, HttpStatus.OK);
    }
}

configuration에서 만들어 놓은 RedisTemplate를 받고 opsForValue()라는 메서드를 통해 Redis의 값을 넣거나 빼내는 것이 가능하다.

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함