본문 바로가기
Spring Framework/Spring Boot

Embedded Redis

by 도쿠니 2022. 6. 10.

Redis

  • Remote Dictionary Server
  • Key - Value 기반의 오픈 소스 In - Memory NoSQL 데이터베이스
    • Key - Value 기반으로 쿼리를 따로 작성할 필요 없이 결과를 바로 가져올 수 있습니다.
    • 메모리에서 데이터를 처리하기 때문에 속도가 빠릅니다.
  • 주로 동시성 제어(Lock)나 세션, 캐시 등으로 사용됩니다.

Redis Collection (데이터 구조)

String 가장 일반적인 형태로, key - value 로 저장하는 형태
List Array 형식의 데이터 구조로 List를 사용하면 처음과 끝에 데이터를 넣고 빼는것은 속도가 빠르지만 중간에 데이터를 삽입할 때는 어려움이 있습니다.
Set 순서가 없는 Strings 데이터 집합으로 Sets 에서는 중복된 데이터는 하나로 처리하기 때문에, 중복에 대한 걱정을 할 필요가 없습니다.
Sorted Set 위의 Sets와 같은 구조이지만 Score를 통해 순서를 정할 수 있습니다. Sorted Sets를 사용하면 Leaderboard와 같은 기능을 쉽게 구현할 수 있습니다.
Hashes Key-Value 구조를 여러개 가진 object 타입을 저장하기 좋은 구조입니다.

 

Redis 특징

  • In - Memory 기반
    • 저장 용량은 적지만 빠른 속도를 가지고 있습니다.
    • 휘발성입니다.
  • NoSQL & Cache 솔루션
  • 명시적으로 삭제나 expire(만료)를 설정하지 않으면 데이터는 삭제되지 않습니다.
  • 여러 대의 서버 구성이 가능합니다
    • 마스터 - 슬레이브 구조
    • 1개의 싱글 쓰레드로 수행되기 때문에 서버 하나에 여러개의 Redis 서버를 띄울 수 있습니다.
    • 싱글 쓰레드이기 때문에 동시에 처리할 수 있는 명령어는 하나입니다.
  • 데이터베이스 or Cache로 사용됩니다.

 


Embedded Redis를 이용한 SpinLock 실습

  • Redis 연습하기위해 Embedded Redis로 사용합니다.
  • SpinLock을 활용한 동시성 제어 및 동시성 제어를 AOP를 활용하여 실습하는데 사용하려고 합니다.
    • SpinLock이란 다른 스레드가 lock을 소유하고 있을 경우, 컨텍스트 스위칭을 하지 않고 그 lock이 반환될 때 까지 계속 Spin(루프)를 돌면서 재시도하는 것을 의미합니다.
    • 자세한 내용은 http://itnovice1.blogspot.com/2019/09/spin-lock.html 를 참고합시다.

 

LocalRedis 실행 설정

  • 스프링 부트가 기동하면서 Bean 등록할 때 레디스를 실행하고, 종료되면 Bean을 삭제할 때 레디스를 종료하도록 설정
  • 해당 Bean이 Redis Repository보다 빨리 실행될 수 있도록 패키지 순서를 위쪽으로 해야합니다.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import redis.embedded.RedisServer;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Configuration
public class LocalRedisConfig {

    @Value("${spring.redis.port}")
    private int redisPort;
    
    private RedisServer redisServer;
    
    @PostConstruct
    public void startRedis() {
        redisServer = new RedisServer(redisPort);
        redisServer.start();
    }
    
    @PreDestroy
    public void stopRedis() {
        if (redisServer != null) {
            redisServer.stop();
        }
    }
}

Redis Repository 등록 (Redisson Client 생성)

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedisRepositoryConfig {
    
    @Value("${spring.redis.host}")
    private String redisHost;
    
    @Value("${spring.redis.port}")
    private int redisPort;
    
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + redisHost + ":" + redisPort);
        return Redisson.create(config);
    }
}

Application.yml

spring:
  datasource:
    url: jdbc:h2:mem:test
    username: sa
    password:
    driver-class-name: org.h2.Driver
  h2:
    console:
      enabled: true
  jpa:
    defer-datasource-initialization: true
    database-platform: H2
    hibernate:
      ddl-auto: create-drop
    open-in-view: false
    properties:
      hibernate:
        format_sql: true
        show_sql: true
        jdbc:
          batch_size: 100
  redis:
    host: 127.0.0.1
    port: 6379

RedisTestService

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class RedisTestService {

    private final RedissonClient redissonClient;

    public String getLock() {
        // Lock 가져오기
        RLock lock = redissonClient.getLock("sampleLock");

        try {
            // lock이 사용중이면 1초 동안 Lock을 기다리고 lock을 얻으면 5초 동안 Lock을 가지고 있다가 해제
            // 명시적으로 unlock을 안하면 5초동안 가지고 있습니다.
            boolean isLock = lock.tryLock(1, 5, TimeUnit.SECONDS);

            if (!isLock) {
                log.error("================== Lock acquisition failed =================");
                return "Lock failed";
            }

        } catch (Exception e) {
            log.error("Redis lock failed");
        }

        return "Lock Success";
    }
}

TestController

private final RedisTestService redisTestService;


@GetMapping("/get-lock")
public String getLock() {
    return redisTestService.getLock();
}

실행 결과

// 처음 시도 시
// 5초간 해당 Lock 잠김
get lock success


// 5초안에 다시 시도할 경우
// 1초동안 Lock이 풀릴 때까지 기다리다 시간이 초과되면 메세지 출력됌
Lock failed

 

 

 

출처

https://velog.io/@rlaghwns1995/Redis-%EA%B8%B0%EB%B3%B8%EC%A0%95%EB%A6%AC

https://zero-base.co.kr/

'Spring Framework > Spring Boot' 카테고리의 다른 글

커스텀 properties 등록하기  (0) 2022.06.08

댓글