<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          小白都能看懂的緩存入門

          共 3797字,需瀏覽 8分鐘

           ·

          2021-04-22 13:53

          緩存是程序員必須了解的技術(shù),無(wú)論是前端、后端還是客戶端,大到復(fù)雜的系統(tǒng)架構(gòu),小到 CPU 或是芯片,都少不了緩存的影子。

          下面只需 5 分鐘,帶你入門緩存技術(shù)。


          什么是緩存?

          緩存(Cache)本意是指可以進(jìn)行高速數(shù)據(jù)交換的存儲(chǔ)器,通俗點(diǎn)來(lái)說(shuō),就是通過(guò)將數(shù)據(jù)提前存放到內(nèi)存,以提高訪問(wèn)速度。

          在我們?cè)O(shè)計(jì)程序或算法時(shí),有兩個(gè)基本指標(biāo),即時(shí)間復(fù)雜度空間復(fù)雜度。有時(shí),我們的程序無(wú)法同時(shí)滿足二者,就只能以時(shí)間換空間,或者以空間換時(shí)間。緩存是以空間換時(shí)間思想的典型應(yīng)用,犧牲一部分內(nèi)存空間,能夠得到數(shù)百倍的讀取性能提升。

          緩存最常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)是鍵值對(duì)(Key/Value),類似字典,即一個(gè) Key 對(duì)應(yīng)一個(gè) Value,訪問(wèn)緩存時(shí)通過(guò) Key 獲取 Value。


          為什么需要緩存?

          最簡(jiǎn)單的后端系統(tǒng)只需要一個(gè)應(yīng)用服務(wù)(比如 Tomcat)和持久化存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)庫(kù)(如 MySQL),對(duì)于一個(gè)訪問(wèn)量很小的系統(tǒng)來(lái)說(shuō),這樣的架構(gòu)就足夠了。



          但隨著系統(tǒng)的用戶數(shù)和訪問(wèn)量的提升,數(shù)據(jù)庫(kù)會(huì)收到越來(lái)越多的并發(fā)請(qǐng)求。由于數(shù)據(jù)庫(kù)是從磁盤中讀取數(shù)據(jù),性能較低,隨著請(qǐng)求數(shù)的增多,數(shù)據(jù)庫(kù)受到的壓力會(huì)越來(lái)越大。由于應(yīng)用訪問(wèn)數(shù)據(jù)庫(kù)的連接數(shù)有限,當(dāng)數(shù)據(jù)庫(kù)的處理能力跟不上請(qǐng)求數(shù)時(shí),新的請(qǐng)求將排隊(duì)等待,從而導(dǎo)致我們的后臺(tái)程序也會(huì)阻塞。當(dāng)并發(fā)請(qǐng)求數(shù)持續(xù)增大時(shí),數(shù)據(jù)庫(kù)甚至?xí)斓?/strong>!歐豁,完蛋。
          因此,我們需要性能更高的讀取數(shù)據(jù)方式,能夠幫助數(shù)據(jù)庫(kù)分擔(dān)壓力,緩存登場(chǎng)了。
          可以在數(shù)據(jù)庫(kù)之上增加一層緩存,當(dāng)后臺(tái)程序首次讀取數(shù)據(jù)時(shí),將得到的數(shù)據(jù)存入緩存中,那么后續(xù)的請(qǐng)求要讀取相同數(shù)據(jù)時(shí),只需從緩存中讀取即可。由于緩存是將數(shù)據(jù)存入內(nèi)存中,讀取速度非??欤诔晒μ嵘阅艿耐瑫r(shí),替數(shù)據(jù)庫(kù)分擔(dān)了大量的壓力。


          因此想要提升系統(tǒng)的整體性能,緩存是不可或缺的。

          緩存類別

          可以將緩存分為兩種類型。

          本地緩存

          直接運(yùn)行在應(yīng)用程序本地的緩存組件。
          比如 JVM 中的 Map 數(shù)據(jù)結(jié)構(gòu),可以作為最簡(jiǎn)單的數(shù)據(jù)緩存:
          class LocalCache {
          private static Map<String, Object> cache = new HashMap<>();
          private LocalCache() {}
          public static void put(String key, Object value) { cache.put(key, value); }
          public static Object get(String key) { return cache.get(key); }
          }

          如果你的應(yīng)用程序只需要運(yùn)行在一臺(tái)服務(wù)器上,并且多個(gè)應(yīng)用程序之間不需要共享緩存的數(shù)據(jù)(比如用戶 token),可以直接采用本地緩存,訪問(wèn)緩存時(shí)不需要通過(guò)網(wǎng)絡(luò)傳輸,非常地方便迅速。
          但是本地緩存會(huì)和你的應(yīng)用程序強(qiáng)耦合,應(yīng)用程序停止,本地緩存也就停止了。而且如果是在分布式場(chǎng)景下,多個(gè)機(jī)器都要使用緩存,此時(shí)如果在每個(gè)服務(wù)器上單獨(dú)維護(hù)一份本地緩存,不僅無(wú)法共享數(shù)據(jù),而且非常浪費(fèi)內(nèi)存(因?yàn)槊颗_(tái)機(jī)器可能緩存了相同的數(shù)據(jù))。
          為解決緩存數(shù)據(jù)共享的問(wèn)題,可以使用分布式緩存。

          分布式緩存

          分布式緩存是指獨(dú)立的緩存服務(wù),不和任何一個(gè)具體的應(yīng)用耦合,可以獨(dú)立運(yùn)行并搭建緩存集群。類似數(shù)據(jù)庫(kù),所有的應(yīng)用程序都可以連接同一個(gè)緩存服務(wù)以獲取相同的緩存數(shù)據(jù)。
          除了數(shù)據(jù)共享外,分布式緩存的優(yōu)點(diǎn)還有很多。比如不需要每臺(tái)機(jī)器單獨(dú)維護(hù)緩存、可以集中管理緩存和整體管控分析、便于擴(kuò)展和容錯(cuò)等。但是應(yīng)用必須要通過(guò)網(wǎng)絡(luò)訪問(wèn)分布式緩存服務(wù),會(huì)產(chǎn)生額外的網(wǎng)絡(luò)開(kāi)銷成本;并且每臺(tái)機(jī)器都有可能會(huì)對(duì)整個(gè)分布式緩存服務(wù)產(chǎn)生影響,而一旦分布式緩存掛了,所有的應(yīng)用都可能出現(xiàn)癱瘓(緩存雪崩)。

          多級(jí)緩存

          上述兩種緩存沒(méi)有絕對(duì)的優(yōu)劣,要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景進(jìn)行選型。其實(shí)還可以將本地緩存與分布式緩存相結(jié)合,形成多級(jí)緩存服務(wù),架構(gòu)如下:
          當(dāng)首次查詢時(shí)(不存在緩存),會(huì)同時(shí)將數(shù)據(jù)寫入本地緩存和分布式緩存。之后的查詢優(yōu)先查詢分布式緩存,而如果分布式緩存宕機(jī),則從本地緩存獲取數(shù)據(jù)。通過(guò)多級(jí)緩存機(jī)制,能夠起到兜底的作用,即使緩存掛掉,也能支撐應(yīng)用運(yùn)行一段時(shí)間。

          緩存淘汰策略

          下面我們思考一個(gè)問(wèn)題,如何去實(shí)現(xiàn)一個(gè)本地緩存呢?
          剛剛提到的 Map 數(shù)據(jù)結(jié)構(gòu)是一個(gè)思路,但是和我們自己的電腦存儲(chǔ)文件、或者是和 JVM 存儲(chǔ)對(duì)象一樣,內(nèi)存當(dāng)然不是無(wú)限的。因此在實(shí)現(xiàn)緩存時(shí),必須要設(shè)計(jì)一套緩存淘汰策略,按照某種機(jī)制回收緩存占用的內(nèi)存,保證緩存數(shù)據(jù)不會(huì)無(wú)限地增長(zhǎng)直到撐爆內(nèi)存。
          下面介紹幾種常見(jiàn)的緩存淘汰策略,關(guān)鍵問(wèn)題就是當(dāng)緩存空間已滿時(shí),應(yīng)該選擇哪些緩存進(jìn)行刪除。

          LRU 最近最少使用

          LRU(Least Recently Used)是最經(jīng)典的內(nèi)存淘汰策略,其設(shè)計(jì)原則是 “如果一個(gè)數(shù)據(jù)在最近一段時(shí)間沒(méi)有被訪問(wèn)到,那么在將來(lái)它被訪問(wèn)的可能性也很小”。即根據(jù)數(shù)據(jù)的最近訪問(wèn)時(shí)間來(lái)進(jìn)行淘汰。缺點(diǎn)是可能會(huì)由于一次冷數(shù)據(jù)的批量查詢而誤刪除大量的熱點(diǎn)數(shù)據(jù)。

          近似 LRU 算法

          類似 LRU 算法,只是每次隨機(jī)選擇一批數(shù)據(jù)進(jìn)行 LRU 淘汰,而不是全量 LRU 運(yùn)算,犧牲部分準(zhǔn)確度,以提升算法執(zhí)行效率。
          Redis 3.0 之后對(duì)其進(jìn)行了優(yōu)化,維護(hù)了一個(gè)侯選池,將隨機(jī)選擇的數(shù)據(jù)放入侯選池中進(jìn)行 LRU 運(yùn)算。當(dāng)侯選池放滿后,新隨機(jī)的數(shù)據(jù)會(huì)替換掉池中最近被訪問(wèn)的數(shù)據(jù)。

          TTL 超時(shí)時(shí)間

          TTL(Time To Live)是指用戶為緩存設(shè)置的過(guò)期時(shí)間,當(dāng)前時(shí)間到達(dá)過(guò)期時(shí)間時(shí),將刪除緩存;如果緩存空間已滿,則優(yōu)先淘汰最接近過(guò)期時(shí)間的數(shù)據(jù)。

          LFU 最少使用

          LFU(Least Frequently Used)策略會(huì)記錄每個(gè)緩存數(shù)據(jù)的最近訪問(wèn)次數(shù)(頻率),并優(yōu)先清除使用次數(shù)較少的數(shù)據(jù)。這種算法存在的顯著缺點(diǎn)是,最新寫入的數(shù)據(jù)由于訪問(wèn)次數(shù)少,常常剛被緩存就刪除了。

          FIFO 先進(jìn)先出

          FIFO(First In First Out)先進(jìn)先出策略會(huì)將數(shù)據(jù)按照寫入緩存的順序進(jìn)行排隊(duì),當(dāng)緩存空間不足時(shí),最先進(jìn)入緩存的數(shù)據(jù)會(huì)被優(yōu)先刪除。是一種比較死板的策略,不考慮數(shù)據(jù)熱度,可能會(huì)淘汰大量的熱點(diǎn)數(shù)據(jù),但是實(shí)現(xiàn)起來(lái)相對(duì)容易。

          Second-Chance

          對(duì) FIFO 算法的改進(jìn),給每個(gè)緩存數(shù)據(jù)添加一個(gè)引用標(biāo)志,當(dāng)數(shù)據(jù)被訪問(wèn)時(shí),設(shè)置其標(biāo)志位為 1。
          當(dāng)緩存空間不足時(shí),會(huì)檢測(cè)隊(duì)列首部(最先寫入緩存)數(shù)據(jù)的引用標(biāo)志位,如果為 1,表示被訪問(wèn)過(guò),則重置標(biāo)志位為 0 并且重新加入隊(duì)列尾部;如果為 0,表示未被訪問(wèn)過(guò),直接淘汰。
          相對(duì) FIFO 算法來(lái)說(shuō),更加靈活,降低了淘汰熱點(diǎn)數(shù)據(jù)的風(fēng)險(xiǎn)。

          Random 隨機(jī)

          隨機(jī)淘汰策略,沒(méi)啥好說(shuō)的,一般不建議使用。

          還有一些其他的策略,比如優(yōu)先淘汰占用空間最大的數(shù)據(jù)等,不再贅述。

          緩存實(shí)現(xiàn)

          要想自己設(shè)計(jì)一個(gè)優(yōu)秀的緩存,還是有難度的,不止是用一個(gè) Java 的數(shù)組或者集合類那么簡(jiǎn)單。幸運(yùn)的是,有很多現(xiàn)成的類庫(kù)、框架和中間件可供我們直接使用。下面介紹一些優(yōu)秀的緩存實(shí)現(xiàn)。

          Caffeine

          Caffeine 是基于 Java8 的近似最佳的高性能本地緩存庫(kù)。提供了靈活的構(gòu)造器 API,能夠輕松地創(chuàng)建緩存對(duì)象,并且組合各種特性??芍^是進(jìn)程緩存之王。
          地址:https://github.com/ben-manes/caffeine/

          Guava Cache

          Guava 是 Google 開(kāi)源的 Java 工具包,而 Guava Cache 是其中的本地緩存實(shí)現(xiàn),提供了 time-based、size-based 等多種緩存淘汰策略,使用起來(lái)非常方便。
          地址:https://github.com/google/guava/

          Ehcache

          Ehcache 是 Java 實(shí)現(xiàn)的開(kāi)源緩存框架,特性齊全,成熟輕量,并且支持通過(guò) RMI 等方式實(shí)現(xiàn)可擴(kuò)展的分布式緩存。
          地址:http://www.ehcache.org/

          Memcached

          開(kāi)源的高性能分布式內(nèi)存 K/V 緩存系統(tǒng),支持主流語(yǔ)言的 API,適合存儲(chǔ)小塊的數(shù)據(jù),可以利用多核 CPU。缺點(diǎn)是不支持持久化。
          地址:http://memcached.org/

          Redis

          知名的 K/V 存儲(chǔ)系統(tǒng),適用于分布式緩存,支持多種數(shù)據(jù)結(jié)構(gòu)、讀寫性能極高,并且可以搭建高可用集群、易擴(kuò)展、支持持久化。還能通過(guò) Lua 腳本實(shí)現(xiàn)原子性。

          Spring Cache

          Spring Cache 提供了抽象的緩存接口,幫助我們更好地使用緩存,底層可以選擇不同的緩存實(shí)現(xiàn),比如 Caffine 或者 Guava Cache。

          沒(méi)有銀彈

          緩存雖然具有很多優(yōu)點(diǎn),但也不是萬(wàn)能的。引入緩存后,還要考慮到緩存刪除、緩存更新、緩存穿透、緩存擊穿、緩存雪崩等問(wèn)題。引入的緩存級(jí)數(shù)越多,數(shù)據(jù)不一致的風(fēng)險(xiǎn)就越大。因此,在架構(gòu)設(shè)計(jì)時(shí),要根據(jù)實(shí)際的業(yè)務(wù)需求來(lái)是否使用緩存、選用何種緩存。
          畢竟,沒(méi)有銀彈。
          -- End --
          雷小帥建立了一個(gè)技術(shù)交流群,需要交流的可以加微信,還有空缺的好友位喲,記得備注一下『進(jìn)群學(xué)習(xí)』。


          瀏覽 70
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美高清 日韩 中文字幕 | 天天添夜夜干 | h网站免费观看 | 中文字幕+乱码+中文乱码视频在线观看 | 天天干天天日天天操 |