1. RedisTemplate
- Spring에서 Redis와 상호작용할 수 있게 해주는 중심 클래스.
항목 | 설명 |
동작 방식 | 자동으로 직렬화 및 연결 관리 처리 (고수준 API) |
구성 |
RedisOperations 인터페이스 구현
|
스레드 안전성 |
설정 완료 후 스레드 안전함
|
뷰(Views) |
opsForValue(), opsForList()
→ 특정 자료구조 전용 API처럼 사용 가능 |
예제) ListOperations
더보기
@Autowired
private ListOperations<String, String> listOps;
public void addLink(String userId, URL url) {
listOps.leftPush(userId, url.toExternalForm());
}
- 특정 자료형용 opsForXxx() 뷰를 DI로 주입 가능
vs StringRedisTemplate
항목 | RedisTemplate | StringRedisTemplate |
직렬화 | Java 직렬화 사용 (JdkSerializationRedisSerializer) |
StringRedisSerializer 사용 (사람이 읽을 수 있는 문자열로 저장됨)
|
용도 | 객체 저장 시 유용 | 키-값 모두 문자열일 때 간편 |
명령 호출 | template.opsForList().leftPush(...) |
동일 (문자열 전용으로 최적화됨)
|
Serializer
항목 | 설명 |
기본 원리 |
객체 ↔ 바이트 간 자동 변환 (Redis에 저장되는 데이터는 모두 byte[])
|
종류 |
- JdkSerializationRedisSerializer: 기본값, Java native 방식 (보안 위험 있음)
- StringRedisSerializer: 문자열 기반 - Jackson2JsonRedisSerializer, GenericJackson2JsonRedisSerializer: JSON 기반 - OxmSerializer: XML용 |
설정
더보기
RedisTemplate.setKeySerializer(...), setValueSerializer(...)
RedisCallback 인터페이스
- RedisConnection 수준의 낮은 레벨 명령어 실행 가능
예제
더보기
redisOperations.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
Long size = connection.dbSize();
((StringRedisConnection)connection).set("key", "value");
return null;
}
});
- StringRedisTemplate 사용 시 connection을 StringRedisConnection으로 캐스팅 가능
2. Redis Cache
RedisCacheManager
- 전체 캐시 시스템을 관리하는 매니저 클래스
- RedisCacheConfiguration를 사용하여 설정
설정) RedisCacheManager
더보기
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.transactionAware()
.withInitialCacheConfigurations(Map.of("predefined",
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()))
.build();
}
기본 동작
- TTL 미설정
- null 값 캐시 금지
- prefix 사용 (캐시이름::key)
- key 직렬화: StringRedisSerializer
- value 직렬화: JdkSerializationRedisSerializer
RedisCacheConfiguration
- 개별 캐시의 동작 방식을 설정하는 구성 클래스
설정 | 설명 |
entryTtl(Duration) |
TTL 설정 (고정 또는 동적으로 설정 가능)
|
prefixCacheNameWith("prefix") | 고정 접두어 설정 |
computePrefixWith(cacheName -> "...") | 동적 접두어 설정 |
disableCachingNullValues() | null 값 캐시 방지 |
serializeKeysWith(...) | 키 직렬화 방식 설정 |
serializeValuesWith(...) | 값 직렬화 방식 설정 |
설정) RedisCacheConfiguration
더보기
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.computePrefixWith(cacheName -> "")
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // TODO: Bean 등록 ?
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer));
TTL vs TTI
구분 | TTL (Time-To-Live) | TTI (Time-To-Idle) |
동작 조건 | 작성/수정 시 TTL 리셋 | 읽기/쓰기 모두 TTL 리셋 |
지원 방식 | Redis 기본 지원 |
Redis 6.2+의 GETEX 명령어 기반
|
Spring 설정 | entryTtl(Duration.ofMinutes(5)) | .enableTimeToIdle() 추가 |
주의점 | 단순 GET 호출 시 TTL 리셋 안 됨 |
반드시 GETEX를 통해 접근해야 동작
|
예제) 동적 TTL 설정
더보기
TtlFunction 인터페이스
enum MyCustomTtlFunction implements TtlFunction {
INSTANCE;
public Duration getTimeToLive(Object key, @Nullable Object value) {
// 키 또는 값에 따라 TTL 계산
}
}
- RedisCacheManager에서 entryTtl(MyCustomTtlFunction.INSTANCE)로 적용 가능
예제) TTI 설정
더보기
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(5)) // TTL 설정 (필수)
.enableTimeToIdle(); // TTI 활성화
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
Locking vs Non-locking
항목 | 설명 |
Non-locking (기본값) | 빠름 putIfAbsent, clear에서 원자성 부족 |
Locking | 명시적 락 키를 사용 - 겹치는 명령어 방지 - 성능은 낮아질 수 있음 |
예제) Locking
더보기
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.builder(
RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)
)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.build();
}
캐시 삭제 전략
- 지울 키를 찾기 위해 사용하는 전략
전략 | 설명 |
KEYS (기본) |
전체 키 조회.
- 대용량에서는 성능 이슈 발생 |
SCAN |
배치 기반 순회 방식
- Lettuce에서 권장 - Jedis는 Standalone만 지원 |
예제) SCAN 설정
더보기
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.builder(
RedisCacheWriter.nonLockingRedisCacheWriter(
connectionFactory,
BatchStrategies.scan(1000) // SCAN 기반, 한 번에 1000개씩 처리
)
)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.build();
}
Redis Cluster
Hash Mapping
Redis 해시 저장 방식
방식 | 설명 |
Jackson2JsonRedisSerializer | 하나의 key에 전체 객체를 JSON 문자열로 저장 (SET 사용) |
Redis Hash + HashMapper | 객체의 속성을 field:value 형식으로 Hash 구조에 저장 (HSET 사용) |
HashMapper
구현체 | 특징 |
ObjectHashMapper |
단순 Java 직렬화 (바이트 기반)
|
BeanUtilsHashMapper |
Java Bean → Map 변환 (평면 구조만 가능)
|
Jackson2HashMapper |
JSON 기반, 중첩 객체까지 지원→ 평면(flat) 또는 중첩 저장 가능
|
설정) Jackson2HashMapper
더보기
@Bean("hashRedisTempate")
public RedisTemplate<String, Object> hashRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(RedisSerializer.byteArray()); // HashMapper가 직접 처리하기 위함
return template;
}
@Bean
public Jackson2HashMapper jackson2HashMapper() {
return new Jackson2HashMapper(false); // flat = false (중첩 JSON 저장)
}
예시) Jackson2HashMapper
더보기
@Component
public class PersonHashRepository {
private final HashOperations<String, byte[], byte[]> hashOps;
private final Jackson2HashMapper mapper;
public PersonHashRepository(
@Qualifier("hashRedisTemplate") RedisTemplate<String, Object> hashRedisTemplate,
Jackson2HashMapper mapper) {
this.hashOps = hashRedisTemplate.opsForHash();
this.mapper = mapper;
}
public void save(String key, Person person) {
Map<byte[], byte[]> mapped = mapper.toHash(person);
hashOps.putAll(key, mapped);
}
public Person find(String key) {
Map<byte[], byte[]> loaded = hashOps.entries(key);
return (Person) mapper.fromHash(loaded);
}
}
Pub/Sub Messaging
publishing
subscribing
Redis Streams
*Scripting
- Lua Script를 실행하는 방법
실행 방식
RedisTemplate
ScriptExecutor
RedisScript<T>: 스크립트를 담는 객체
예제
lua script
더보기
local current = redis.call('GET', KEYS[1])
if current == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[2])
return true
end
return false
spring data redis
더보기
@Bean
public RedisScript<Boolean> script() {
ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua"));
return RedisScript.of(scriptSource, Boolean.class);
}
@Autowired
RedisOperations<String, String> redisOperations;
@Autowired
RedisScript<Boolean> script;
public boolean checkAndSet(String expectedValue, String newValue) {
return redisOperations.execute(script, List.of("key"), expectedValue, newValue);
}
성능
- DefaultRedisScript: SHA1 재계산
- evalsha 사용: 스크립트 SHA1 캐시를 자동 활용
*Redis Transactions
- Redis는 트랜잭션 내의 명령들을 명령 큐(queue)에 저장했다가 EXEC 시점에 실행함
기본 명령어
명령어 | 설명 |
MULTI |
트랜잭션 시작 (이후 명령들이 일시적으로 큐에 저장됨)
|
EXEC |
큐에 저장된 명령들을 한 번에 실행
|
DISCARD |
큐에 저장된 명령들을 모두 무시하고 취소
|
설정
- 기본적으로 RedisTemplate은 스프링 트랜잭션과 연동되지 않음 (@Transactional)
- 스프링 트랜잭션과 연동하기 위해 추가적인 설정이 필요함
설정) @Transactional 연동
더보기
@Configuration
@EnableTransactionManagement
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTemplate() {
var template = new StringRedisTemplate(redisConnectionFactory());
template.setEnableTransactionSupport(true); // 중요!!
return template;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource()); // JDBC 트랜잭션 매니저
}
}
- JDBC 트랜잭션과 함께 연동하고 싶을 때 쓰는 방식
- setEnableTransactionSupport(): Redis connection이 현재 쓰레드에 바운드됨
사용
SessionCallback
- Redis 명령을 같은 세션 안에서 고정되게 해주는 인터페이스
- Redis 트랜잭션 명령을 하도록 도와줌
- Redis의 MULTI~EXEC 사이의 명령은 같은 커넥션에서 실행되어야 함
더보기
List<Object> txResults = redisTemplate.execute(new SessionCallback<>() {
public List<Object> execute(RedisOperations operations) {
try {
operations.multi(); // 트랜잭션 시작
operations.opsForSet().add("key", "value1");
return operations.exec(); // 실행
} catch (RuntimeException e) {
operations.discard(); // 꼭 discard로 정리
throw e;
}
}
});
- 예외가 발생하면, 반드시 discard()로 정리하기 (exec()을 못 탐 → 다음 명령도 트랜잭션으로 인식됨)
트랜잭션 중 읽기 제한
- 한 트랜잭션 안에서 쓰여진 데이터를 읽기 명령으로 읽을 수 없음
- 읽기 명령은 새 커넥션으로 실행됨 (트랜잭션 큐를 못 봄)
Pipelining
*Support Classes
- Java 표준 인터페이스를 Redis 위에서 구현한 클래스들을 제공
클래스 | Redis 자료구조 | 설명 |
RedisList<T> | List, Queue, Deque |
FIFO/LIFO 동작 가능
BLPOP, BRPOP 같은 blocking 동작도 가능 |
RedisSet<T> | Set |
Redis의 SADD, SREM, SINTER 등 지원
|
RedisZSet<T> | Sorted Set |
ZADD, ZRANGE, ZINTER 등 지원
|
RedisAtomicLong | String(Number) |
INCR, DECR 등 Redis 기반 카운터
|
장점
항목 | 효과 |
저장소 추상화 |
Redis 대신 Java in-memory list로 대체 가능
|
테스트 용이성 | 모킹/대체 쉬움 |
유연한 구조 |
프로토타입 환경에서 in-memory, 운영에서는 Redis만 바꿔 끼우기
|
설정
더보기
@Bean
RedisList<String> myQueue(RedisTemplate<String, String> redisTemplate) {
return new DefaultRedisList<>(redisTemplate, "queue-key");
}
- "queue-key"라는 Redis 리스트 키로 연결되는 Java List가 생성됨
예제
더보기
public class AnotherExample {
private Deque<String> queue;
public void addTag(String tag) {
queue.push(tag); // 내부적으로 Redis LPUSH
}
public String popTag() {
return queue.pop(); // 내부적으로 Redis LPOP or RPOP
}
}
출처
'Database > Redis' 카테고리의 다른 글
[Redis] 3-4. Introduction to Redis: Pub/Sub (1) | 2025.06.04 |
---|---|
[실전 레디스] 6-1. 트러블 슈팅: 서버 정보 (1) | 2025.03.23 |
[실전 레디스] 5-4. 레디스 운용 관리: 관리 (0) | 2025.03.21 |
[실전 레디스] 5-3. 레디스 운용 관리: 셋팅 (0) | 2025.03.21 |
[실전 레디스] 5-2. 레디스 운용 관리: 아키텍처 (0) | 2025.03.21 |