캐시 서버 구축을 위해 레디스를 공부하게 됐다.
이전의 noSQL 글은 여기에...
2022.08.02 - [Database] - [NoSQL] NoSQL 입문 / 개념
Redis (Remote Dictionary Server)
레디스는 비정형 데이터(noSQL) 저장 관리 위한 Key-Value 데이터 스토어를 말한다.
Redis의 활용사례
- I/O가 많이 발생하는 데이터 처리할 때
- 실시간 랭킹 형태 데이터
- 조회수 카운트 형태 데이터 (유튜브 조회수 처리)
- 사용자의 세션 관리 (개인화 빅데이터)
- 사용자의 세션 유지, 활동 추적 형태의 데이터
- 좋아요 기능
- 장바구니 기능
- API 캐싱
- IOT 영역
레디스는 인메모리 구조로 I/O가 많이 발생하는 데이터를 처리할때 가장 큰 장점이 된다.
레디스가 없다면 실시간 랭킹 데이터의 경우 몇 초단위로 DB에 order by 요청을 해야한다.
또 조회수 카운트는 조회가 일어날 수록 계속해서 카운트를 업데이트해줘야한다.
이렇게 되면 서버에 큰 무리가 있을 것이다.
그래서 레디스를 이용해 메모리에 저장하고 이를 일정한 주기로 실제 rdb에 저장하며 활용한다고 한다.
데이터 구조
Table | 데이터를 저장하는 논리적 구조 |
Data Set | 테이블을 구성하는 논리적 단위 |
Key | 하나 이상의 조합된 값으로 표현 |
Value | Key에 대한 데이터 값 → 하나 이상의 field / element |
String : 대부분의 데이터는 문자열로 처리 → 숫자, JPEG, Timestamp 등
set <Key><Value>
get <Key>
getset <Key><Value> //존재하는 키에 새 값 추가하고 이전 값 출력
append <Key> <Value> //키가 존재하면 값 뒤에 값 추가
//여러개 한번에 처리
mset <Key1><Value1> <Key2><Value2> <Key3><Value3>
mget <Key1> <Key2> <Key3>
//숫자
incr <Key> //키에 저장된 값 1 증가시킴
decr <Key> //키에 저장된 값 1 감소시킴
incrby <Key><Value> //해당 키에 저장된 숫자에 값만큼 증가시킴
decrby <Key><Value> //해당 키에 저장된 숫자에 값만큼 감소시킴
strlen <Key> //해당 키의 값인 문자열 길이 출력
List : 순서를 가진 데이터 구조 (Head --- tail)
Pub-Sub (생산자-소비자) 패턴에서 많이 사용
lpush <Key><Value>
rpush <Key><Value>
lpop <Key>
rpop <Key>
lrange <Key><Value> //-1은 인덱스의 끝 의미
lindex <Key><Index> //해당 키의 left부터 세서 해당 index의 값 출력
Set → 데이터 존재 여부 확인에서 많이 사용
//set insert
sadd <Key><Value>
smember <Key> //모든 set 값 조회
sismember <Key><Value> // value 존재하면 1, 아니면 0
sinter <Key> <Key> //해당 키에 저장된 값 교집합 출력
scard <Key> //해당 키에 저장된 값들의 개수 출력
spop <Key><Value> //해당 키에 저장된 값 중 임의 값 삭제
smove <Key><NewKey><Value> //값의 이동
Sorted Set: 정렬된 set 데이터 처리
랭킹 처리, 정렬에서 많이 사용
score : 요소의 가중치 ⇒ 요소의 정렬 결정 (default 오름차순)
zadd <Key><Score><Value>
zrange <Key><StartIndex><EndIndex> [withscores] // == score와 같이 조회하라
zrangebyscore <Key><MaxScore><MinScore> [withscores] //score 범위의 값 조회
zrangebyrank <Key><StartRank><EndRank> //rank 범위의 값 조회
zrevrange <Key><StartIndex><EndIndex> //범위에 따라 내림차순으로 정렬 조회
zcount <Key><StartIndex><EndIndex> //해당 범위 해당되는 값의 개수
Hash : 키-값 쌍으로 이뤄진 데이터 (자바의 map 구조)
Key - Value 밑에 sub Key - Value 형식의 데이터 구조
//필드에 insert
hset <Key><Field><Value>
hget <Key><Field>
hgetall <Key> //해당 키에 저장된 모든 필드, 값 출력
//여러개 한번에 처리
hmset <Key><Field1><Value1> <Field2><Value2> <Field3><Value3>
mget <Key><Field1> <Field2> <Field3>
hlen <Key> //해당 키의 필드 개수 출력
hdel <Key> //해당 키의 필드 삭제
hkeys <Key> //해당 키의 모든 필드 목록 출력
hvals <Key> //해당 키의 모든 값에서 필드 이름 뺀 모든 값 목록 출력
//숫자
hincrby <Key><Field><Value //해당 키의 해당 필드의 값의 증감 처리
hincrbyfloat <Key><Field><Value> //해당 키의 해당 필드의 값의 증감 처리 -> float
Bitmap : 0&1로 표현하는 데이터
Geospatial : 좌표 데이터
stream → 로그 관리에서 많이 사용
Key 관리
keys * //주의.. 부하 심한편 실제 서비스에선 사용 X
keys h* //h로 시작하는 key 조회
del <Key>
exists <Key> //해당 키 존재 여부 확인
rename <Key><new Key> //해당 키 이름 변경
expire <Key><Time> //해당 키의 만료시간 설정
ttl <Key> //해당 키의 남은 만료시간 조회
persist <Key> //해당 키의 만료시간 삭제
Redis 특징
- Key-value 데이터 구조
- 컬렉션 구조 지원
- 인메모리로 데이터 처리해 빠르고 가벼움
- 디스크로 백업 가능 > Dump, AOF 방식
Redis 사용 방법
설치 방법
♣ 윈도우 설치
https://github.com/microsoftarchive/redis/releases
♣ Linux 설치
https://yunhyeonglee.tistory.com/24
Redis Client
- Lettuce :자바 레디스 클라이언트 라이브러리
- Jedis :자바 레디스 클라이언트 라이브러리
- Spring-boot-starter-data-redis : 스프링부트 레디스 캐시 설정 지원
Spring Boot + Redis 캐시서버 실습
이제 직접 스프링부트에 redis를 적용해 간단한 캐시 저장 실습만 해보겠다.
Spring-boot-starter-data-redis 라이브러리를 추가해준다.
<dependency>
<groupId>org.springframework.boot</groupId> \
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-redis')
}
DB 설정과 가져올 엔티티를 준비해준다.
application.yml
spring:
cache:
type: redis
redis:
host: 127.0.0.1
port: 6379
datasource:
url: (db url)
username: (db username)
password: (pw)
jpa:
show-sql: true
hibernate:
ddl-auto: update
캐시를 사용하기 위해 @EnableCaching 처리를 해준다
@EnableCaching
@SpringBootApplication
public class CachetestApplication {
public static void main(String[] args) {
SpringApplication.run(CachetestApplication.class, args);
}
}
redis 사용을 위해 설정 정보를 작성해준다. RedisConfig
@Configuration
public class RedisConfig {
@Value("${spring.redis.port}")
public int port;
@Value("${spring.redis.host}")
public String host;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public CacheManager cacheManager() {
RedisCacheManager.RedisCacheManagerBuilder builder =
RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory());
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer 변경
.entryTtl(Duration.ofMinutes(5)); // 캐시 수명 5분
builder.cacheDefaults(configuration);
return builder.build();
}
}
redis 연결과 캐시처리에 대한 설정을 한 redisTemplate을 만들어줬다.
이제 요청이 들어오면 캐시처리를 해준다.
Controller
@Controller
public class CacheTestController {
private final CacheTestService service;
public CacheTestController(CacheTestService service) { this.service = service; }
@GetMapping("price/{chargerType}")
public String getPriceList(@PathVariable String chargerType, Model model) {
List<ChargingPrice> chargingPriceList = service.getPriceList(chargerType);
model.addAttribute("list", chargingPriceList);
return "price";
}
@GetMapping("price/all")
public String getPriceList(Model model) {
List<ChargingPrice> chargingPriceList = service.getPriceListAll();
model.addAttribute("list", chargingPriceList);
return "price";
}
}
Service
@Service
public class CacheTestService {
private final CacheTestRepository repository;
public CacheTestService(CacheTestRepository repository) {
this.repository = repository;
}
@Cacheable(key = "#chargerType", value = "ChargingPrice")
public List<ChargingPrice> getPriceList(String chargerType) {
List<ChargingPrice> chargingPriceList = repository.findByChargerType(chargerType);
return chargingPriceList;
}
@Cacheable(value = "ChargingPrice")
public List<ChargingPrice> getPriceListAll() {
List<ChargingPrice> chargingPriceList = repository.findAll();
return chargingPriceList;
}
}
서비스단에서 캐시처리를 해줬는데
@Cacheable 로 간단하게 처리해줬다.
Repository
@Repository
public interface CacheTestRepository extends JpaRepository<ChargingPrice, Long> {
List<ChargingPrice> findByChargerType(String chargerType);
}
레포지터리는 그냥 JpaRepository를 상속받아 줬다.
이제 실제 요청을해본다
충전 타입이 low인 충전요금을 불러왔다. 1392 ms가 걸렸다.
오래걸린 것도 아니지만 다시 요청해보니 11 ms로 줄었다.
캐시를 확인해보니 잘 들어가 있는거 확인 할 수 있다.
확인을 위해 redis 실행
ChargingPrice::low 라는 이름으로 들어간 캐시 확인
값 확인해보니 잘 들어갔다.
이렇게 간단하게 캐시서버를 만들어 사용할 수있고
자료구조별로도 처리할 수 있다. (자료구조별 메소드 제공)
참고
https://brunch.co.kr/@skykamja24/575
이것이 레디스다 | 정경석 | 한빛미디어 - 교보문고 (kyobobook.co.kr)
'Database' 카테고리의 다른 글
[DAsP 1과목] 전사 아키텍처 이해 (정리본 / 요약본) (0) | 2022.11.17 |
---|---|
[SQLD 2과목] SQL 기본 및 활용 (정리본 / 요약본) (0) | 2022.09.07 |
[SQLD 1과목] 데이터 모델링의 이해 (정리본 / 요약본) (1) | 2022.08.25 |
[NoSQL] NoSQL 입문 / 개념 (0) | 2022.08.02 |
[InfluxDB] InfluxDB 입문 (설치 / 사용법 /Tick Stack / 시계열 데이터베이스 / Flux) (1) | 2022.04.11 |
댓글