Skip to main content

7. 캐시

캐시는 다음의 특징을 가집니다.

  • 캐시는 불필요한 데이터 전송을 줄여서, 네트워크 비용을 줄여줍니다.
  • 캐시는 네트워크 병목을 줄여줍니다. 대역폭을 늘리지 않고도 페이지를 빨리 불러올 수 있게 됩니다.
  • 캐시는 원 서버에 대한 요청을 줄여줍니다. 서버는 부하를 줄일 수 있으며 더 빨리 응답할 수 있게 됩니다.
  • 페이지를 먼 곳에서 불러올수록 시간이 많이 걸리는데, 캐시는 거리로 인한 지연을 줄여줍니다.

7.1 불필요한 데이터 전송#

  • 서버에서 똑같은 바이트를 네트워크를 통해서 반복해서 이동하는 경우는 네트워크의 대역폭을 잡아먹고, 전송을 니르고, 웹 서버에 부하를 줍니다.
  • 캐시를 이용하면 첫번째 서버 응답은 캐시에 보관됩니다. 이러한 사본이 요청에 대한 응답으로 사용될 수 있기 때문에 원 서버가 중복해서 트래픽을 주고받는 낭비가 줄어듭니다.

7.2 대역폭 병목#

  • 캐시는 네트워크 병목을 줄여줍니다.
  • 클라이언트들이 서버에 접근할 때의 속도는 그 경로에 있는 가장 느린 네트워크 속도와 같습니다. 이때 빠른 LAN에 있는 캐시에서 서버를 가져오면 성능을 높일 수 있습니다.

7.3 갑작스런 요청 쇄도(Flash Crowds)#

  • 캐싱은 갑작스런 요청 쇄도에 대처하기위해 중요합니다.

7.4 거리로 인한 지연#

  • 대역폭이 문제가 되지않더라도 거리가 문제가 될 수 있습니다.
  • 모든 네트워크 라우터는 제 각각 인터넷 트래픽을 지연시킵니다.
  • 클라이언트와 서버 사이에 라우터가 그다지 많지 않아도, 빛의 속도 그자체가 유의미한 지연을 만듭니다.
  • 캐시를 통해서 이러한 지연을 줄일 수 있습니다.

7.5 적중과 부적중#

  • 캐시가 세상 모든 문서의 사본을 저장하지는 않습니다.
  • 캐시가 적중되는 경우에서는 캐시 적중(cache hit)라고 하며 대응하는 사본이 없는 경우, 원서버로 전달하고 이를 캐시 부적중(cache miss)라고 합니다.

재검사(Revalidation)#

  • 원 서버 콘텐츠는 변경될 수 있기 때문에, 캐시는 반드시 사본이 최신인지를 검사해야합니다. 이를 신선도 검사라고 합니다.
  • 캐시는 스스로 원한다면 언제든 서버를 재검수할 수 있습니다.
  • 캐시는 캐시된 사본의 재검사가 필요할 때, 원 서버에 작은 재검사 요청을 보내며 변경되지 않았다면 서버는 304 Not Modified 응답을 보냅니다.

다음의 경우 If-Modified-Since 헤더를 보내며, 이는 서버에게 캐시가 변경된 이후 사본을 보내달라는 요청입니다.

  • 서버 콘텐츠가 변경되지 않은 경우 : 재검사 적중
  • 서버 콘텐츠가 변경된 경우 : 재검수 부적중, 다시 보냅니다. (200 OK)
  • 객체가 삭제된 경우 : 객체 삭제, 사본을 삭제합니다. (404 Not Found)

적중률#

  • 캐시가 요청을 처리하는 비율을 캐시 적중률이라고 합니다. (100%는 모든 요청이 캐시 적중임을 의미합니다.)
  • 캐시 관리자는 캐시 적중률이 100%에 근접되는 것이 좋으나 현실은 40%라도 좋습니다.

