본문 바로가기

MY개발생각

[개발생각] 캐시 스탬피드

캐시 스탬피드를 실무에서 겪었던때는 다음 티스토리팀에 다닐때, 처음으로 겪어봤다. 그때는 이게 왜이러지 하고 그냥 넘겼었는데...

지금 생각하니 참 무지했다. ㅠ 

 

캐시를 사용할때, 캐시가 있으면, 캐시된값을 전달하고, 캐시가 없으면, 디비등에서 조회하여 캐시를 만들고,

해당값을 전달하는 방식으로 코딩을 자주 한다.

 

여기서 중요한점은,  동시성이 중요한 시스템에서는 (대량 트래픽 서비스),

캐시가 없는 시점에 디비를 조회하는 행위를 여러 요청이 동시에 할수가 있다는 점이다.

캐시가 없는 시점에 캐시를 만드는 경우, 락을 걸지않는 이상,

캐시를 만드는 행위를 동시에 여러 요청들이 수행하게 된다.

 

여기서 중복 조회 이슈가 발생하게 된다.

엄밀히 보면, 하나의 요청만 디비를 조회하고, 캐시를 만들면 되는 건데, 동시에 같은 행위를 여러번 하는 꼴이라,

잘못하면, 디비나 캐시시스템에 무리가 갈수있는 위험한 상태가 된다.

 

이런 경우와 같이,

캐시를 만들기 위한 중복 요청이 마치 물소떼(스탬피드)와 같이 동시에 몰려온다는 뜻에서 

캐시 스탬피드 문제라고 한다...

 

이걸 해결하기위해서는 캐시가 없어서, 디비를 조회하는 시점에 락을 걸어 하나의 요청만 디비를 조회하여 캐시를 만들도록 하면 된다.

하지만, 락을 걸면,

결국 나머지 요청들은 대기를 하거나, 예외를 발생하도록 만들어야 하기때문에,

사용자 경험이나, 시스템적으로도 좋은 설계는 아니게 된다.

(스레드 블럭, 또는 스핀락으로인한 cpu로드 올라감.)

 

이런경우 사용하는 가장좋은 방법중에 하나가, 캐시 만료시간을  실제 만료시간 + 랜덤 시간(0~20초)으로 설정하는 방법이 있다.

실제 만료시간에 랜던시간을 설정하여 모두 같은 시간에 캐시를 갱신하는 동시성을 줄이고,

최대한 겹치는 경우의 수를 줄이기 위한 방법이다. 

 

이러면 동시성이 랜던시간의 갭만큼으로 확률적으로 요청이 분산되기 때문에 동시성의 문제를 해결할수있다.

좀더 하드코어버전은 저 랜덤시간을 히스토그램을 통해,

실제 만료가 많이 일어나는 시간을 피하여 설정하도록 통계학적 알고리즘을 사용하여 시간을 더하는 방식도 있지만,

이렇게 까지 할 필요는 개인적으로는 없을듯하다. (배보다 배꼽이 클수있다 ㅋ)

 

+a의 랜덤시간을 캐시 만료시간에 더하도록하여, 동시성을 줄이는 방법만 사용하여도 충분히 캐시 스탬피드 문제는 해결할수있다.

 

온디멘드로 캐시를 만들고 사용하는 방법을 사용하는 경우, 캐시 스탬피드 문제에 대해서 한번은 꼭 생각해 보아야하고,

경우에 따라서는, 배치를 통해 미리 캐시를 하나의 프로세스에서 만들고,

그것을 사용만 하는 설계로 만드는게 더 좋은경우도 있다는것을 알아야 한다.

 

참고로, 레디스 같은 시스템을 사용하는경우, 로컬캐시를 같이 사용하면, 성능상으로 좀더 이점을 가져올수있다. (네트워크 비용 절감)

로컬캐시 없음 ---> 레디스 조회 ---> 레디스 없음 ---> 디비 조회 ---> 로컬캐시 생성 ---> 레디스 캐시 생성