Odin; the Cache engine
- Java 100%
| .idea | ||
| benchmark | ||
| benchmark2 | ||
| build-logic | ||
| gradle | ||
| gungnir | ||
| huginn | ||
| muninn | ||
| muninn2 | ||
| .gitattributes | ||
| .gitignore | ||
| Algorithm.md | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| odin.png | ||
| README.md | ||
| REQUIREMENTS.md | ||
| settings.gradle.kts | ||
Odin, the Cache Engine
Java 25 기반 경량 고성능 캐시 라이브러리
Odin은 가볍고 빠른 in-memory 캐시를 목표로 한 현대적인 Java 캐시 라이브러리입니다.
특징
- 극한의 경량성: JAR ≈ 450KB, Zero Dependency (Core)
- 고성능: W-TinyLFU + Lock-free 설계
- Java 25 네이티브: Virtual Threads, Structured Concurrency
- Off-Heap 지원: Foreign Memory API 기반 대용량 캐시
- Spring Boot 완전 지원:
@Cacheable,@CacheConfig
빠른 시작
Gradle (Kotlin DSL)
dependencies {
implementation("com.elex-project:odin:1.0.0")
// Spring Boot 사용 시
implementation("com.elex-project:odin-spring-boot-starter:1.0.0")
}
기본 사용법
OdinCache<String, User> cache = Odin.<String, User>newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(30))
.refreshAfterWrite(Duration.ofMinutes(10))
.recordStats(true)
.build();
User user = cache.get("user:123", this::loadUserFromDB);
Spring Boot 사용법
1. 의존성
implementation("com.elex-project:odin-spring-boot-starter:1.0.0")
2. application.yml
spring:
cache:
type: odin
3. 서비스 예시
@Service
@CacheConfig(cacheNames = "users")
public class UserService {
@Cacheable(key = "#id")
public User findById(Long id) { ... }
@CachePut(key = "#user.id")
public User save(User user) { ... }
@CacheEvict(key = "#id")
public void delete(Long id) { ... }
}
주요 기능
Core
- Size / Weight 기반 Eviction
- W-TinyLFU Admission Policy
- Refresh-after-Write
- Off-Heap Tier (Foreign Memory API)
- 상세 통계 (
CacheStats)
Spring Boot
@Cacheable,@CachePut,@CacheEvict,@CacheConfig- Auto Configuration
설정 예시
OdinCache<String, User> cache = Odin.<String, User>newBuilder()
.maximumSize(50_000)
.maximumWeight(512 * 1024 * 1024L) // 512MB
.weigher((key, value) -> value.getSize())
.expireAfterWrite(Duration.ofMinutes(15))
.refreshAfterWrite(Duration.ofMinutes(10))
.enableOffHeap(true)
.offHeapMaxSize(1L * 1024 * 1024 * 1024) // 1GB
.serializer(Serializer.kryo())
.recordStats(true)
.build();
Off-Heap + Foreign Memory API
1. 기본 사용법
OdinCache<String, User> cache = Odin.<String, User>newBuilder()
.maximumSize(5_000) // Heap Tier 크기
.enableOffHeap(true) // Off-Heap 활성화
.offHeapMaxSize(512L * 1024 * 1024) // 512MB
.serializer(new MySerializer()) // 필수: 직렬화 방식
.build();
2. Off-Heap 동작 원리
- Heap Tier — 빠른 접근 (기본)
- Off-Heap Tier — 메모리 압력 시 자동 이동
- Eviction — W-TinyLFU + Weight 기반
3. OffHeapStore 상세 활용
// OffHeapStore 직접 사용 예시
OffHeapStore store = new OffHeapStore(1024 * 1024 * 1024L); // 1GB
byte[] data = serializer.serialize(user);
long offset = store.put(data);
byte[] loaded = store.get(offset, data.length);
User restored = serializer.deserialize(loaded);
4. 고급 설정 예시
Odin Cache는 Builder 패턴을 통해 직관적으로 설정할 수 있습니다.
설정은 성능, 메모리, 만료 정책, 확장 기능으로 구분됩니다.
1. 기본 용량 설정
| 설정 메서드 | 설명 | 추천 값 | 비고 |
|---|---|---|---|
maximumSize(long) |
최대 엔트리 수 | 10,000 ~ 100,000 | Heap Tier 크기 |
maximumWeight(long) |
최대 총 가중치 (bytes) | 256MB ~ 2GB | Weight-based eviction |
weigher(Weigher) |
가중치 계산 함수 | Custom | 메모리 크기 기반 |
예시:
.maximumSize(50_000)
.maximumWeight(512 * 1024 * 1024L) // 512MB
.weigher((key, value) -> value.sizeInBytes())
2. 만료(Expiry) 정책
| 설정 메서드 | 설명 | 추천 값 |
|---|---|---|
expireAfterWrite(Duration) |
쓰기 후 만료 | 10m ~ 1h |
expireAfterAccess(Duration) |
접근 후 만료 (Idle) | 5m ~ 30m |
refreshAfterWrite(Duration) |
백그라운드 Refresh | expireAfterWrite보다 짧게 |
예시:
.expireAfterWrite(Duration.ofMinutes(30))
.refreshAfterWrite(Duration.ofMinutes(10)) // Stale-While-Revalidate
3. Off-Heap 설정 (대용량 추천)
| 설정 메서드 | 설명 | 필수 |
|---|---|---|
enableOffHeap(boolean) |
Off-Heap 활성화 | - |
offHeapMaxSize(long) |
Off-Heap 최대 크기 (bytes) | 필수 |
serializer(Serializer) |
직렬화 방식 | Kryo 권장 |
예시:
.enableOffHeap(true)
.offHeapMaxSize(1L * 1024 * 1024 * 1024) // 1GB
.compressor(Compressor.lz4())
.serializer(Serializer.kryo())
4. 통계 및 이벤트
| 설정 메서드 | 설명 |
|---|---|
recordStats(boolean) |
통계 수집 활성화 (CacheStats) |
removalListener(RemovalListener) |
제거 이벤트 리스너 |
예시:
.recordStats(true)
.removalListener((key, value, cause) ->
log.info("Removed {} due to {}", key, cause))
5. Spring Boot 설정 (application.yaml)
spring:
cache:
type: odin # 또는 none
odin:
cache:
default:
maximum-size: 10000
expire-after-write: 30m
record-stats: true
caches:
users:
maximum-size: 5000
expire-after-write: 1h
설정 전략 추천
1. 일반 서비스
.maximumSize(20_000)
.expireAfterWrite(Duration.ofMinutes(15))
.recordStats(true)
2. 대용량 + 메모리 절약
.maximumSize(5_000)
.enableOffHeap(true)
.offHeapMaxSize(2L * 1024*1024*1024)
.serializer(new ZstdSerializer()) // zstd algorithm
3. 실시간 고성능
.maximumSize(100_000)
.refreshAfterWrite(Duration.ofSeconds(30))
설정 우선순위:
- 용량 제한 (
maximumSize/maximumWeight) - 만료 정책 (
expireAfterWrite) - Off-Heap (메모리 부족 시)
- 통계 (
recordStats)
OdinCache<String, LargeObject> cache = Odin.<String, LargeObject>newBuilder()
.maximumSize(2_000) // Heap: 2,000개
.maximumWeight(256 * 1024 * 1024L) // Heap Weight: 256MB
.enableOffHeap(true)
.offHeapMaxSize(2L * 1024 * 1024 * 1024) // Off-Heap: 2GB
.weigher((key, value) -> value.estimatedSize())
.serializer(Serializer.kryo())
.expireAfterWrite(Duration.ofHours(2))
.refreshAfterWrite(Duration.ofHours(1))
.recordStats(true)
.build();
5. Off-Heap 모니터링
CacheStats stats = cache.stats();
System.out.println(stats.summary());
// OffHeapStore 직접 접근 (디버깅용)
if (cache instanceof OdinCacheImpl) {
OdinCacheImpl<?, ?> impl = (OdinCacheImpl<?, ?>) cache;
if (impl.getOffHeapStore() != null) {
System.out.println(impl.getOffHeapStore());
}
}
6. 주의사항 및 Best Practice
1. Serializer 선택
- Kryo (권장): 성능 최고
- Java Serializer: 의존성 없음, 느림
2. Off-Heap 사용 시점
- Heap 메모리가 1GB 이상 사용될 때
- Large Object (이미지, JSON, Report 등)
3. GC 압력 완화 효과
- Heap 사용량 60~80% 감소 가능
- Full GC 빈도 대폭 감소
4. Thread Safety
- OffHeapStore는 내부적으로
synchronized사용 - 고부하 환경에서는
Virtual Threads와 잘 어울림
7. 성능 비교 (예상)
| 항목 | Heap Only | Off-Heap Tier |
|---|---|---|
| GC Pressure | 높음 | 낮음 |
| Latency (Hit) | 매우 빠름 | 빠름 |
| Memory Limit | JVM Heap | Off-Heap + Heap |
| Large Object | 메모리 부족 | 안정적 |
Off-Heap을 활용한 실전 팁:
- Hot Data → Heap
- Warm Data → Off-Heap
- Cold Data → Redis (추후 Distributed Tier)
프로젝트 구조
- muninn : 코어 캐싱 엔진
- huginn : 스프링 부트 스타터
- gungnir : 시리얼라이저
- benchmark : JMH 벤치마크
로드맵
- Off-Heap 지원
- Spring Boot Starter
- FrequencySketch 고도화
- Eviction 정책 강화
- JCache 완전 호환
- Distributed Cache Hook
- GraalVM Native Image 최적화
- Micrometer Metrics
