PageSourceSearch

https://hayeon17kim.github.io/posts/cache/

html hayeon17kim.github.io collected 2026-10-03 09:17:02 UTC 34,513 bytes, 35 lines download raw bytes

1<!DOCTYPE html><html lang="kr-KR" ><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="pv-cache-enabled" content="false"><meta name="generator" content="Jekyll v4.2.0" /><meta property="og:title" content="캐시 전략" /><meta name="author" content="hayeon_kim" /><meta property="og:locale" content="kr_KR" /><meta name="description" content="최근 다중 서버 환경에서 로컬 캐시를 글로벌 캐시로 변경해야 하는 상황이 있었는데, 작업을 하면서 예전에 시스템 디자인 강의 때 배운 캐싱 개념을 다시 짚어보게 되었다." /><meta property="og:description" content="최근 다중 서버 환경에서 로컬 캐시를 글로벌 캐시로 변경해야 하는 상황이 있었는데, 작업을 하면서 예전에 시스템 디자인 강의 때 배운 캐싱 개념을 다시 짚어보게 되었다." /><link rel="canonical" href="https://hayeon17kim.github.io//posts/cache/" /><meta property="og:url" content="https://hayeon17kim.github.io//posts/cache/" /><meta property="og:site_name" content="하연" /><meta property="og:type" content="article" /><meta property="article:published_time" content="2022-02-10T00:00:00+08:00" /><meta name="twitter:card" content="summary" /><meta property="twitter:title" content="캐시 전략" /><meta name="twitter:site" content="@MONI56219559" /><meta name="twitter:creator" content="@hayeon_kim" /><meta name="google-site-verification" content="google_meta_tag_verification" /> 
1<script type="application/ld+json"> {"author":{"@type":"Person","name":"hayeon_kim"},"headline":"캐시 전략","dateModified":"2022-02-10T21:37:04+08:00","datePublished":"2022-02-10T00:00:00+08:00","url":"https://hayeon17kim.github.io//posts/cache/","description":"최근 다중 서버 환경에서 로컬 캐시를 글로벌 캐시로 변경해야 하는 상황이 있었는데, 작업을 하면서 예전에 시스템 디자인 강의 때 배운 캐싱 개념을 다시 짚어보게 되었다.","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"https://hayeon17kim.github.io//posts/cache/"},"@context":"https://schema.org"}</script>
1<title>캐시 전략 | 하연</title><link rel="shortcut icon" href="/assets/img/favicons/favicon.ico" type="image/x-icon"><link rel="icon" href="/assets/img/favicons/favicon.ico" type="image/x-icon"><link rel="apple-touch-icon" href="/assets/img/favicons/apple-icon.png"><link rel="apple-touch-icon" href="/assets/img/favicons/apple-icon-precomposed.png"><link rel="apple-touch-icon" sizes="57x57" href="/assets/img/favicons/apple-icon-57x57.png"><link rel="apple-touch-icon" sizes="60x60" href="/assets/img/favicons/apple-icon-60x60.png"><link rel="apple-touch-icon" sizes="72x72" href="/assets/img/favicons/apple-icon-72x72.png"><link rel="apple-touch-icon" sizes="76x76" href="/assets/img/favicons/apple-icon-76x76.png"><link rel="apple-touch-icon" sizes="114x114" href="/assets/img/favicons/apple-icon-114x114.png"><link rel="apple-touch-icon" sizes="120x120" href="/assets/img/favicons/apple-icon-120x120.png"><link rel="apple-touch-icon" sizes="144x144" href="/assets/img/favicons/apple-icon-144x144.png"><link rel="apple-touch-icon" sizes="152x152" href="/assets/img/favicons/apple-icon-152x152.png"><link rel="apple-touch-icon" sizes="180x180" href="/assets/img/favicons/apple-icon-180x180.png"><link rel="icon" type="image/png" sizes="192x192" href="/assets/img/favicons/android-icon-192x192.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="96x96" href="/assets/img/favicons/favicon-96x96.png"><link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicons/favicon-16x16.png"><link rel="manifest" href="/assets/img/favicons/manifest.json"><meta name='msapplication-config' content='/assets/img/favicons/browserconfig.xml'><meta name="msapplication-TileColor" content="#ffffff"><meta name="msapplication-TileImage" content="/assets/img/favicons/ms-icon-144x144.png"><meta name="theme-color" content="#ffffff"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"><link rel="dns-prefetch" href="https://fonts.gstatic.com"><link rel="preconnect" href="https://www.google-analytics.com" crossorigin="use-credentials"><link rel="dns-prefetch" href="https://www.google-analytics.com"><link rel="preconnect" href="https://www.googletagmanager.com" crossorigin="anonymous"><link rel="dns-prefetch" href="https://www.googletagmanager.com"><link rel="preconnect" href="cdn.jsdelivr.net"><link rel="dns-prefetch" href="cdn.jsdelivr.net"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha256-LA89z+k9fjgMKQ/kq4OO2Mrf8VltYml/VES+Rg0fh20=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/[email protected]/css/all.min.css" integrity="sha256-+N4/V/SbAFiW1MPBCXnfnP9QSN3+Keu+NlB+0ev/YKQ=" crossorigin="anonymous"><link rel="stylesheet" href="/assets/css/style.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/afeld/[email protected]/dist/bootstrap-toc.min.css"> 
1<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
1 
1<script defer src="https://cdn.jsdelivr.net/combine/npm/[email protected],npm/bootstrap@4/dist/js/bootstrap.min.js"></script>
1 
1<script defer src="/assets/js/dist/post.min.js"></script>
1 
1<script defer src="/app.js"></script>
vendor: 64 bytes, line 1
1 <script defer src="https://www.googletagmanager.com/gtag/js?id=
1UA-173202420-1
vendor: 12 bytes, line 1
1"></script> 
1<script> document.addEventListener("DOMContentLoaded", function(event) { 
vendor: 127 bytes, line 1
1window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '
1UA-173202420-1
vendor: 4 bytes, line 1
1'); 
1}); </script>
1<body data-spy="scroll" data-target="#toc"><div id="sidebar" class="d-flex flex-column align-items-end"><div class="profile-wrapper text-center"><div id="avatar"> <a href="/" alt="avatar" class="mx-auto"> <img src="/assets/img/bio-photo.jpg" alt="avatar" onerror="this.style.display='none'"> </a></div><div class="site-title mt-3"> <a href="/">하연</a></div><div class="site-subtitle font-italic">어제보다 나은 코드를 위해</div></div><ul class="w-100"><li class="nav-item"> <a href="/" class="nav-link"> <i class="fa-fw fas fa-home ml-xl-3 mr-xl-3 unloaded"></i> <span>HOME</span> </a><li class="nav-item"> <a href="/archives/" class="nav-link"> <i class="fa-fw fas fa-archive ml-xl-3 mr-xl-3 unloaded"></i> <span>ARCHIVES</span> </a><li class="nav-item"> <a href="/tags/" class="nav-link"> <i class="fa-fw fas fa-tags ml-xl-3 mr-xl-3 unloaded"></i> <span>TAGS</span> </a><li class="nav-item"> <a href="/categories/" class="nav-link"> <i class="fa-fw fas fa-stream ml-xl-3 mr-xl-3 unloaded"></i> <span>CATEGORIES</span> </a></ul><div class="sidebar-bottom mt-auto d-flex flex-wrap justify-content-center"> <a href="https://github.com/hayeon17kim" aria-label="github" class="order-3" target="_blank" rel="noopener"> <i class="fab fa-github-alt"></i> </a> <a href="https://twitter.com/MONI56219559" aria-label="twitter" class="order-4" target="_blank" rel="noopener"> <i class="fab fa-twitter"></i> </a> <a href=" javascript:location.href = 'mailto:' + ['hayeon17kim','gmail.com'].join('@')" aria-label="email" class="order-5" > <i class="fas fa-envelope"></i> </a> <a href="/feed.xml" aria-label="rss" class="order-6" > <i class="fas fa-rss"></i> </a> <span class="icon-border order-2"></span> <span id="mode-toggle-wrapper" class="order-1"> <i class="mode-toggle fas fa-adjust"></i> 
1<script type="text/javascript"> class ModeToggle { static get MODE_KEY() { return "mode"; } static get DARK_MODE() { return "dark"; } static get LIGHT_MODE() { return "light"; } constructor() { if (this.hasMode) { if (this.isDarkMode) { if (!this.isSysDarkPrefer) { this.setDark(); } } else { if (this.isSysDarkPrefer) { this.setLight(); } } } var self = this; /* always follow the system prefers */ this.sysDarkPrefers.addListener(function() { if (self.hasMode) { if (self.isDarkMode) { if (!self.isSysDarkPrefer) { self.setDark(); } } else { if (self.isSysDarkPrefer) { self.setLight(); } } self.clearMode(); } self.updateMermaid(); }); } /* constructor() */ setDark() { $('html').attr(ModeToggle.MODE_KEY, ModeToggle.DARK_MODE); sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.DARK_MODE); } setLight() { $('html').attr(ModeToggle.MODE_KEY, ModeToggle.LIGHT_MODE); sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.LIGHT_MODE); } clearMode() { $('html').removeAttr(ModeToggle.MODE_KEY); sessionStorage.removeItem(ModeToggle.MODE_KEY); } get sysDarkPrefers() { return window.matchMedia("(prefers-color-scheme: dark)"); } get isSysDarkPrefer() { return this.sysDarkPrefers.matches; } get isDarkMode() { return this.mode == ModeToggle.DARK_MODE; } get isLightMode() { return this.mode == ModeToggle.LIGHT_MODE; } get hasMode() { return this.mode != null; } get mode() { return sessionStorage.getItem(ModeToggle.MODE_KEY); } /* get the current mode on screen */ get modeStatus() { if (this.isDarkMode || (!this.hasMode && this.isSysDarkPrefer) ) { return ModeToggle.DARK_MODE; } else { return ModeToggle.LIGHT_MODE; } } updateMermaid() { if (typeof mermaid !== "undefined") { let expectedTheme = (this.modeStatus === ModeToggle.DARK_MODE? "dark" : "default"); let config = { theme: expectedTheme }; /* re-render the SVG › <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344> */ $(".mermaid").each(function() { let svgCode = $(this).prev().children().html(); $(this).removeAttr("data-processed"); $(this).html(svgCode); }); mermaid.initialize(config); mermaid.init(undefined, ".mermaid"); } } flipMode() { if (this.hasMode) { if (this.isSysDarkPrefer) { if (this.isLightMode) { this.clearMode(); } else { this.setLight(); } } else { if (this.isDarkMode) { this.clearMode(); } else { this.setDark(); } } } else { if (this.isSysDarkPrefer) { this.setLight(); } else { this.setDark(); } } this.updateMermaid(); } /* flipMode() */ } /* ModeToggle */ let toggle = new ModeToggle(); $(".mode-toggle").click(function() { toggle.flipMode(); }); </script>
1 </span></div></div><div id="topbar-wrapper" class="row justify-content-center topbar-down"><div id="topbar" class="col-11 d-flex h-100 align-items-center justify-content-between"> <span id="breadcrumb"> <span> <a href="/"> Posts </a> </span> <span>캐시 전략</span> </span> <i id="sidebar-trigger" class="fas fa-bars fa-fw"></i><div id="topbar-title"> Post</div><i id="search-trigger" class="fas fa-search fa-fw"></i> <span id="search-wrapper" class="align-items-center"> <i class="fas fa-search fa-fw"></i> <input class="form-control" id="search-input" type="search" aria-label="search" placeholder="Search..."> <i class="fa fa-times-circle fa-fw" id="search-cleaner"></i> </span> <span id="search-cancel" >Cancel</span></div></div><div id="main-wrapper"><div id="main"><div class="row"><div id="post-wrapper" class="col-12 col-lg-11 col-xl-8"><div class="post pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4"><h1 data-toc-skip>캐시 전략</h1><div class="post-meta text-muted d-flex flex-column"><div> <span class="timeago " data-toggle="tooltip" data-placement="bottom" title="Thu, Feb 10, 2022, 12:00 AM +0800" > Feb 10 <i class="unloaded">2022-02-10T00:00:00+08:00</i> </span> by <span class="author"> hayeon_kim </span></div><div> <span> Updated <span class="timeago lastmod" data-toggle="tooltip" data-placement="bottom" title="Thu, Feb 10, 2022, 10:37 PM +0900" > Feb 10 <i class="unloaded">2022-02-10T21:37:04+08:00</i> </span> </span> <span class="readtime" data-toggle="tooltip" data-placement="bottom" title="2385 words">13 min</span></div></div><div class="post-content"><p>최근 다중 서버 환경에서 로컬 캐시를 글로벌 캐시로 변경해야 하는 상황이 있었는데, 작업을 하면서 예전에 시스템 디자인 강의 때 배운 캐싱 개념을 다시 짚어보게 되었다.</p><h1 id="cache">Cache</h1><h2 id="캐시-사용-이유">캐시 사용 이유</h2><ul><li>Repeat: 반복적인 호출이 있는 데이터<li>Immutability: 정적인(static) 데이터 (html, css, images..)<li>Computation heavy: 비용이 많은 작업의 결과물<ul><li>ex) 만약 특정 지점에서 특정 목적지로 가는 최단거리를 구하는 알고리즘의 시간복잡도가 O(2^n)이라고 해보자. 이 결과를 요청할 때마ë
1‹¤ 계산을 한다면 서버의 부담이 커질 것이다. 요청할 때마다 계산을 하는 것이 아니라 한번 계산을 하고 이 결과를 캐시하여 가져올 수 있도록 만들면 server failure가 감소할 것이다.<li>실제 구글맵, 카카오맵에서도 캐시를 통해 목적지 계산 결과를 리턴하고 있다.</ul></ul><h2 id="조회">조회</h2><p>facebook 에서 캐시를 사용해 post 를 조회한다고 해보자.</p><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153418799-dd3bd64d-1407-4165-b3fe-4d8880736140.png" alt="image" /></p><p>캐시에 post 데이터가 없을 때 post 조회 요청을 하면 다음과 같은 프로세스로 진행된다.</p><ol><li>클라이언트가 GET /post 요청을 서버에 한다.<li>서버는 캐시에 해당 post 데이터가 있는 지 확인한다.<li>없다면, DB에 select 요청을 해 post 데이터를 가져온다.<li>post를 캐시에 저장한다.<li>서버는 클라이언트에 post 정보를 응답한다.</ol><p>위와 같이 캐시에 데이터가 없을 때는 지연 시간(latency)가 증가한다.</p><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153418832-e5673492-0aa7-4b99-bf82-8ae1f47c45e1.png" alt="image" /> 캐시 후에는 클라이언트에 빠르게 응답할 수 있다. 캐시에 post 데이터가 없을 때 post 조회 요청을 하면 다음과 같은 프로세스로 진행된다.</p><ol><li>클라이언트가 GET /post 요청을 서버에 한다.<li>서버는 캐시에 해당 post 데이터가 있는 지 확인한다.<li>있다면, post를 가져온다.<li>서버는 클라이언트에 캐시에서 읽어온 post 정보를 응답한다.</ol><p>따라서 첫번째 요청보다는 빠르게 응답할 수 있다.</p><h2 id="수정">수정</h2><p>조회는 위와 같은 프로세스로 일어나는데, 만약 post를 edit한다면 어떻게 될까? 이렇게 데이터 변경 시 캐시 메모리와 디비의 데이터 저장 시점에 관한 정책을 캐시 쓰기 정책(Cache Write Policy)라고 한다. 두 가지 방법을 사용할 수 있는데, <strong>Write Through</strong>와 <strong>Write Back</strong>이 그것이다.</p><h3 id="write-through">Write through</h3><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153418878-7696e007-8865-4581-980f-2a8670b7b40d.png" alt="image" /></p><p>캐시와 DB 둘다에 업데이트를 해버리는 방식이다.</p><ul><li>장점: 캐시와 디비에 업데이트를 함께 해, 데이터의 일관성(consistency)을 유지할 수 있어서 안정적이다.<li>단점: DB에 업데이트를 치는 비용이 있기 때문에 지연 시간(latency)이 증가한다.<li>적절한 상황: 데이터의 일관성이 중요할 때 사용한다.<li>프로세스<ol><li>클라이언트에서 post 수정 요청을 보낸다.<li>서버는 우선 캐시를 업데이트한다.<li>그리고 DB를 업데이트한다.<li>클라이언트에 응답한다.</ol></ul><h3 id="write-back-cache">write back cache</h3><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153418909-939812eb-33f4-4743-ba70-5f2b7bbb7070.png" alt="image" /></p><p>서버는 캐시에 먼저 데이터를 쓰고 클라이언트에 응답하고, 약간의 지연 후에 데이터를 다시 데이터베이스에 쓴다.</p><ul><li>장점: 데이터베이스에 대한 쓰기 비용을 줄일 수 있다. (latency가 감소한다.)<li>단점<ul><li>데이터의 일관성(consistency)이 write through 에 비해 떨어진다.<ul><li>동시에 여러 클라이언트가 요청을 한다면 업데이트 이전 데이터를 가져올 수도 있다. (이는 다중 서버 환경일 시 발생할 수 있는 문제이다.)</ul></ul><li>적절한 상황<ul><li>데이터의 일관성이 중요할 때 사용한다.<li>쓰기가 많을 때 사용한다.</ul><li>프로세스<ol><li>클라이언트에서 post 수정 요청을 보낸다.<li>서버는 우선 캐시를 업데이트한다.<li>캐시 변경을 기록한다.<li>클라이언트에 응답한다.<li>서버는 asynchronous하게 변경 기록을 ë³´ê³  그에 맞ê²
1Œ n초마다 DB를 업데이트한다.</ol></ul><h3 id="적절한-캐시-쓰기-정책">적절한 캐시 쓰기 정책?</h3><p>그렇다면 <strong>write through</strong> 와 <strong>write back</strong> 중 어떤 캐시 쓰기 정책을 사용해야 할까?</p><p>⇒ <strong>서비스의 특성</strong>에 따라 다르다!</p><ul><li>facebook feed: 업데이트가 바로바로 확인이 되어야 한다. ⇒ write through<li>blog: 업데이트가 바로 반영되지 않아도 된다. ⇒ write back</ul><h2 id="캐시-사용처">캐시 사용처</h2><ul><li>프론트: 인증, html, css, javascript, images..<li>백엔드: 그냥 많이 쓰임<li>하드웨어: CPU cache L1/L2/L3</ul><aside> 💡 CPU Cache L1/L2/L3? 캐시 메모리는 CPU가 메모리를 읽는 과정에서 자주 이용하는 정보를 캐시 메모리에 저장함으로써 재사용 시 나타날 정보의 거리를 줄인다. L은 Level의 앞글자를 따온 것으로 L1 캐시는 1ì°¨ 캐시 메모리이다. L1부터 순차적으로 데이터를 찾아 L1에 찾고자 하는 데이터가 없으면 순차적으로 L2, L3로 올라가며 데이터를 찾는다.</aside><h2 id="다중-서버-환경에서의-캐시">다중 서버 환경에서의 캐시</h2><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153418957-75102040-5925-4edf-aec2-3690ae5810a8.png" alt="image" /></p><ul><li>Youtube comment system을 생각해보자. 클라이언트가 “댓글 수정” 요청을 보내고, 이 요청이 서버1에 닿는다면, 서버1은 해당 요청을 수행하여 캐시와 DB를 업데이트할 것이다.<li>그러나 이때 업데이트한 캐시는 서버1이 사용하고 있는 캐시이기 때문에 다른 서버가 사용하는 캐시에는 여전히 변경이 되지 않은 상태이다.<li>따라서 다른 유저가 요청을 했을 때 변경 전의 데이터를 ë³¼ 수 있다는 것이다. 즉, Data Consistency가 떨어진다.<li>또한 최신 정보를 가져오기 위해 캐시가 아니라 DB를 계속 조회해야 한다면 Server Failure가 높아질 것이다.<li>그렇다면 캐시를 사용할 때 어떻게 하면 DB 조회를 최소화하고, 데이터의 일관성을 유지할 수 있을까?</ul><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153419012-d2be3641-d20c-432b-a262-472e95deced0.png" alt="image" /></p><ul><li>위 그림과 같이 여러 대의 서버가 하나의 캐시 저장소를 공유하면 된다. 그럼 다른 서버들도 변경이 반영된 최신의 데이터를 가져올 수 있다.</ul><h2 id="cache-eviction">Cache Eviction</h2><ul><li>캐시 공간이 필요할 때 어떤 데이터를 지우는 것을 Eviction이라고 한다.<li>LRU와 LFU 알고리즘이 있다.</ul><aside> 💡 캐시 서버는 속도를 위해서 대부분 메모리를 사용한다. 메모리를 사용하면 특히 용량이 부족하기 때문에 이 공간을 어떻게 사용해야 하는지에 대해 문제가 발생하게 된다. 데이터 공간을 확보하기 위해서 Eviction은 굉장히 중요하다.</aside><h3 id="lru-least-recently-used">LRU (Least Recently Used)</h3><p>캐시의 크기가 한계에 이르면, 사용된 지 오래된 순으로 제거한다.</p><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="https://user-images.githubusercontent.com/50407047/153419048-b66c5f2b-75d5-4869-8aff-bbe4f9a33e71.png" alt="image" /></p><p>캐시의 크기가 5라 가정할 때, LRU 캐시 만료 정책을 사용하는 캐시는 다음과 같은 순서로 진행된다.</p><ul><li>M1, M2, M3, M4, M5 순으로 데이터를 넣는다.<li>M1이 가장 뒤에 있고, M5가 앞에 있다.<li>새로운 M6 객체를 삽입하려고 시도할 때, 캐시의 크기가 한계에 이르렀기 때문에 Eviction을 수행한다.<li>가장 뒤에 있는(사용된 지 가장 오래된) M1 객체를 제거한다.<li>M6객체는 앞으로 들어온다. 또 다른 M7 객체가 들어온다면 M2를 제거하고 M6 앞으로 들어올 것이다.</ul><h3 id="lfu-least-frequently-used">LFU (Least Frequently Used)</h3><aside> 💡 frequency: key-value 를 얼마나 retrieve 했는지에 대한 카운트</aside><p>캐시의 크기가 한계에 이르면, 사용된 빈도가 낮은 순으로 제거된다.</p><p>LFU는 내부적으로 사용 빈도수를 측정하는 Frequency Table을 갖고 있다.</p><p>다음과 같이 Frequency Table이 있고, 현재 캐시가 꽉 ì°¨ 있다고 가정하자.</p><div class="language-jsx highlighter-rouge"><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
22
33
44
55
66
7</pre><td class="rouge-code"><pre><span class="nx">FrequencyTable</span> <span class="o">=</span> <span class="p">{</span>
8	<span class="mi">1</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M1</span><span class="p">,</span> <span class="nx">M2</span> <span class="p">],</span>
9	<span class="mi">2</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M3</span><span class="p">,</span> <span class="nx">M4</span> <span class="p">],</span>
10	<span class="mi">3</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M5</span> <span class="p">],</span>
11	<span class="mi">4</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M6</span> <span class="p">]</span>
12<span class="p">};</span>
13</pre></table></code></div></div><p>M2를 사용했다면 다음과 같이 FrequencyTable이 변한다.</p><div class="language-jsx highlighter-rouge"><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
142
153
164
175
186
19</pre><td class="rouge-code"><pre><span class="nx">FrequencyTable</span> <span class="o">=</span> <span class="p">{</span>
20	<span class="mi">1</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M1</span> <span class="p">],</span>
21	<span class="mi">2</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M3</span><span class="p">,</span> <span class="nx">M4</span><span class="p">,</span> <span class="nx">M2</span> <span class="p">],</span>
22	<span class="mi">3</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M5</span> <span class="p">],</span>
23	<span class="mi">4</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M6</span> <span class="p">]</span>
24<span class="p">};</span>
25</pre></table></code></div></div><p>새로운 M7 객체가 들어온다고 할 때, 이미 캐시는 꽉 차 있기 때문에 Eviction을 진행해야 한다. 현재 키가 1인 M1이 가장 사용 빈도수가 낮으므로, M1이 제거된다.</p><div class="language-jsx highlighter-rouge"><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
262
273
284
295
30</pre><td class="rouge-code"><pre><span class="nx">FrequencyTable</span> <span class="o">=</span> <span class="p">{</span>
31	<span class="mi">2</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M3</span><span class="p">,</span> <span class="nx">M4</span><span class="p">,</span> <span class="nx">M2</span> <span class="p">],</span>
32	<span class="mi">3</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M5</span> <span class="p">],</span>
33	<span class="mi">4</span><span class="p">:</span> <span class="p">[</span> <span class="nx">M6</span> <span class="p">]</span>
34<span class="p">};</span>
35</pre></table></code></div></div><p>다음에 또 다른 객체가 들어온다면, 현재 가장 낮은 빈도수인 (key가 2인 배열의 요소 중 하나) 객체가 제거될 것이다.</p></div><div class="post-tail-wrapper text-muted"><div class="post-meta mb-3"> <i class="far fa-folder-open fa-fw mr-1"></i> <a href='/categories/system-design/'>system-design</a></div><div class="post-tags"> <i class="fa fa-tags fa-fw mr-1"></i> <a href="/tags/system-design/" class="post-tag no-text-decoration" >system-design</a></div><div class="post-tail-bottom d-flex justify-content-between align-items-center mt-3 pt-5 pb-2"><div class="license-wrapper"> This post is licensed under <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a> by the author.</div><div class="share-wrapper"> <span class="share-label text-muted mr-1">Share</span> <span class="share-icons"> <a href="https://twitter.com/intent/tweet?text=캐시 전략 - 하
35연&url=https://hayeon17kim.github.io//posts/cache/" data-toggle="tooltip" data-placement="top" title="Twitter" target="_blank" rel="noopener" aria-label="Twitter"> <i class="fa-fw fab fa-twitter"></i> </a> <a href="https://www.facebook.com/sharer/sharer.php?title=캐시 전략 - 하연&u=https://hayeon17kim.github.io//posts/cache/" data-toggle="tooltip" data-placement="top" title="Facebook" target="_blank" rel="noopener" aria-label="Facebook"> <i class="fa-fw fab fa-facebook-square"></i> </a> <a href="https://telegram.me/share?text=캐시 전략 - 하연&url=https://hayeon17kim.github.io//posts/cache/" data-toggle="tooltip" data-placement="top" title="Telegram" target="_blank" rel="noopener" aria-label="Telegram"> <i class="fa-fw fab fa-telegram"></i> </a> <i class="fa-fw fas fa-link small" onclick="copyLink()" data-toggle="tooltip" data-placement="top" title="Copy link"></i> </span></div></div></div></div></div><div id="panel-wrapper" class="col-xl-3 pl-2 text-muted topbar-down"><div class="access"><div id="access-lastmod" class="post"> <span>Recent Update</span><ul class="post-content pl-0 pb-1 ml-1 mt-2"><li><a href="/posts/cache/">캐시 전략</a><li><a href="/posts/today-i-learned-2021-04-26/">🐥 주니어 성장일기: 2021.04.26</a><li><a href="/posts/today-i-learned-2021-04-28/">🐥 주니어 성장일기: 2021.04.28</a><li><a href="/posts/today-i-learned-2021-04-25/">🐥 주니어 성장일기: 2021.04.25</a><li><a href="/posts/today-i-learned-2021-04-27/">🐥 주니어 성장일기: 2021.04.27</a></ul></div><div id="access-tags"> <span>Trending Tags</span><div class="d-flex flex-wrap mt-3 mb-1 mr-3"> <a class="post-tag" href="/tags/java/">java</a> <a class="post-tag" href="/tags/javascript/">JavaScript</a> <a class="post-tag" href="/tags/bitcamp/">bitcamp</a> <a class="post-tag" href="/tags/modernjs/">modernJS</a> <a class="post-tag" href="/tags/project/">project</a> <a class="post-tag" href="/tags/algorithm/">algorithm</a> <a class="post-tag" href="/tags/trouble-shooting/">trouble-shooting</a> <a class="post-tag" href="/tags/devil/">devil</a> <a class="post-tag" href="/tags/corejs/">corejs</a> <a class="post-tag" href="/tags/network/">network</a></div></div></div>
35<script src="https://cdn.jsdelivr.net/gh/afeld/[email protected]/dist/bootstrap-toc.min.js"></script>
35<div id="toc-wrapper" class="pl-0 pr-4 mb-5"> <span class="pl-3 pt-2 mb-2">Contents</span><nav id="toc" data-toggle="toc"></nav></div></div></div><div class="row"><div class="col-12 col-lg-11 col-xl-8"><div id="post-extend-wrapper" class="pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4"><div id="related-posts" class="mt-5 mb-2 mb-sm-4"><h3 class="pt-2 mt-1 mb-4 ml-1" data-toc-skip>Further Reading</h3><div class="card-deck mb-4"><div class="card"> <a href="/posts/system-deisign-01/"><div class="card-body"> <span class="timeago small" > Jul 10, 2021 <i class="unloaded">2021-07-10T00:00:00+08:00</i> </span><h3 class="pt-0 mt-1 mb-3" data-toc-skip>시스템 디자인 개념정리: Latency, Throughput, CDN, Availability</h3><div class="text-muted small"><p> Latency &amp; Throughput 시스템 디자인에서 정말 중요한 개념이다. Latency(지연시간) 네트워크에서 하나의 데이터 패킷이 한 지점에서 다른 지점으로 보내지는 데 소요되는 시간을 말한다. 보통 요청이 왔을 때 요청을 처리하고 응답할 때까지의 시간을 말한다. 일반적으로 Latency는 두 네트워크의 거리가 멀 때 ...</p></div></div></a></div><div class="card"> <a href="/posts/docker/"><div class="card-body"> <span class="timeago small" > Jun 23, 2021 <i class="unloaded">2021-06-23T00:00:00+08:00</i> </span><h3 class="pt-0 mt-1 mb-3" data-toc-skip>Docker란 무엇인가</h3><div class="text-muted small"><p> 배경 Environment Disparity 문제를 해결해준다. 다른 머신에서도 같은 환경을 구현할 수 있다. ex) 작업하는 머신은 윈도우고, 서버 머신은 리눅스라고 할 때, 코드를 고치고 배포할 때 environment disparity때문에 동작을 하지 않을 수 있다. 이때 도커를 윈도우와 리눅스에 설치를 하고, Dockerfile을...</p></div></div></a></div><div class="card"> <a href="/posts/today-i-learned-2021-05-06/"><div class="card-body"> <span class="timeago small" > May 6, 2021 <i class="unloaded">2021-05-06T00:00:00+08:00</i> </span><h3 class="pt-0 mt-1 mb-3" data-toc-skip>🐥 주니어 성장일기: 2021.05.01</h3><div class="text-muted small"><p> Vue.js 를 처음 접했다. 원래 담당하고 있는 업무의 다른 파트 진행이 더디게 되어서 다른 간단한 업무를 맡았다. Vue.js를 사용해야 했었는데, 그래서 오늘은 간단한 Vue.js 신텍스를 공부하였다. React에 비해서 확실히 배우기 쉬운 측면이 있는 것 같다. React는 어떤 것을 구현할 때 여러 방법 중 하나의 방법을 택해...</p></div></div></a></div></div></div><div class="post-navigation d-flex justify-content-between"> <a href="/posts/system-deisign-01/" class="btn btn-outline-primary" prompt="Older"><p>시스템 디자인 개념정리: Latency, Throughput, CDN, Availability</p></a> <span class="btn btn-outline-primary disabled" prompt="Newer"><p>-</p></span></div><div id="disqus" class="pt-2 pb-2"><p class="text-center text-muted small pb-5"> Loading comments from <a href="https://disqus.com/">Disqus</a> ...</p></div>
35<script src="/assets/js/lib/jquery.disqusloader.min.js"></script>
35 
35<script> const options = { scriptUrl: '//hayeon17kim-github-io.disqus.com/embed.js', disqusConfig: function() { this.page.title = '캐시 전략'; this.page.url = 'https://hayeon17kim.github.io//posts/cache/'; this.page.identifier = '/posts/cache/'; } }; $.disqusLoader('#disqus', options); </script>
35</div></div></div>
35<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js"></script>
35 
35<script type="text/javascript"> const imgs = document.querySelectorAll('.post-content img'); const observer = lozad(imgs); observer.observe(); </script>
35<footer class="d-flex w-100 justify-content-center"><div class="d-flex justify-content-between align-items-center"><div class="footer-left"><p class="mb-0"> © 2022 <a href="https://twitter.com/MONI56219559">hayeon_kim</a>. <span data-toggle="tooltip" data-placement="top" title="Except where otherwise noted, the blog posts on this site are licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0) License by the author.">Some rights reserved.</span></p></div><div class="footer-right"><p class="mb-0"> Powered by <a href="https://jekyllrb.com" target="_blank" rel="noopener">Jekyll</a> with <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank" rel="noopener">Chirpy</a> theme.</p></div></div></footer></div><div id="search-result-wrapper" class="d-flex justify-content-center unloaded"><div class="col-12 col-sm-11 post-content"><div id="search-hints"><h4 class="text-muted mb-4">Trending Tags</h4><a class="post-tag" href="/tags/java/">java</a> <a class="post-tag" href="/tags/javascript/">JavaScript</a> <a class="post-tag" href="/tags/bitcamp/">bitcamp</a> <a class="post-tag" href="/tags/modernjs/">modernJS</a> <a class="post-tag" href="/tags/project/">project</a> <a class="post-tag" href="/tags/algorithm/">algorithm</a> <a class="post-tag" href="/tags/trouble-shooting/">trouble shooting</a> <a class="post-tag" href="/tags/devil/">devil</a> <a class="post-tag" href="/tags/corejs/">corejs</a> <a class="post-tag" href="/tags/network/">network</a></div><div id="search-results" class="d-flex flex-wrap justify-content-center text-muted mt-3"></div></div></div></div><div id="mask"></div><a id="back-to-top" href="#" aria-label="back-to-top" class="btn btn-lg btn-box-shadow" role="button"> <i class="fas fa-angle-up"></i> </a> 
35<script src="https://cdn.jsdelivr.net/npm/[email protected]/dest/simple-jekyll-search.min.js"></script>
35 
35<script> SimpleJekyllSearch({ searchInput: document.getElementById('search-input'), resultsContainer: document.getElementById('search-results'), json: '/assets/js/data/search.json', searchResultTemplate: '<div class="pl-1 pr-1 pl-sm-2 pr-sm-2 pl-lg-4 pr-lg-4 pl-xl-0 pr-xl-0"> <a href="https://hayeon17kim.github.io/{url}">{title}</a><div class="post-meta d-flex flex-column flex-sm-row text-muted mt-1 mb-1"> {categories} {tags}</div><p>{snippet}</p></div>', noResultsText: '<p class="mt-5">Oops! No result founds.</p>', templateMiddleware: function(prop, value, template) { if (prop === 'categories') { if (value === '') { return `${value}`; } else { return `<div class="mr-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`; } } if (prop === 'tags') { if (value === '') { return `${value}`; } else { return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`; } } } }); </script>
vendor: 1 bytes, line 35
35

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.