바이트 적중률#

  • 문서들이 모두 같은 크기인 것은 아니기 때문에 캐시 적중률이 모든 것을 말해주는 것은 아닙니다.
  • 바이트 단위 적중률은 캐시를 통해 제공된 모든 바이트의 비율을 표현합니다. (100%는 모든 바이트가 캐시에서 왔으며 어떤 트래픽도 인터넷으로 나가지 않음을 의미합니다.)
  • 문서적중률과 바이트 단위 적중률은 모두 중요합니다.

적중과 부적중의 구별#

  • HTTP는 클라이언트에게 응답이 캐시 적중이었는지, 우너 서버 접근인지 말해줄 수 있는 방법을 제공하지 않습니다.
  • 클라이언트가 응답이 캐시에서 왔는지 알아내는 방법 중 하나는 Date 헤더를 이용하는 것입니다.
  • 또 다른 방법 중 하나는 응답이 얼마나 오래되었는지 확인하는 Age헤더를 사용하는 것입니다.

7.6 캐시 토폴로지#

  • 개인 전용 캐시 : 한명에게 할당된 캐시
  • 공용 캐시 : 사용자 집단에게 자주 쓰이는 페이지

개인 전용 캐시#

  • 많은 저장 공간이 필요없으므로 작고 저렴합니다.
  • 익스플로러는 캐시된 문서를 임시 파일이라 불립니다.

공용 프락시 캐시#

  • 프락시 캐시는 로컬 캐시에서 문서를 제공하거나, 혹은 사용자의 입장에서 서버에 접근합니다.
  • 공용 캐시에는 여러 사용자가 접근하기 때문에 불필요한 트래픽을 줄일 수 있습니다.
  • 각 개인 전용 캐시는 네트워크를 거쳐 여러번 가져옵니다.

프락시 캐시 계층들#

  • 클라이언트 주위에는 작고 저렴한 캐시를 사용하고 계층 상단에는 많은 사용자들에 의해 공유되는 문서를 유지합니다.
  • 다만, 프락시 연쇄가 깊어질수록 각 중간 프락시는 현저한 성능 저하가 발생합니다.

캐시망, 콘텐츠 라우팅, 피어링#

캐시망 안에서 콘텐츠 라우팅을 위하 설계된 캐시들은 다음의 일을 할 수 있습니다.

  • URL에 근거하여 부모 캐시와 원 서버 중 하나를 동적으로 선택합니다.
  • URL에 근거하여 특정 부모 캐시를 동적으로 선택합니다.
  • 부모 캐시에게 가기 전에 캐시된 사본을 로컬에서 선택합니다.
  • 다른 캐시들이 그들의 캐시된 콘텐츠에 부분적으로 접근할 수 있도록 허용하되, 그들의 캐시를 통한 Internet transit은 허용되지 않습니다.

7.7 캐시 처리단계#

일반적으로 HTTP GET 메시지 하나를 처리하는 기본적인 캐시 처리 절차는 일곱 단계로 이루어집니다.

  1. 요청 받기 - 캐시는 네트워크로부터 도착한 요청 메시지를 읽습니다.
  2. 파싱 - 캐시는 메시지를 파싱하여 URL과 헤더들을 추출합니다.
  3. 검색 - 캐시는 로컬 복사본이 있는지 검사하고, 사본이 없다면 사본을 받아옵니다.
  4. 신선도 검사 - 캐시는 캐시된 사본이 충분히 신선한지 검사하고, 신선하지 않다면 변경사항이 있는지 서버에게 확인합니다.
  5. 응답 생성 - 캐시는 새로운헤더와 캐시된 본문으로 응답 메시지를 만듭니다.
  6. 발송 - 캐시는 네트워크를 통해 응답을 클라이언트에게 돌려줍니다.
  7. 로깅 - 선택적으로, 캐시는 로그파일에 트랜잭션에 대해 서술한 로그 하나를 남깁니다.

