- Redis – install
- Redis – cluster
- Redis – SpringBoot2 redis cluster : strings, lists, hashs, sets, sortedsets, geo, hyperloglog
- Redis – Spring-data-redis : @Cacheable, @CachePut, @CacheEvict, @RedisHash
- Redis – spring-data-redis : 발행/구독(pub/sub) 모델의 구현
- Redis – Reactive redis
- Python으로 Redis cache 일괄 삭제 프로그램 만들기
SpringBoot2와 redis cluster를 연동하고, redis command에 대해 설명합니다. 다음을 참고하여 프로젝트를 하나 생성합니다.
>> Intellij Community 프로젝트생성
Boot를 실행하기 위한 Application Class 생성
package com.redis.cluster;
@SpringBootApplication
public class RedisClusterApplication {
public static void main(String[] args) {
SpringApplication.run(RedisClusterApplication.class, args);
}
}
build.gradle 수정
spring-boot-starter-data-redis를 이용하여 redis cluster를 연동합니다.
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.rest'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'com.google.code.gson:gson'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
application.yml 수정
h2, jpa, redis 설정을 추가합니다. cluster 노드 정보를 넣고, redis에 패스워드가 있으면 패스워드 정보도 추가합니다.
server:
port: 8081
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/test
driver-class-name: org.h2.Driver
username: sa
jpa:
database-platform: org.hibernate.dialect.H2Dialect
properties.hibernate.hbm2ddl.auto: update
showSql: true
redis:
cluster:
nodes:
- 15.164.98.87:6300
- 15.164.98.87:6301
- 15.164.98.87:6302
- 15.164.98.87:6400
- 15.164.98.87:6401
- 15.164.98.87:6402
password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
test/resources 아래에 logback_test.xml 생성
유닛 테스트에서 log를 사용하기 위해 test/resources 아래에 logback_test.xml를 생성합니다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%-5level %d{HH:mm:ss} %logger{15}.%method:%line - %msg%n
</pattern>
</encoder>
</appender>
<logger name="com.redis.cluster" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>
RedisClusterTest 생성
Redis가 제공하는 명령어를 실습합니다.
자주 사용하는 기본 명령어
@Test
public void commonCommand() {
ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
valueOps.set("key1", "key1value");
valueOps.set("key2", "key2value");
// Key 타입 조회.
assertEquals(DataType.STRING, redisTemplate.type("key1"));
// 존재하는 Key의 개수를 반환.
assertSame(2L, redisTemplate.countExistingKeys(Arrays.asList("key1", "key2", "key3")));
// Key가 존재하는지 확인
assertTrue(redisTemplate.hasKey("key1"));
// Key 만료 날짜 세팅
assertTrue(redisTemplate.expireAt("key1", Date.from(LocalDateTime.now().plusDays(1L).atZone(ZoneId.systemDefault()).toInstant())));
// Key 만료 시간 세팅
assertTrue(redisTemplate.expire("key1", 60, TimeUnit.SECONDS));
// Key 만료 시간 조회
assertThat(redisTemplate.getExpire("key1"), greaterThan(0L));
// Key 만료 시간 해제
assertTrue(redisTemplate.persist("key1"));
// Key 만료시간이 세팅 안되어있는경우 -1 반환
assertSame(-1L, redisTemplate.getExpire("key1"));
// Key 삭제
assertTrue(redisTemplate.delete("key1"));
// Key 일괄 삭제
assertThat(redisTemplate.delete(Arrays.asList("key1", "key2", "key3")), greaterThan(0L));
}
String structure command
redis command set, get에 대한 내용입니다.
@Test
public void opsValue() {
ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
Collection<String> cacheKeys = new ArrayList<>();
String cacheKey = "value_";
for (int i = 0; i < 10; i++) {
cacheKeys.add(cacheKey + i);
valueOps.set(cacheKey + i, String.valueOf(i), 60, TimeUnit.SECONDS);
}
List<String> values = valueOps.multiGet(cacheKeys);
assertNotNull(values);
assertEquals(10, values.size());
log.info("##### opsValue #####");
log.info("{}", values);
}
##### opsSortedSet ##### [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Lists structure command – 순서 있음. value 중복 허용
redis command lpush, llen, lrange, lpop, rpop에 대한 내용입니다.
@Test
public void opsList() {
ListOperations<String, String> listOps = redisTemplate.opsForList();
String cacheKey = "valueList";
for (int i = 0; i < 10; i++)
listOps.leftPush(cacheKey, String.valueOf(i));
assertSame(DataType.LIST, redisTemplate.type(cacheKey));
assertSame(10L, listOps.size(cacheKey));
log.info("##### opsList #####");
log.info("{}", listOps.range(cacheKey, 0, 10));
assertEquals("0", listOps.rightPop(cacheKey));
assertEquals("9", listOps.leftPop(cacheKey));
assertEquals(true, redisTemplate.delete(cacheKey));
}
##### opsList ##### [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Hashes structure command – 순서 없음. key 중복 허용안함, value 중복 허용
redis command hset, hget, hlen, hdel에 대한 내용입니다.
@Test
public void opsHash() {
HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
String cacheKey = "valueHash";
for (int i = 0; i < 10; i++)
hashOps.put(cacheKey, "key_" + i, "value_" + i);
assertSame(DataType.HASH, redisTemplate.type(cacheKey));
assertSame(10L, hashOps.size(cacheKey));
log.info("##### opsHash #####");
Set<String> hkeys = hashOps.keys(cacheKey);
for (String hkey : hkeys) {
log.info("{} / {}", hkey, hashOps.get(cacheKey, hkey));
}
assertEquals("value_5", hashOps.get(cacheKey, "key_5"));
assertSame(1L, hashOps.delete(cacheKey, "key_5"));
assertSame(null, hashOps.get(cacheKey, "key_5"));
}
##### opsHash ##### key_0 / value_0 key_1 / value_1 key_2 / value_2 key_3 / value_3 key_4 / value_4 key_6 / value_6 key_7 / value_7 key_8 / value_8 key_9 / value_9 key_5 / value_5
Set structure command – 순서 없음, value 중복 허용안함
redis command sadd, scard, smembers, sismember에 대한 내용입니다.
@Test
public void opsSet() {
SetOperations<String, String> setOps = redisTemplate.opsForSet();
String cacheKey = "valueSet";
for (int i = 0; i < 10; i++)
setOps.add(cacheKey, String.valueOf(i));
assertSame(DataType.SET, redisTemplate.type(cacheKey));
assertSame(10L, setOps.size(cacheKey));
log.info("##### opsList #####");
log.info("{}", setOps.members(cacheKey));
assertEquals(true, setOps.isMember(cacheKey, "5"));
}
##### opsSet ##### [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
SortedSet structure command – 순서 있음, value 중복 허용안함
rediscommand zadd, zcard, zrange, zrank에 대한 내용입니다.
@Test
public void opsSortedSet() {
ZSetOperations<String, String> zsetOps = redisTemplate.opsForZSet();
String cacheKey = "valueZSet";
for (int i = 0; i < 10; i++)
zsetOps.add(cacheKey, String.valueOf(i), i);
assertSame(DataType.ZSET, redisTemplate.type(cacheKey));
assertSame(10L, zsetOps.size(cacheKey));
log.info("##### opsSortedSet #####");
log.info("{}", zsetOps.range(cacheKey, 0, 10));
assertSame(0L, zsetOps.reverseRank(cacheKey, "9"));
}
##### opsSortedSet ##### [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Geo structure command – 좌표 정보 처리, 타입은 zset으로 저장.
redis command geoadd, geodist, geopos에 대한 내용입니다.
@Test
public void opsGeo() {
GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
String[] cities = {"서울", "부산"};
String[][] gu = {{"강남구", "서초구", "관악구", "동작구", "마포구"}, {"사하구", "해운대구", "영도구", "동래구", "수영구"}};
Point[][] pointGu = {{new Point(10, -10), new Point(11, -20), new Point(13, 10), new Point(14, 30), new Point(15, 40)}, {new Point(-100, 10), new Point(-110, 20), new Point(-130, 80), new Point(-140, 60), new Point(-150, 30)}};
String cacheKey = "valueGeo";
// previous key delete
redisTemplate.delete(cacheKey);
for (int x = 0; x < cities.length; x++) {
for (int y = 0; y < 5; y++) {
geoOps.add(cacheKey, pointGu[x][y], gu[x][y]);
}
}
log.info("##### opsGeo #####");
Distance distance = geoOps.distance(cacheKey, "강남구", "동작구");
assertNotNull(distance);
assertEquals(4469610.0767, distance.getValue(), 4);
log.info("Distance : {}", distance.getValue());
List<Point> position = geoOps.position(cacheKey, "동작구");
assertNotNull(position);
for (Point point : position) {
assertEquals(14.000001847743988d, point.getX(), 4);
assertEquals(30.000000249977013d, point.getY(), 4);
log.info("Position : {} x {}", point.getX(), point.getY());
}
}
##### opsGeo ##### Distance : 4469610.0767 Position : 14.000001847743988 x 30.000000249977013
HyperLogLog structure command – 집합의 원소의 개수 추정, 타입은 string으로 저장.
redis command pfadd, pfcount에 대한 내용입니다.
@Test
public void opsHyperLogLog() {
HyperLogLogOperations<String, String> hyperLogLogOps = redisTemplate.opsForHyperLogLog();
String cacheKey = "valueHyperLogLog";
String[] arr1 = {"1", "2", "2", "3", "4", "5", "5", "5", "5", "6", "7", "7", "7"};
hyperLogLogOps.add(cacheKey, arr1);
log.info("##### opsHyperLogLog #####");
log.info("count : {}", hyperLogLogOps.size(cacheKey));
redisTemplate.delete(cacheKey);
}
##### opsHyperLogLog ##### count : 7
최신 소스는 GitHub 사이트를 참고해 주세요.
https://github.com/codej99/SpringRedisCluster/tree/feature/rediscluster















