본문 바로가기

프로그래밍/Web programming

Cache

참고 블로그1





Cache는 성능 향상을 위해 사용한다.

브라우저 캐시와 서버단에서의 캐시가 있다.






브라우저 캐시

서버로부터 받아왔던 자원들을 캐시에 저장해 둔다.

일정 기간 같은 리소스 요청은 캐시에 있는 내용을 쓰게 됨으로써 서버와의 통신 비용이 줄어든다.



서버 캐시

DB 조회 비용을 줄이기 위해 주로 사용된다.

자주 변경되지 않는 데이터는 캐싱하여 DB 통신 비용을 줄일 수 있다. 






서버 캐시에 대해서 알아보자.

얘기해볼 것은 Ehcache, Memcached, Redis 






1. Ehcache


Spring을 사용하면 가장 쉽게 사용할 수 있는 캐시이다. 



# Spring boot gradle 설정

compile('org.springframework.boot:spring-boot:spring-bookt-starter-cache'_

compile group: 'net.sf.ehcache', name:'ehcache', version: '2.10.3'




# ehcache.xml 생성

<ehcache>

  <diskStore path="java.io.tmpdir"/>

  <defaultCacht

    maxEntriesLocalHeap="10000"

    maxEntriesLocalDisk="10000"

    eternal="false"

    ..

    memoryStoreEvictionPolicy="LRU"'/>


  <cache name="myCache"

    .../>

</ehcache>



cache 에 name을 부여하면서 캐시를 의미있게 분류해서 사용할 수 있다.


- maxEntriesLocalHeap: 메모리에 생성될 최대 객체 갯수

- maxEntriesLocalDisk: 디스크에 생설될 최대 객체 갯수

- eternal: 저장될 캐시를 삭제할 것인가

- timeToIdleSeconds: 캐시가 사용되지 않은 초 설정, 그후 삭제

- timeToLiveSeconds: 해당 초가 지나면 캐시 삭제

- diskExpiryThreadIntervalSeconds: Disk Expiry 스레드의 작업 수행 간격 설정

- memeoryStoreEvictionPolicy: 객체 갯수가 메모리에 생성될 최대 객체 갯수에 도달하면 객체를 제거하고 새로 추가하는 정책





# Config 파일에 CacheManager를 Bean으로 등록


@Configuration

public class MvcConfig implements WebMvcConfigurer {


  @Bean

  public CacheManager cacheManager() {

    return new EhCacheCacheManager(ehCacheCacheManager().getObject());

  }


  @Bean

  public EhCacheManagerFactoryBean ehCacheCacheManager() {

    EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();

    factory.setConfigLocation(new ClassPathResource("ehcache.xml"));

    factory.setShard(true);

    return factory;

  }

}




# 캐시 적용

@RestController

@EnableCaching

@RequestMapping("/api/my")

public class ApiMyController {


  @Autowired

  private MyService myService;


  @GetMapping("")

  public Iterable<My> list() {

    return myService.gets();

  }


  @PutMapping("/{id}")

  public ResponseEntity<My> update(@PathVariable Long id, @RequestBody MyDto updatedMyDto) {

    return ResponseEntity.status(HttpStatus.OK).body(myService.update(id, updatedMyDto));

  }

}


  

@Service

public class MyService {

  @Autowired

  private MyRepository myRepository;


  @Cacheable(value = "myCache")

  public Iterable<My> gets() {

    return myRepository.findAll();

  }


  @Transactional

  @CacheEvict(value = "myCache", allEntries = true) {

  public My update(Long id, MyDto updatedMyDto) {

    My savedMy = getMyById(id);

    return savedMy.update(updatedMyDto);

  }


  private My getMyById(Long id) {

    return myRepository.findById(id).orElseThrow(NotMatchedException::new);

  }

}



@EnableCache // 캐시 기능을 사용하겠다.

@Cacheable // 해당 케시를 사용하겠다.

@CacheEvict // 헤당 캐시를 갱신하겠다


참고로 저런식으로 비즈니스 로직과 캐싱이 어노테이션에 의해 분리가 된 걸 보고 AOP가 적용되었다고 볼 수 있다!




캐시를 직접 관리하고 싶으면 bash가 있는 Redis나  Memcached를 고려해봐야 한다.  (Memcached는 안써봐서 자세히 모르는 상태..)

그럼 Memcached와 Redis를 다뤄보자.












2. Redis vs Memcached


- Redis는 disk 저장이 가능하여 메모리에만 있던 휘발성 데이터가 사라져도 복구가 가능하다.

- Redis가 더 다양한 데이터 타입을 지원한다.

- Redis가 더 다양한 API를 지원한다.

- 확장성이 있다.


- Redis가 메모리를 더 많이 사용한다.

- Redis는 메모리 파편화가 더 심하다.


많은 데이터를 다뤄야 한다면 Redis가 낫다.

데이터 복구가 필요하다면 Redis를 써야 한다. 


단순 캐싱 용도라면 Memcached를 쓰는게 낫다.