단계 1: 요청받기#

  • 캐시는 네트워크 커넥션에서의 활동을 감지하고, 들어오는 데이터를 읽습니다.
  • 고성능 캐시는 여러 개의 들어오는 커넥션들로부터 데이터를 동시에 읽어들이고 메시지 전체가 도착하기 전에 트랜잭션 처리를 시작합니다.

단계 2: 파싱#

  • 캐시는 요청 메시지를 여러 부분으로 파싱하여 헤더 부분을 조작하기 쉬운 자료 구조에 담습니다.
  • 이를 통해 캐싱 소프트웨어가 헤더 필드를 처리하고 조작하기 쉽게 만들어줍니다.

단계 3: 검색#

  • 캐시는 URI을 알아내고 그에 해당하는 로컬 사본이 있는지 검사합니다.
  • 캐시된 객체는 서버 응답 본문과 원 서버 응답 헤더를 포함하고 있으므로, 캐시 적중 동안 올바른 서버 헤더가 반환될 수 있습니다.

단계 4: 신선도 검사#

  • HTTP는 캐시가 일정 기간 동안 서버 문서의 사본을 보유할 수 있도록 해줍니다. 이 기간이 넘기면 신선하지 않은 것으로 판단합니다.
  • HTTP의 신선도 검사 규칙이 매우 복잡합니다.

단계 5: 응답 생성#

  • 캐시는 캐시된 서버 응답 헤더를 토대로 응답 헤더를 생성합니다.
  • 캐시는 클라이언트에 맞게 이 헤더를 조정해야 하는 책임이 있습니다.
  • 다만 캐시는 Date 헤더를 조정하면 안됩니다.

단계 6: 발생#

  • 응답 헤더가 준비되면, 캐시는 응답을 클라이언트에게 돌려줍니다.
  • 프락시 캐시는 클라이언트와의 커넥션을 유지할 필요가 있으며, 고성능 캐시는 콘텐츠 복사를 피함으로써 데이터를 효과적으로 전송하기 위해 노력합니다.

단계 7: 로깅#

  • 대부분의 캐시는 로그 파일과 캐시 사용에 대한 통계를 유지합니다.

캐시 처리 플로 차트#

image


7.8 사본을 신선하게 유지하기#

  • 캐시된 사본 모두가 서버의 문서와 항상 일치하는 것은 아닙니다.
  • HTTP는 캐시된 사본이 서버와 일치할 수 있도록 유지하게하는 메커니즘이 있으며, 이를 문서 만료와 서버 재검사라 부릅니다.

문서 만료#

  • Cache-ControlExpires라는 특별한 헤더를 이용해서 원 서버가 각 문서에 유효기간을 붙일 수 있게 해줍니다.
  • 캐시 문서가 만료되기 전에 캐시는 필요하다면 서버와으 접촉 없이 사본을 제공할 수 있습니다.

유효기간과 나이#

헤더설명
Cache-Control: max-age문서의 최대 나이를 정의합니다.
Expires절대 유효기간을 명시합니다.

서버 재검사#

서버 재검사 : 캐시가 원 서버에게 문서가 변경되었는지의 여부를 물어볼 필요가 있음을 의미합니다.

  • 재검사 결과 콘텐츠가 변경되었다면, 캐시는 그 문서의 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보내줍니다.
  • 재검사 결과 콘텐츠가 변경되지 않았다면, 캐시는 새 만료일을 포함한 새 헤더들만 가져와서 캐시 안의 헤더들을 갱신합니다.

조건부 메서드와의 재검사#

HTTP의 조건부 메서드는 재검사를 효율적으로 만들어줍니다.

아래는 캐시 재검사에 사용되는 두 조건부 헤더입니다.

헤더설명
If-Modified-Since: <date>문서가 주어진 날짜 이후로 수정되면 요청 메서드를 처리합니다.
If-None-Match: <date>캐시된 태그가 서버에 있는 문서의 태그와 다를 때만 요청을 처리합니다.

