ํ์ฅ ๊ฐ๋ฅํ๊ณ ๊ฐ๋ ฅํ Java ์บ์ ํ๋ ์์ํฌ
SB Cached Collection์ TTL, ์ถ์ถ ์ ์ฑ , ๋ค์ํ ์ฐธ์กฐ ํ์ , Write-Through/Write-Behind, Refresh-Ahead ์ ๋ต์ ์ง์ํ๋ ์ํฐํ๋ผ์ด์ฆ๊ธ ์บ์ ์๋ฃจ์ ์ ๋๋ค.
- โ TTL (Time To Live): ์๋ ๋ง๋ฃ ๋ฐ ๊ฐฑ์
- โ ๋ค์ํ ์ถ์ถ ์ ์ฑ : LRU, LFU, FIFO, RANDOM, TTL
- โ Reference ํ์ ์ง์: STRONG, SOFT, WEAK (GC ํ๋ ฅ)
- โ ๋ก๋ฉ ์ ๋ต: SYNC, ASYNC (Thundering Herd ๋ฐฉ์ง)
- โ ์ฐ๊ธฐ ์ ๋ต: READ_ONLY, WRITE_THROUGH, WRITE_BEHIND
- โ ๊ฐฑ์ ์ ๋ต: ON_MISS, REFRESH_AHEAD
- โ ์ค๋ ๋ ์์ : ConcurrentHashMap ๊ธฐ๋ฐ ๊ณ ์ฑ๋ฅ
- โ Spring ํตํฉ: CacheManager, @Cacheable ์ง์
- โ ํต๊ณ ์์ง: Hit Rate, Load Time, Metrics
- ๐ ์บ์ ์๋ฐ์ : ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ์ฌ์ ๋ก๋ฉ
- ๐ Write-Through/Write-Behind: ๋ฐฑ์๋ ์ ์ฅ์ ๋๊ธฐํ
- โก Refresh-Ahead: ๋ง๋ฃ ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์
- ๐ JMX/Prometheus ํตํฉ: ์ค์๊ฐ ๋ชจ๋ํฐ๋ง
- ๐ ํ์ฅ ๊ฐ๋ฅ: Loader, Writer, EvictionStrategy ์ปค์คํฐ๋ง์ด์ง
- ๐ก๏ธ ๋ฉ๋ชจ๋ฆฌ ์์ : MaxSize, Reference ํ์ ์ผ๋ก OOM ๋ฐฉ์ง
<dependency>
<groupId>org.scriptonbasestar.cache</groupId>
<artifactId>cache-collection</artifactId>
<version>sb-cache-20251107-1-DEV</version>
</dependency>
<!-- Spring ํตํฉ (์ ํ ์ฌํญ) -->
<dependency>
<groupId>org.scriptonbasestar.cache</groupId>
<artifactId>cache-spring</artifactId>
<version>sb-cache-20251107-1-DEV</version>
</dependency>// 1. Loader ์ ์ (๋ฐ์ดํฐ ์์ค)
SBCacheMapLoader<Long, User> loader = new SBCacheMapLoader<>() {
@Override
public User loadOne(Long id) throws SBCacheLoadFailException {
return userRepository.findById(id)
.orElseThrow(() -> new SBCacheLoadFailException("User not found: " + id));
}
@Override
public Map<Long, User> loadAll() throws SBCacheLoadFailException {
return userRepository.findAll().stream()
.collect(Collectors.toMap(User::getId, user -> user));
}
};
// 2. ์บ์ ์์ฑ (5๋ถ TTL, ์ต๋ 1,000๊ฐ, LRU)
SBCacheMap<Long, User> cache = SBCacheMap.<Long, User>builder()
.loader(loader)
.timeoutSec(300)
.maxSize(1000)
.evictionPolicy(EvictionPolicy.LRU)
.enableMetrics(true)
.build();
// 3. ์ฌ์ฉ
User user = cache.get(123L); // ์ฒซ ํธ์ถ: DB ์กฐํ
User cached = cache.get(123L); // ๋ ๋ฒ์งธ: ์บ์ ํํธ
// 4. ํต๊ณ ํ์ธ
System.out.println("Hit Rate: " + cache.getHitRate());
System.out.println("Average Load Time: " + cache.getAverageLoadTime() + "ms");-
๐ USER_GUIDE.md - ์ข ํฉ ์ฌ์ฉ์ ๊ฐ์ด๋
- Quick Start, ํต์ฌ ๊ฐ๋ , ๊ณ ๊ธ ๊ธฐ๋ฅ
- ์ค์ ์์ (User Profile, API Response, Product Catalog)
- ์ฑ๋ฅ ํ๋, ๋ชจ๋ํฐ๋ง, ํธ๋ฌ๋ธ์ํ
-
๐ SPRING_INTEGRATION.md - Spring ํตํฉ ๊ฐ์ด๋
- CacheManager ์ค์ , @Cacheable ์ฌ์ฉ๋ฒ
- Auto-Configuration (YAML/Properties)
- Actuator ํตํฉ, 4๊ฐ์ง ์ค์ ์์
-
๐ API_REFERENCE.md - ์ ์ฒด API ๋ ํผ๋ฐ์ค
- ๋ชจ๋ ํด๋์ค/์ธํฐํ์ด์ค/Enum ์์ธ ์ค๋ช
- ๋ฉ์๋ ์๊ทธ๋์ฒ, ํ๋ผ๋ฏธํฐ, ๋ฐํ๊ฐ
- 5๊ฐ์ง ์ฌ์ฉ ์์
-
๐๏ธ ARCHITECTURE.md - ์์คํ ์ํคํ ์ฒ
- ๋ชจ๋ ๊ตฌ์กฐ, ๋ ์ด์ด ์ํคํ ์ฒ
- 5๊ฐ์ง ๋์์ธ ํจํด, ํ์ฅ ํฌ์ธํธ
- ์ค๋ ๋ ์์ ์ฑ, ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ, ์ฑ๋ฅ ์ต์ ํ
๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ ๋์ํ๋ 3๊ฐ์ง ์ฐธ์กฐ ํ์ :
// STRONG: ๋ช
์์ ์ ๊ฑฐ ์ ๊น์ง ์ ์ง (๊ธฐ๋ณธ๊ฐ)
SBCacheMap<String, Config> configCache = SBCacheMap.<String, Config>builder()
.timeoutSec(3600)
.referenceType(ReferenceType.STRONG)
.build();
// SOFT: ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ GC๊ฐ ํ์ (๋์ฉ๋ ์บ์)
SBCacheMap<String, byte[]> imageCache = SBCacheMap.<String, byte[]>builder()
.timeoutSec(3600)
.referenceType(ReferenceType.SOFT) // OOM ๋ฐฉ์ง
.build();
// WEAK: ๋ค์ GC์์ ํ์ (์์ ์บ์)
SBCacheMap<String, Report> tempCache = SBCacheMap.<String, Report>builder()
.timeoutSec(60)
.referenceType(ReferenceType.WEAK) // ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
.build();MaxSize ์ด๊ณผ ์ ์ด๋ค ํญ๋ชฉ์ ์ ๊ฑฐํ ์ง ๊ฒฐ์ :
// LRU: ๊ฐ์ฅ ์ต๊ทผ์ ์ฌ์ฉ๋์ง ์์ ํญ๋ชฉ ์ ๊ฑฐ (๊ธฐ๋ณธ๊ฐ)
SBCacheMap<Long, User> lruCache = SBCacheMap.<Long, User>builder()
.maxSize(10000)
.evictionPolicy(EvictionPolicy.LRU)
.build();
// LFU: ๊ฐ์ฅ ์ ๊ฒ ์ฌ์ฉ๋ ํญ๋ชฉ ์ ๊ฑฐ
SBCacheMap<String, Product> lfuCache = SBCacheMap.<String, Product>builder()
.maxSize(5000)
.evictionPolicy(EvictionPolicy.LFU) // ์ธ๊ธฐ๋ ๊ธฐ๋ฐ
.build();
// FIFO: ๊ฐ์ฅ ๋จผ์ ์ถ๊ฐ๋ ํญ๋ชฉ ์ ๊ฑฐ
// RANDOM: ๋ฌด์์ ํญ๋ชฉ ์ ๊ฑฐ
// TTL: ๊ฐ์ฅ ์ค๋๋ ํญ๋ชฉ ์ ๊ฑฐ์บ์ ๋ฏธ์ค ์ ๋ก๋ฉ ๋ฐฉ์:
// SYNC: ๋๊ธฐ ๋ก๋ฉ (๊ธฐ๋ณธ๊ฐ)
SBCacheMap<String, Data> syncCache = SBCacheMap.<String, Data>builder()
.loader(dataLoader)
.loadStrategy(LoadStrategy.SYNC)
.build();
// ASYNC: ๋น๋๊ธฐ ๋ก๋ฉ (Thundering Herd ๋ฐฉ์ง)
SBCacheMap<String, Report> asyncCache = SBCacheMap.<String, Report>builder()
.loader(reportLoader)
.loadStrategy(LoadStrategy.ASYNC) // ์ค๋ณต ๋ก๋ฉ ๋ฐฉ์ง
.build();
// ASYNC ๋์:
// Thread 1: miss โ load โ return
// Thread 2: miss โ wait โ (Thread 1 ์๋ฃ) โ return
// Thread 3: miss โ wait โ (Thread 1 ์๋ฃ) โ return
// ๊ฒฐ๊ณผ: 1๋ฒ๋ง ๋ก๋ฉ (ํจ์จ์ )์บ์์ ๋ฐฑ์๋ ์ ์ฅ์ ๋๊ธฐํ:
// WRITE_THROUGH: ๋๊ธฐ ์ฐ๊ธฐ (์ฆ์ ์์ํ)
SBCacheMap<Long, User> writeThrough = SBCacheMap.<Long, User>builder()
.loader(userLoader)
.writer(userWriter)
.writeStrategy(WriteStrategy.WRITE_THROUGH)
.build();
writeThrough.put(123L, user); // ์บ์ + DB ๋์ ์ฐ๊ธฐ
// WRITE_BEHIND: ๋น๋๊ธฐ ์ฐ๊ธฐ (์ฑ๋ฅ ์ฐ์ ) + ์ฌ์๋ ๋ก์ง
SBCacheMap<String, Session> writeBehind = SBCacheMap.<String, Session>builder()
.loader(sessionLoader)
.writer(sessionWriter)
.writeStrategy(WriteStrategy.WRITE_BEHIND) // ๋ฐฐ์น ์ฒ๋ฆฌ
.writeBehindBatchSize(100) // 100๊ฐ์ฉ ๋ฐฐ์น
.writeBehindIntervalSeconds(5) // 5์ด๋ง๋ค ํ๋ฌ์
.writeBehindMaxRetries(3) // ์คํจ ์ ์ต๋ 3ํ ์ฌ์๋
.writeBehindRetryDelayMs(1000) // ์ฌ์๋ ๊ฐ๊ฒฉ 1์ด
.build();
writeBehind.put("session1", session); // ์บ์์๋ง ์ฐ๊ณ ์ฆ์ ๋ฐํ
// ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋์ค์ DB ๋ฐ์ (์คํจ ์ ์๋ ์ฌ์๋)์บ์ ํญ๋ชฉ ๊ฐฑ์ ๋ฐฉ์:
// ON_MISS: ๋ฏธ์ค ์์๋ง ๊ฐฑ์ (๊ธฐ๋ณธ๊ฐ)
SBCacheMap<String, Config> onMiss = SBCacheMap.<String, Config>builder()
.loader(configLoader)
.refreshStrategy(RefreshStrategy.ON_MISS)
.build();
// REFRESH_AHEAD: ๋ฏธ๋ฆฌ ๊ฐฑ์ (ํญ์ ์ ์ ํ ๋ฐ์ดํฐ)
SBCacheMap<String, StockPrice> refreshAhead = SBCacheMap.<String, StockPrice>builder()
.loader(stockPriceLoader)
.timeoutSec(60) // 1๋ถ TTL
.refreshStrategy(RefreshStrategy.REFRESH_AHEAD) // 30์ด ํ ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์
.build();
// REFRESH_AHEAD ๋์:
// T=0: cache.get(key) โ miss โ load โ return
// T=30: cache.get(key) โ hit (์ฆ์ ๋ฐํ) + ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์ ์์
// T=60: cache.get(key) โ hit (์ด๋ฏธ ๊ฐฑ์ ๋จ, ์ฆ์ ๋ฐํ)@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new SBCacheManager()
.addCache("users", SBCacheMap.<Object, Object>builder()
.timeoutSec(300)
.maxSize(1000)
.evictionPolicy(EvictionPolicy.LRU)
.referenceType(ReferenceType.SOFT)
.enableMetrics(true)
.build());
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}application.yml:
sb:
cache:
enabled: true
caches:
users:
timeout-sec: 300
max-size: 1000
eviction-policy: LRU
reference-type: SOFT
enable-metrics: true
products:
timeout-sec: 600
max-size: 5000
eviction-policy: LFU
reference-type: STRONGGET /actuator/health:
{
"status": "UP",
"components": {
"sbCache": {
"status": "UP",
"details": {
"users": {
"status": "UP",
"size": 234,
"maxSize": 1000,
"hitRate": 0.87,
"missRate": 0.13
}
}
}
}
}// ํต๊ณ ์กฐํ
double hitRate = cache.getHitRate();
double missRate = cache.getMissRate();
long loadCount = cache.getLoadCount();
double avgLoadTime = cache.getAverageLoadTime();
System.out.println("Hit Rate: " + (hitRate * 100) + "%");
System.out.println("Average Load Time: " + avgLoadTime + "ms");sb-cached-collection/
โโโ cache-core/ # ํต์ฌ ์ธํฐํ์ด์ค ๋ฐ ์ ๋ต
โ โโโ strategy/ # ReferenceType, EvictionPolicy, LoadStrategy, etc.
โ โโโ loader/ # SBCacheMapLoader, SBCacheListLoader
โ โโโ writer/ # SBCacheMapWriter
โ โโโ exception/ # SBCacheLoadFailException, SBCacheWriteFailException
โ
โโโ cache-collection/ # ์บ์ ๊ตฌํ์ฒด
โ โโโ map/ # SBCacheMap (๋ฉ์ธ ๊ตฌํ)
โ โโโ storage/ # ReferenceBasedStorage
โ โโโ strategy/ # LRU, LFU, FIFO, RANDOM, TTL ๊ตฌํ
โ
โโโ cache-loader-jdbc/ # JDBC Loader
โโโ cache-loader-file/ # File Loader
โโโ cache-metrics/ # ํต๊ณ ๋ฐ ๋ชจ๋ํฐ๋ง
โโโ cache-spring/ # Spring Framework ํตํฉ
โโโ SBCacheManager # Spring CacheManager ๊ตฌํ
โโโ SBCache # Spring Cache ๊ตฌํ
โโโ boot/ # Auto-Configuration
์์กด์ฑ ๊ทธ๋ํ:
cache-core (๋
๋ฆฝ)
โ
โโโ cache-collection
โโโ cache-loader-*
โโโ cache-metrics
โ
cache-spring
- Lock-free ์ฝ๊ธฐ (๋์ ๋์์ฑ)
- ์ธ๊ทธ๋จผํธ ๊ธฐ๋ฐ ์ ๊ธ (์ฐ๊ธฐ ๋ถ์ฐ)
- O(1) ํ๊ท ์๊ฐ ๋ณต์ก๋
SYNC (์ค๋ณต ๋ก๋ฉ):
Thread 1: load (300ms)
Thread 2: load (300ms)
Thread 3: load (300ms)
์ด DB ์ฟผ๋ฆฌ: 3ํ
ASYNC (ํ ๋ฒ๋ง ๋ก๋ฉ):
Thread 1: load (300ms)
Thread 2: wait โ return
Thread 3: wait โ return
์ด DB ์ฟผ๋ฆฌ: 1ํ
| ์ ๋ต | ๋จ์ผ ์ฐ๊ธฐ | 100๊ฐ ์ฐ๊ธฐ | DB ๋ถํ |
|---|---|---|---|
| WRITE_THROUGH | 10ms | 1000ms | ๋์ |
| WRITE_BEHIND | 1ms | 100ms | ๋ฎ์ |
- ์ฌ์ฉ์๋ ํญ์ ์บ์ ํํธ ๊ฒฝํ
- ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ฐฑ์ โ ์๋ต ์๊ฐ ๋จ์ถ
# ์ ์ฒด ํ
์คํธ ์คํ
mvn test
# ํน์ ๋ชจ๋ ํ
์คํธ
cd cache-collection && mvn test
# ํ
์คํธ ๊ฒฐ๊ณผ
Tests run: 148, Failures: 0, Errors: 0, Skipped: 1ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง:
- SBCacheMap: 45๊ฐ ํ ์คํธ
- ReferenceType: 7๊ฐ ํ ์คํธ
- EvictionPolicy: 8๊ฐ ํ ์คํธ
- Spring Integration: 48๊ฐ ํ ์คํธ
- โ JDBCLoader: DataSource ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ก๋ฉ
- โ FileLoader: ํ์ผ ์์คํ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ก๋ฉ
- โ 5๊ฐ์ง ์ถ์ถ ์ ์ฑ : LRU, LFU, FIFO, RANDOM, TTL
- โ Strategy ํจํด์ผ๋ก ํ์ฅ ๊ฐ๋ฅ
- โ maxSize ์ด๊ณผ ์ ์๋ ์ถ์ถ
- โ STRONG, SOFT, WEAK ์ฐธ์กฐ ํ์
- โ ReferenceBasedStorage ๊ตฌํ
- โ ReferenceQueue๋ฅผ ํตํ ์๋ ์ ๋ฆฌ
- โ GC์ ํ๋ ฅํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
- โ ๋๊ธฐ/๋น๋๊ธฐ ์ฐ๊ธฐ ์ ๋ต
- โ ๋ฐฑ์๋ ์ ์ฅ์ ๋๊ธฐํ
- โ ๋ฐฐ์น ์ฒ๋ฆฌ ์ง์
- โ TTL 50% ๋๋ฌ ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ฐฑ์
- โ ํญ์ ์ ์ ํ ๋ฐ์ดํฐ ์ ๊ณต
- โ ์ฌ์ฉ์ ๊ฒฝํ ํฅ์
- โ SBCacheManager, SBCache ๊ตฌํ
- โ Auto-Configuration ์ง์
- โ YAML/Properties ์ค์
- โ CacheMetrics: Hit Rate, Load Time
- โ Actuator Health Indicator
- โ Prometheus/Micrometer ํตํฉ
# ์ ์ฒด ๋น๋
mvn clean install
# ํน์ ๋ชจ๋ ๋น๋
cd cache-collection && mvn clean install
# ํ
์คํธ ์คํต
mvn clean install -DskipTestsApache License 2.0 - ์์ธ ๋ด์ฉ์ LICENSE ํ์ผ์ ์ฐธ์กฐํ์ธ์.
๊ธฐ์ฌ๋ฅผ ํ์ํฉ๋๋ค! Issue ์ ๋ณด ๋ฐ Pull Request๋ฅผ ์์ ๋กญ๊ฒ ์ ์ถํด์ฃผ์ธ์.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'feat: Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- GitHub Issues: sb-cached-collection/issues
- Email: [email protected]
Made with โค๏ธ by ScriptonBaseStar Team