Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- MSA
- 테스트 코드
- Kafka
- springboot
- Til
- Redis
- querydls
- Github Actions
- testcode
- swagger
- EC2
- algorithm
- CI/CD
- rabbitmq
- AWS
- Java
- 어노테이션
- JWT
- trouble shooting
- 아키텍처
- 객체지향원칙
- spring boot
- 멀티 모듈
- docker
- 유효성 검사
- DevOps
- 프로그래머스
- aop
- JPA
- Intellij
Archives
- Today
- Total
개발노트
25.04.13 Spring Redis hashOps.keys()로 패턴 조회 안 되는 이유와 SCAN을 사용한 해결법 본문
TroubleShooting
25.04.13 Spring Redis hashOps.keys()로 패턴 조회 안 되는 이유와 SCAN을 사용한 해결법
ddong-kka 2025. 4. 13. 01:24문제
Redis 명령어 CLI에서 다음과 같이 입력하면 예상대로 잘 작동한다.
그래서 Java 코드에서도 아래와 같이 패턴을 넣으면 잘 작동할 줄 알았다.
하지만 내 예상과는 다르게 아무것도 조회가 되지않았다.
// Redis에서 모든 리뷰의 좋아요 수를 가져와 HashMap으로 반환
public Map<String, Integer> getAllReviewIdsWithLikes() {
hashOps = likeCountRedisTemplate.opsForHash();
// 예시: 모든 리뷰의 Redis 키 패턴을 가져오고 해당하는 값들을 조회
Set<String> reviewIds = hashOps.keys("review:like:*"); // "review:like:"로 시작하는 모든 키들
Map<String, Integer> reviewLikesMap = new HashMap<>();
// 각 reviewId에 대해 좋아요 수를 가져와서 Map에 저장
for (String reviewId : reviewIds) {
// "review:like:" 접두어 제거
String plainReviewId = reviewId.replace(LIKE_KEY_PREFIX, "");
// count 값은 Integer로만 저장된다고 가정하고 바로 캐스팅
Integer likeCount = (Integer)hashOps.get(reviewId, "count");
if (likeCount != null) {
reviewLikesMap.put(plainReviewId, likeCount);
}
}
return reviewLikesMap;
}
원인 분석
hashOps.keys("pettern") 자바 코드는 내가 생각한 것 처럼 Redisdml KEYS 명령어 처럼 동작하지 않는다.
이 메서드는 단일 해시 키 안의 필드들만들 조회하는 기능이다.
즉, HKEYS review:like:* 이런 식으로는 동작하지 않음.
HKEYS review:like:*
review:like:1, review:like:2와 같이 여러 개의 해시 키에 대해 반복적으로 조회해야 할 때는 SCAN을 써야 한다는 사실을 알게되었다.
해결 방법
검색해보니 여러 키를 패턴으로 조회하고 싶으면 SCAN 명령어를 써야 한다.
SCAN은 Redis에서 전체 키 중 패턴에 맞는 키들을 반복적으로 검색하는 명령이고 성능상 KEYS 보다 안전하고 비차단 방식이라 대용량에서도 안전하게 사용 가능하다는 장점이있다.
Spring에서는 RedisConnection.scan()을 사용하면된다고한다.
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
ScanOptions options = ScanOptions.scanOptions()
.match("review:like:*")
.count(1000)
.build();
Cursor<byte[]> cursor = connection.scan(options);
cursor를 통해 하나씩 키를 순회하며 원하는 데이터를 꺼낼 수 있다. 커서는 반복이 끝나면 반드시 close() 해줘야하고,
커넥션도 닫아줘야한다.
// Redis에서 모든 리뷰의 좋아요 수를 가져와 HashMap으로 반환
public Map<String, Integer> getAllReviewIdsWithLikes() {
hashOps = likeCountRedisTemplate.opsForHash();
String pattern = "review:like:*";
RedisConnection connection = likeCountRedisTemplate.getConnectionFactory().getConnection();
ScanOptions options = ScanOptions.scanOptions()
.match(pattern)
.count(1000) // 한 번에 가져올 키 개수
.build();
// SCAN을 통해 Redis에서 키를 가져옴
Cursor<byte[]> keys = connection.scan(options);
// 결과를 저장할 맵
Map<String, Integer> reviewLikesMap = new HashMap<>();
// SCAN을 통해 가져온 키들에 대해 처리
while (keys.hasNext()) {
// Redis에서 가져온 키 (byte[])를 문자열로 변환
String key = new String(keys.next());
// Redis에서 해당 키에 대한 좋아요 수를 가져옴
Integer likeCount = getLikeCountFromRedis(key);
key = key.replace(LIKE_KEY_PREFIX, "");
// reviewLikesMap에 키와 좋아요 수를 추가
reviewLikesMap.put(key, likeCount);
}
// SCAN이 끝난 후, 연결 자원을 닫음
connection.close();
return reviewLikesMap;
}
// Redis에서 특정 키에 대한 좋아요 수를 가져오는 메서드
private Integer getLikeCountFromRedis(String key) {
// HashOperations을 사용하여 해당 키에서 "count" 값을 가져옴
hashOps = likeCountRedisTemplate.opsForHash();
Integer count = (Integer)hashOps.get(key, "count");
// "count" 값이 있으면 Integer로 변환하여 반환, 없으면 기본값 0 반환
if (count != null) {
return count;
} else {
return 0; // 기본값 0 반환
}
}
이렇게 코드를 변경함으로 써 키를 가져와 정상적으로 다룰 수 있게되었다!
'TroubleShooting' 카테고리의 다른 글
25.04.17 MySQL에서 UUID, Double 처리 문제 및 해결 방안 (0) | 2025.04.17 |
---|---|
25.04.15 결제 도메인에서 마일리지/쿠폰 차감 책임을 분리한 이유 (0) | 2025.04.15 |
25.04.10 Jackson LocalDateTime 직렬화 오류 해결 (0) | 2025.04.09 |
2025.03.22 RabbitMQ concurrency 설정과 비관적 락을 활용한 재고 감소 동시성 제어 (0) | 2025.03.25 |
25.03.21 사가 패턴 무한 재시도 방지 (0) | 2025.03.21 |