If-Modified-Since: 날짜 재검사#

  • 일반적으로 IMS 요청이라하며, 서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 합니다.
  • If-Modified-SinceLast-Modified 헤더와 함께 동작합니다.
  • 일부 웹서버는 실제 날짜로 구분하지는 않고, 문자열 비교를 수행할 때도 있습니다.

If-None-Match: 엔티티 태그 재검사#

  • 최근 변경 일시 재검사가 적절히 일어나기 힘든 경우도 있습니다.
    • 문서가 일정 시간 간격으로 다시 쓰이나 같은 데이터를 포함하는 경우
    • 사소한 것의 변경인 경우
    • 최근 변경 일시를 정확하게 판별할 수 없는 경우
    • 1초보다 작은 간격으로 갱신되는 경우
  • 이때 버저닝 등으로 확인할 수 있습니다.
  • 캐시가 여러 개의 사본을 가지고 있는 경우, 여러개의 엔티티 태그를 포함할 수 있습니다.

약한 검사기와 강한 검사기#

  • 엔티티 태그와 최근 변경일시는 둘 다 캐시 검사기입니다.
  • 약한 검사기는 콘텐츠가 조금 변경되도 그 정도면 같다고 판단할 수 있습니다. (W/로 약한 접두사로 구별합니다.)
  • 강한 검사기는 콘텐츠가 바뀔때마다 바뀝니다.

언제 엔티티 태그를 사용하고 언제 Last-Modified 일시를 사용하는가#

  • 서버가 엔티티 태그를 반환했다면, 반드시 엔티티 태그 검사기를 사용해야 합니다.
  • 원 서버는 실현 불가능하지만 않다면 엔티티 태그 검사기를 보내야하고, 이점이 있다면 강한 엔티티 태그 대신 약한 엔티티 태그르 보낼 수도 있습니다.
  • 또한 Last-Modified 값을 같이 보내는 것도 선호합니다.

7.9 캐시 제어#

HTTP 분서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버에는 여러가지 방법이 존재합니다.

  • Cache-Control: no-store 헤더를 응답에 첨부할 수 있습니다.
  • Cache-Control: no-cache 헤더를 응답에 첨부할 수 있습니다.
  • Cache-Control: must-revalidate 헤더를 응답에 첨부할 수 있습니다.
  • Cache-Control: max-age 헤더를 응답에 첨부할 수 있습니다.
  • Expires 날짜 헤더를 응답에 첨부할 수 있습니다.
  • 아무 만료 정보도 주지 않고, 캐시가 스스로 체험적인 방법으로 결정하게 할 수 있습니다.

no-cache와 no-store 응답 헤더#

  • HTTP/1.1은 신선도를 관라히가 위해, 객체를 캐시하는 것을 제한하거나 캐시된 객체를 제공하는 여러 방법을 제공합니다.
  • no-store 표시 응답은 캐시가 그 응답의 사본을 만드는 것을 금지합니다.
  • no-cache 응답은 로컬 캐시저장소에 저장될 수는 잇으나, 서버와 재검사를 하지 않는 경우에는 사용하지 못합니다.

Max-Age 응답 헤더#

  • max-age 헤더는 신선하다고 간주된 문서로 서버로부터 온 이후로 흐른 시간 입니다.
  • s-maxage 는 max-age와 비슷하나 공용 캐시에만 적용합니다.
  • maximum aging을 설정함으로서 캐시를 조절할 수 있으며 0으로 하면 문서를 캐시하지 않거나 리프레시하지 않도록 요청할 수 있습니다.

Expires 응답 헤더#

  • 초 단위의 시간 대신 실제 만료 날짜를 명시합니다.
  • 다만, 많은 서버가 동기화되지 않는 경우가 있기에 부정적인 의견도 있습니다.
  • Expires: 0 응답 헤더는 문법 위반이므로, 몇몇 소프트웨어와 문제를 일으킬 수 있습니다.

