LabHub
学习 学习路径 课程

Redis 与缓存

实现 cache-aside 并测命中率

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

cache-aside 를 직접 구현해 미스와 히트의 비용 차이를 숫자로 확인하고, 네거티브 캐싱과 버전 키 무효화까지 붙인다.

왜 중요한가

cache-aside 는 가장 흔한 패턴이라 코드는 어디서든 복사할 수 있습니다. 정작 어려운 것은 세부입니다. 쓰기 시 캐시를 갱신할지 삭제할지, 없는 데이터를 캐시할지, 그리고 전체를 한 번에 무효화해야 할 때 무엇을 할지입니다. 첫 번째 — 갱신이 아니라 삭제가 정답인 이유는 두 쓰기가 엇갈릴 때 갱신은 옛 값을 남길 수 있지만 삭제는 그렇지 않기 때문입니다. 두 번째 — 존재하지 않는 키를 반복 조회하는 트래픽은 캐시를 통과해 원본을 그대로 때립니다. 이것을 캐시 관통이라고 하고, 짧은 TTL 의 네거티브 캐싱으로 막습니다. 세 번째 — 상품 카테고리가 통째로 바뀌었을 때 관련 키를 전부 찾아 지우는 것은 의존성 추적 지옥입니다. 버전 키는 그것을 버전 숫자 하나 올리는 일로 바꿉니다.

단계

  1. /opt/app/slowdb.py 를 127.0.0.1:8151 에 띄운다. GET /item/1 이 200 을 주고 GET /stats{"queries":<n>} 를 준다.
  2. /root/ca/cache.pyget_item(id) 를 만든다. 캐시 미스면 원본을 조회해 item:<id> 키에 300초로 저장한다. /root/ca/miss.outsource=origin ms=<정수> 를 적고 ms 는 100 이상이어야 한다.
  3. 같은 항목을 다시 조회한다. /root/ca/hit.outsource=cache ms=<정수> origin_queries_delta=0 을 적고 ms 는 50 미만이어야 한다.
  4. /root/ca/update.py 는 원본을 갱신한 뒤 캐시 키를 삭제한다. 소스에 갱신값을 캐시에 다시 쓰는 코드가 있으면 안 된다. 실행 후 EXISTS item:1 이 0 이어야 한다.
  5. 존재하지 않는 item:9999 를 조회하면 원본이 404 를 주고, 그 결과를 60초 수명으로 캐시한다. 두 번째 조회에서 원본 조회 수가 늘지 않아야 하고 TTL item:9999 가 1 이상 60 이하여야 한다.
  6. /root/ca/bench.py 로 100회 조회(고유 키 20개)를 돌리고 /root/ca/hitratio.txthits=<n> misses=<n> ratio=<소수> 를 적는다. ratio 는 0.75 이상이어야 한다.
  7. 캐시 키를 v<버전>:item:<id> 형태로 바꾸고 버전을 ver:item 키로 관리한다. 버전을 올린 뒤 조회하면 원본 조회 수가 늘어야 한다. /root/ca/version.outold_ver=<n> new_ver=<n> refetched=true 를 적는다.

참고

느린 원본 띄우기

/opt/app/slowdb.py 를 127.0.0.1:8151 에 띄운다. GET /item/1 이 200 을 주고 GET /stats{"queries":<n>} 를 준다.

/opt/app/slowdb.py 는 조회마다 일부러 시간을 씁니다. 자기가 받은 조회 수도 알려 줍니다.

캐시 미스 경로 만들기

/root/ca/cache.pyget_item(id) 를 만든다. 캐시 미스면 원본을 조회해 item:<id> 키에 300초로 저장한다. /root/ca/miss.outsource=origin ms=<정수> 를 적고 ms 는 100 이상이어야 한다.

캐시에 없으면 원본을 다녀와 캐시에 넣고 돌려줍니다. 첫 조회는 느릴 수밖에 없습니다.

두 번째 조회를 캐시로 처리하기

같은 항목을 다시 조회한다. /root/ca/hit.outsource=cache ms=<정수> origin_queries_delta=0 을 적고 ms 는 50 미만이어야 한다.

같은 키를 다시 조회하면 원본에 안 가야 합니다. 원본 조회 수가 늘지 않는지 확인하세요.

갱신 시 캐시를 삭제하기

/root/ca/update.py 는 원본을 갱신한 뒤 캐시 키를 삭제한다. 소스에 갱신값을 캐시에 다시 쓰는 코드가 있으면 안 된다. 실행 후 EXISTS item:1 이 0 이어야 한다.

갱신이 아니라 삭제입니다. 왜 그런지는 읽기 자료에 있습니다. 다음 조회가 알아서 최신을 채웁니다.

없는 키를 네거티브 캐싱하기

존재하지 않는 item:9999 를 조회하면 원본이 404 를 주고, 그 결과를 60초 수명으로 캐시한다. 두 번째 조회에서 원본 조회 수가 늘지 않아야 하고 TTL item:9999 가 1 이상 60 이하여야 한다.

존재하지 않는 키를 반복 조회하면 매번 원본에 갑니다. 짧은 수명으로 '없음'도 캐시하세요.

적중률 측정하기

/root/ca/bench.py 로 100회 조회(고유 키 20개)를 돌리고 /root/ca/hitratio.txthits=<n> misses=<n> ratio=<소수> 를 적는다. ratio 는 0.75 이상이어야 한다.

히트와 미스를 각각 세어 비율을 냅니다. 요청 분포에 따라 값이 달라집니다.

버전 키로 전체 무효화하기

캐시 키를 v<버전>:item:<id> 형태로 바꾸고 버전을 ver:item 키로 관리한다. 버전을 올린 뒤 조회하면 원본 조회 수가 늘어야 한다. /root/ca/version.outold_ver=<n> new_ver=<n> refetched=true 를 적는다.

키 이름에 버전을 심으면 버전만 올려도 옛 키를 아무도 안 찾습니다. 삭제를 직접 하지 않아도 됩니다.