이런 의미로 단순 캐시로 쓰기에는 너무 많은 기능을 가지고 있다고 봐야합니다. 그런만큼 메모리에서만 작동하는 캐시시스템보다는 성능은 느립니다.
하지만 대용량 데이터나 캐시해야 할 양이 엄청 많다면? 그리고 한번 캐시한 거 영구 저장이 되면 좋겠다면? (물론 삭제 기능은 있고...)
에이 그런거라면 차라리 주 저장소를 Document DB로 바꾸는게 낫죠..
아 그렇긴 하네요... 근데, 개발자들이 NoSQL을 아주 단순히 쓰는 큰 이유는 RDBMS 처럼 관계형 데이터 처리에 익숙해서 key-value나 Document, grid 에 대한 처리에 난감해 한다는 거지요. 이 문제는 hibernate-ogm 으로 어떻게 해결하는지 향후 글을 써 보겠습니다.
우선 MongoDB 로도 가능하다는 보여 드리죠.
우선 제가 사용한 라이브러리는 spring-data-mongo, mongo-java-driver 입니다.
1. MongoCacheManager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Slf4j | |
public class MongoCacheManager extends AbstractTransactionSupportingCacheManager { | |
private MongoTemplate mongoTemplate; | |
public MongoCacheManager(MongoTemplate mongoTemplate) { | |
Guard.shouldNotBeNull(mongoTemplate, "mongoTemplate"); | |
this.mongoTemplate = mongoTemplate; | |
} | |
@Override | |
protected Collection<? extends Cache> loadCaches() { | |
Collection<Cache> caches = Lists.newArrayList(); | |
for (String name : getCacheNames()) { | |
caches.add(new MongoCache(name, mongoTemplate)); | |
} | |
return caches; | |
} | |
@Override | |
public Cache getCache(String name) { | |
synchronized (this) { | |
Cache cache = super.getCache(name); | |
if (cache == null) { | |
cache = new MongoCache(name, mongoTemplate); | |
addCache(cache); | |
} | |
return cache; | |
} | |
} | |
} |
2. MongoCache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Slf4j | |
public class MongoCache implements Cache { | |
private String name; | |
private MongoTemplate mongoTemplate; | |
public MongoCache(String name, MongoTemplate mongoTemplate) { | |
Guard.shouldNotBeEmpty(name, "name"); | |
Guard.shouldNotBeNull(mongoTemplate, "mongoTemplate"); | |
this.name = name; | |
this.mongoTemplate = mongoTemplate; | |
if (log.isDebugEnabled()) | |
log.debug("MongoCache를 생성합니다. name=[{}], mongodb=[{}]", name, mongoTemplate.getDb().getName()); | |
} | |
@Override | |
public String getName() { | |
return name; | |
} | |
@Override | |
public Object getNativeCache() { | |
return mongoTemplate; | |
} | |
@Override | |
public ValueWrapper get(Object key) { | |
Guard.shouldNotBeNull(key, "key"); | |
CacheItem item = mongoTemplate.findOne(new Query(Criteria.where("key").is(key)), CacheItem.class, name); | |
Object result = null; | |
if (item != null) { | |
result = item.getValue(); | |
if (log.isDebugEnabled()) | |
log.debug("캐시 값을 로드했습니다. key=[{}], value=[{}]", key, result); | |
} | |
SimpleValueWrapper wrapper = null; | |
if (result != null) | |
wrapper = new SimpleValueWrapper(result); | |
return wrapper; | |
} | |
@Override | |
public void put(Object key, Object value) { | |
Guard.shouldNotBeNull(key, "key"); | |
if (!mongoTemplate.collectionExists(name)) | |
mongoTemplate.createCollection(name); | |
if (log.isDebugEnabled()) | |
log.debug("캐시에 값을 저장합니다. key=[{}], value=[{}]", key, value); | |
if (get(key) == null) | |
mongoTemplate.insert(new CacheItem(key, value), name); | |
else | |
mongoTemplate.upsert(new Query(Criteria.where("key").is(key)), | |
Update.update("value", value), | |
name); | |
} | |
@Override | |
public void evict(Object key) { | |
Guard.shouldNotBeNull(key, "key"); | |
mongoTemplate.remove(new Query(Criteria.where("key").is(key)), name); | |
} | |
@Override | |
public void clear() { | |
mongoTemplate.dropCollection(name); | |
} | |
@Getter | |
@Setter | |
public static class CacheItem implements Serializable { | |
private Object key; | |
private Object value; | |
public CacheItem() { this(null, null); } | |
public CacheItem(Object key, Object value) { | |
this.key = key; | |
this.value = value; | |
} | |
@Override | |
public String toString() { | |
return "CacheItem@{key=" + key + ", value=" + value + "}"; | |
} | |
} | |
} |
MongoCacheManager 는 별 내용 없구요... MongCache는 Spring 의 MongoTemplate 를 적극 이용하여, 저장/검색을 수행하였습니다.
상세 내용은 Spring Data Mongo 매뉴얼을 보시면 되겠습니다.
3. MongoCacheConfiguration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Configuration | |
@EnableCaching | |
@ComponentScan(basePackageClasses = {UserRepository.class}) | |
@Slf4j | |
public class MongoCacheConfiguration extends AbstractMongoConfiguration { | |
@Override | |
protected String getDatabaseName() { | |
return "debop4j_core_cache"; | |
} | |
@Override | |
@Bean | |
public Mongo mongo() throws Exception { | |
return new Mongo("localhost"); | |
} | |
@Bean | |
public MongoCacheManager mongoCacheManager() throws Exception { | |
return new MongoCacheManager(mongoTemplate()); | |
} | |
} |
너무 간단하죠? 나머지 테스트 코드는 앞 EhCache, Memcached 의 예와 똑같아서 생략하겠습니다.
MongoDB의 경우 Database를 지정해주고, 각 캐시명을 Collection 명으로 매핑합니다.
그럼 이만...
댓글 없음:
댓글 쓰기