Must-Revalidate 응답 헤더#

  • 성능을 개선하기 위해 신선하지 않은 객체를 제공하는 것을 막고 캐시가 엄격하게 하기를 원한다면 Cache-Control: must-revalidate 를 사용할 수 있습니다.
  • 이 경우, 원서버에서 받아올 수 없다면 504 Gateway Timeout Error를 반환합니다.

휴리스틱 만료#

  • Cache-Control: max-age 헤더나 Expires 헤더 중 어느 것도 포함하지 않는다면, 캐시는 경험적으로 최대 나이를 계산합니다.
  • 일반적으로 문서가 얼마나 자주 바뀌는지에 대한 추정으로 나이를 계산합니다. (LM 인자 알고리즘 : 변경이 예전이라면, 지금도 아마 괜찮을 것)
  • 생각보다 흔하게 발생됩니다.

클라이언트 신선도 계약#

  • 웹 브라우저에서는 캐시를 강제로 갱신하는 기능도 보통 있습니다.

주의할점#

  • 문서 만료는 완벽한 시스템이 아니며, 퍼블리셔가 잘못해서 유효기간을 나중으로 설정한다면 변경이 반영이 되지 않습니다. 이러한 이유로 기간을 길게 잡지 않습니다.
  • 유효기간을 사용하지 않아서, 문서가 얼마나 오랫동안 신선할 것인지 캐시가 알기 어렵게 되는 경우도 많습니다.

7.10 캐시 제어 설정#

웹 서버는 캐시 제어와 만료 HTTP 헤더들을 설정하는 서로 다른 메커니즘을 제공합니다.

아파치로 HTTP 헤더 제어하기#

  • mod_headers : 개별 헤더들을 설정하게 해줍니다.
  • mod_expires : 적절한 만료 날짜가 담긴 Expires 헤더를 자동으로 생성하는 프로그램 로직을 제공합니다.
  • mod_cern_meta : HTTP 헤더들의 파일을 틀정 객체와 연결시켜줍니다.

HTTP-EQUIV를 통한 HTML 캐시 제어#

  • HTTP 서버 응답 헤더는 문서의 만료와 캐시 제어 정보를 돌려주기 위해 사용됩니다.
  • HTTP-EQUIV 태그를 통해 웹 서버 설정 파일과의 상호작용 없이 쉽게 HTTP 문서에 HTTP 헤더 정보를 부여합니다.
  • 다만, 이는 매우 서투른 일이고 올바른 HTTP 헤더를 사용해야합니다.

7.11 자세한 알고리즘#

HTTP 명세는 문서의 나이와 캐시 신선도를 계산하는 알고리즘을 제공합니다.

나이와 신선도 수명#

$충분히_신선한지 = ($나이 < $신선도_수명)

  • 캐시가 신선한지 알려주기 위해서는 캐시된 사본의 나이신선도 수명을 알려주면 됩니다.
  • 캐시는 서버 만료 정보와 클라이언트 신선도 요구사항을 조합해서 최대 신선도 수명을 판별합니다.

나이 계산#

$겉보기_나이 = max(0, $응답을_받은_시각 - $Date_헤더값)

$보정된_겉보기_나이 = max($겉보기_나이, $Age_헤더값)

  • 응답의 나이는 응답이 서버에 생성되었을 때부터 지금까지의 총 시간입니다.
  • 일반적으로 Date와 Age 헤더를 분석해서 사용합니다.

겉보기 나이는 Date 헤더에 기반합니다.#

  • 클라이언트와 서버의 시계가 다를 수도 있습니다. 이러한 문제는 발생하는 문제를 클록 스큐라고 부릅니다.
  • 따라서 프락시와 캐시는 날짜를 변경하면 안됩니다.

점층적 나이 계산#

  • 클록 슈크를 해결하기 위해 문서가 프락시나 캐시를 통과할 때마다 Age 헤더에 상대적인 나이를 누적해서 더하도록 합니다.
  • 중간 애플리케이션은 자신의 내부 시계를 통해 문서의 체류시간을 계산할 수 있습니다.

