- 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