네트워크 지연에 대한 보상#

  • 느린 네트워크나 과부하가 걸린 서버에서 문서가 네트워크에 오래된 경우 추정이 힘들 수 있습니다.
  • 왕복 지연 계산으로 통해 네트워크 지연을 보수적으로 교정할 수 있습니다.

완전한 나이 계산 알고리즘#

$나이 = $문서가_우리의_캐시에_도착했을_때의_나이 + $사본이_얼마나_오래_우리의_캐시에_있었는지

캐시된 문서의 나이

신선도 수명 계산#

  • 신선도 수명은 서버와 클라이언틍의 제약조건에 의존합니다.
  • 캐시는 요구에 따라 충실하게 이행해야합니다.

완전한 서버 신선도 알고리즘#

핵심은 문서의 나이와 신선도 한계라는 두가지 변수가 관련되어 있습니다.

sub 서버_신선도_한계 {
local($휴리스틱, $서버_신선도_한계, $마지막으로_변경된_시각)
$휴리스틱 = 0;
if ($Max_Age_값이_설정되었나) { $서버_신선도_한계 = $Max_Age_값 }
elsif ($Expires_값이설정되었나) { $서버_신선도_한계 = $Expires_값 - $Date_값 }
elsif ($Last_Modified_값이_설정되었나) {
$마지막으로_변경된_시각 = max(0, $Date_값 - $Last_Modified_값)
$서버_신선도_한계 = int($마지막으로_변경된_시각 * $lm_인자);
$휴리스틱 = 1;
} else {
$서버_신선도_한계 = $캐시_최소_수명_기본값;
$휴리스틱 = 1;
}
if ($휴리스틱) {
if ($서버_신선도_한계 > $캐시_최대_수명_기본값) $서버_신선도_한계 = $캐시_최대_수명_기본값
if ($서버_신선도_한계 < $캐시_최소_수명_기본값) $서버_신선도_한계 = $캐시_최소_수명_기본값
}
return $서버_신선도_한계
}
sub 클라이언트가_수정한_신선도_한계 {
$나이_한계 = 서버_신선도_한계()
if ($Max_Stale_값이_설정되었나) {
if ($Max_Stale_값 == $INT_MAX) { $나이_한계 = $INT_MAX }
else { $나이_한계 = 서버_신선도_한계() + $Max_Stale_값 }
}
if ($Min_Fresh_값이_설정되었나) {
$나이_한계 = min($나이_한계, 서버_신선도_한계() - $Min_Fresh_값)
}
if ($Max_Age_값이_설정되었나) { $나이_한계 = min($나이_한계, $Max_Age_값) }
}

7.12 캐시와 광고#

지금까지의 핵심은 캐시가 성능을 개선하고 트래픽을 줄여야합니다. 즉, 캐시는 사용자들에게 더 좋은 경험을 제공해야합니다.

광고 회사의 딜레마#

  • 캐시는 광고를 사용자에게 더 쉽게 제공될 수 있습니다. 그러나, 보는 횟수를 모를 수 있게됩니다.

퍼블리셔의 응답#

  • 오늘날의 광고회사들은 캐시가 광고 시청수를 못가져가도록 광고를 CGI 게이트웨이를 통해 제공합니다.
  • 접근마다 광고 URL을 고쳐서 사용합니다.
  • 다만 이러한 접근은 캐시싱의 긍정적 효과를 감소시킬 수 있습니다.

로그 마이그레이션#

  • 이상적인 해결책 하나는 서버로 요청이 가지 않도록 하는 것입니다.
  • 적중 로그는 크기 때문에 옮기기도 힘들고 표준화가 안되어 조직되어 있지도 않습니다.

적중 측정과 사용량 제한#

  • 이를 해결하기 위해 특정 URL에 대한 캐시 적중 횟수를 정기적으로 서버에게 돌려주는 Meter라고 하는 새 헤더 하나를 추가합니다.
Last updated on