0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何說(shuō)服技術(shù)老大用Redis?

OSC開源社區(qū) ? 來(lái)源:OSC開源社區(qū) ? 2023-05-18 10:38 ? 次閱讀
bcf2147a-f4d8-11ed-90ce-dac502259ad0.png這個(gè)問(wèn)題很微妙,可能這位同學(xué)內(nèi)心深處,覺得 Redis 是所有應(yīng)用緩存的標(biāo)配。緩存的世界很廣闊,對(duì)于應(yīng)用系統(tǒng)來(lái)講,我們經(jīng)常將緩存劃分為本地緩存分布式緩存本地緩存:應(yīng)用中的緩存組件,緩存組件和應(yīng)用在同一進(jìn)程中,緩存的讀寫非常快,沒有網(wǎng)絡(luò)開銷。但各應(yīng)用或集群的各節(jié)點(diǎn)都需要維護(hù)自己的單獨(dú)緩存,無(wú)法共享緩存。分布式緩存:和應(yīng)用分離的緩存組件或服務(wù),與本地應(yīng)用隔離,多個(gè)應(yīng)用可直接共享緩存。

1 緩存的本質(zhì)

我們常常會(huì)講:“加了緩存,我們的系統(tǒng)就會(huì)更快” 。所謂的 “更快”,本質(zhì)上做到了如下兩點(diǎn):
  • 減小 CPU 消耗

    將原來(lái)需要實(shí)時(shí)計(jì)算的內(nèi)容提前算好、把一些公用的數(shù)據(jù)進(jìn)行復(fù)用,這可以減少 CPU 消耗,從而提升響應(yīng)性能。

  • 減小 I/O 消耗

    將原來(lái)對(duì)網(wǎng)絡(luò)、磁盤等較慢介質(zhì)的讀寫訪問(wèn)變?yōu)閷?duì)內(nèi)存等較快介質(zhì)的訪問(wèn),從而提升響應(yīng)性能。

假如可以通過(guò)增強(qiáng) CPU、I/O 本身的性能來(lái)滿足需求的話,升級(jí)硬件往往是更好的解決方案,即使需要一些額外的投入成本,也通常要優(yōu)于引入緩存后可能帶來(lái)的風(fēng)險(xiǎn)。從開發(fā)角度來(lái)說(shuō),引入緩存會(huì)提高系統(tǒng)復(fù)雜度,因?yàn)槟阋紤]緩存的失效、更新、一致性等問(wèn)題。從運(yùn)維角度來(lái)說(shuō),緩存會(huì)掩蓋掉一些缺陷,讓問(wèn)題在更久的時(shí)間以后,出現(xiàn)在距離發(fā)生現(xiàn)場(chǎng)更遠(yuǎn)的位置上。從安全角度來(lái)說(shuō),緩存可能泄漏某些保密數(shù)據(jù),也是容易受到攻擊的薄弱點(diǎn)。因此,緩存是把雙刃劍。

2 本地緩存 JDK Map

JDK Map 經(jīng)常用于緩存實(shí)現(xiàn):
  • HashMap

    HashMap 是一種基于哈希表的集合類,它提供了快速的插入、查找和刪除操作??梢詫㈡I值對(duì)作為緩存項(xiàng)的存儲(chǔ)方式,將鍵作為緩存項(xiàng)的唯一標(biāo)識(shí)符,值作為緩存項(xiàng)的內(nèi)容。

  • ConcurrentHashMap

    ConcurrentHashMap 是線程安全的 HashMap,它在多線程環(huán)境下可以保證高效的并發(fā)讀寫操作。

  • LinkedHashMap

    LinkedHashMap 是一種有序的 HashMap ,它保留了元素插入的順序,可以按照插入順序或者訪問(wèn)順序進(jìn)行遍歷。

  • TreeMap

    TreeMap 是一種基于紅黑樹的有序 Map,它可以按照鍵的順序進(jìn)行遍歷。

筆者曾經(jīng)負(fù)責(zé)藝龍紅包系統(tǒng),紅包活動(dòng)就是存儲(chǔ)在ConcurrentHashMap中 ,通過(guò)定時(shí)任務(wù)刷新緩存bd058c3a-f4d8-11ed-90ce-dac502259ad0.png核心流程:1、紅包系統(tǒng)啟動(dòng)后,初始化一個(gè) ConcurrentHashMap 作為紅包活動(dòng)緩存 ;2、數(shù)據(jù)庫(kù)查詢所有的紅包活動(dòng),并將活動(dòng)信息存儲(chǔ)在 Map 中;3、定時(shí)任務(wù)每隔 30 秒 ,執(zhí)行緩存加載方法,刷新緩存。為什么紅包系統(tǒng)會(huì)將紅包活動(dòng)信息存儲(chǔ)在本地內(nèi)存 ConcurrentHashMap 呢 ?
  • 紅包系統(tǒng)是高并發(fā)應(yīng)用,快速將請(qǐng)求結(jié)果響應(yīng)給前端,大大提升用戶體驗(yàn);

  • 紅包活動(dòng)數(shù)量并不多,就算全部放入到 Map 里也不會(huì)產(chǎn)生內(nèi)存溢出的問(wèn)題;

  • 定時(shí)任務(wù)刷新緩存并不會(huì)影響紅包系統(tǒng)的業(yè)務(wù)。

筆者見過(guò)很多單體應(yīng)用都使用這種方案,該方案的特點(diǎn)是簡(jiǎn)潔易用,工程實(shí)現(xiàn)也容易 。

3 本地緩存框架

雖然使用 JDK Map 能快捷構(gòu)建緩存,但緩存的功能還是比較孱弱的。因?yàn)楝F(xiàn)實(shí)場(chǎng)景里,我們可能需要給緩存添加緩存統(tǒng)計(jì)、過(guò)期失效、淘汰策略等功能。于是,本地緩存框架應(yīng)運(yùn)而生。流行的 Java 緩存框架包括:Ehcache , Google Guava , Caffine Cache 。bd1b4a52-f4d8-11ed-90ce-dac502259ad0.png下圖展示了 Caffine 框架的使用示例。bd2b5a8c-f4d8-11ed-90ce-dac502259ad0.png雖然本地緩存框架的功能很強(qiáng)大,但是本地緩存的缺陷依然明顯。1、高并發(fā)的場(chǎng)景,應(yīng)用重啟之后,本地緩存就失效了,系統(tǒng)的負(fù)載就比較大,需要花較長(zhǎng)的時(shí)間才能恢復(fù);2、每個(gè)應(yīng)用節(jié)點(diǎn)都會(huì)維護(hù)自己的單獨(dú)緩存,緩存同步比較頭疼。

4 分布式緩存

分布式緩存是指將緩存數(shù)據(jù)分布在多臺(tái)機(jī)器上,以提高緩存容量和并發(fā)讀寫能力的緩存系統(tǒng)。分布式緩存通常由多臺(tái)機(jī)器組成一個(gè)集群,每臺(tái)機(jī)器上都運(yùn)行著相同的緩存服務(wù)進(jìn)程,緩存數(shù)據(jù)被均勻地分布在集群中的各個(gè)節(jié)點(diǎn)上。Redis 是分布式緩存的首選,甚至我們一提到緩存,很多后端工程師首先想到的就它。下圖是神州專車訂單的 Redis 集群架構(gòu) 。將 Redis 集群拆分成四個(gè)分片,每個(gè)分片包含一主一從,主從可以切換。應(yīng)用 A 根據(jù)不同的緩存 key 訪問(wèn)不同的分片。bd851ffe-f4d8-11ed-90ce-dac502259ad0.png與本地緩存相比,分布式緩存具有以下優(yōu)點(diǎn):1、容量和性能可擴(kuò)展通過(guò)增加集群中的機(jī)器數(shù)量,可以擴(kuò)展緩存的容量和并發(fā)讀寫能力。同時(shí),緩存數(shù)據(jù)對(duì)于應(yīng)用來(lái)講都是共享的。2、高可用性由于數(shù)據(jù)被分布在多臺(tái)機(jī)器上,即使其中一臺(tái)機(jī)器故障,緩存服務(wù)也能繼續(xù)提供服務(wù)。但是分布式緩存的缺點(diǎn)同樣不容忽視。1、網(wǎng)絡(luò)延遲分布式緩存通常需要通過(guò)網(wǎng)絡(luò)通信來(lái)進(jìn)行數(shù)據(jù)讀寫,可能會(huì)出現(xiàn)網(wǎng)絡(luò)延遲等問(wèn)題,相對(duì)于本地緩存而言,響應(yīng)時(shí)間更長(zhǎng)。2、復(fù)雜性分布式緩存需要考慮序列化、數(shù)據(jù)分片、緩存大小等問(wèn)題,相對(duì)于本地緩存而言更加復(fù)雜。筆者曾經(jīng)也認(rèn)為無(wú)腦上緩存 ,系統(tǒng)就一定更快,但直到一次事故,對(duì)于分布式緩存的觀念才徹底改變。2014 年,同事開發(fā)了比分直播的系統(tǒng),所有的請(qǐng)求都是從分布式緩存 Memcached 中獲取后直接響應(yīng)。常規(guī)情況下,從緩存中查詢數(shù)據(jù)非???,但在線用戶稍微多一點(diǎn),整個(gè)系統(tǒng)就會(huì)特別卡。通過(guò) jstat 命令發(fā)現(xiàn) GC 頻率極高,幾次請(qǐng)求就將新生代占滿了,而且 CPU 的消耗都在 GC 線程上。初步判斷是緩存值過(guò)大導(dǎo)致的,果不其然,緩存大小在 300k 到 500k 左右。解決過(guò)程還比較波折,分為兩個(gè)步驟:
  1. 修改新生代大小,從原來(lái)的 2G 修改成 4G,并精簡(jiǎn)緩存數(shù)據(jù)大小 (從平均 300k 左右降為 80k 左右);

  2. 緩存拆成兩個(gè)部分,第一部分是全量數(shù)據(jù),第二部分是增量數(shù)據(jù)(數(shù)據(jù)量很小)。頁(yè)面第一次請(qǐng)求拉取全量數(shù)據(jù),當(dāng)比分有變化的時(shí)候,通過(guò) websocket 推送增量數(shù)據(jù)。

經(jīng)過(guò)這次優(yōu)化,筆者理解到:緩存雖然可以提升整體速度,但是在高并發(fā)場(chǎng)景下,緩存對(duì)象大小依然是需要關(guān)注的點(diǎn),稍不留神就會(huì)產(chǎn)生事故。另外我們也需要合理地控制讀取策略,最大程度減少 GC 的頻率,從而提升整體性能。

5 多級(jí)緩存

開源中國(guó)網(wǎng)站最開始完全是用本地緩存框架 Ehcache 。后來(lái)隨著訪問(wèn)量的激增,出現(xiàn)了一個(gè)可怕的問(wèn)題:“因?yàn)?Java 程序更新很頻繁,每次更新的時(shí)候都要重啟。一旦重啟后,整個(gè) Ehcache 緩存里的數(shù)據(jù)都被清掉。重啟后若大量訪問(wèn)進(jìn)來(lái)的話,開源中國(guó)的數(shù)據(jù)庫(kù)基本上很快就會(huì)崩掉”。于是,開源中國(guó)開發(fā)了多級(jí)緩存框架J2Cache,使用了多級(jí)緩存Ehcache + Redis。多級(jí)緩存有如下優(yōu)勢(shì):
  1. 離用戶越近,速度越快;

  2. 減少分布式緩存查詢頻率,降低序列化和反序列化的 CPU 消耗;

  3. 大幅度減少網(wǎng)絡(luò) IO 以及帶寬消耗。

本地緩存做為一級(jí)緩存,分布式緩存做為二級(jí)緩存,首先從一級(jí)緩存中查詢,若能查詢到數(shù)據(jù)則直接返回,否則從二級(jí)緩存中查詢,若二級(jí)緩存中可以查詢到數(shù)據(jù),則回填到一級(jí)緩存中,并返回?cái)?shù)據(jù)。若二級(jí)緩存也查詢不到,則從數(shù)據(jù)源中查詢,將結(jié)果分別回填到一級(jí)緩存,二級(jí)緩存中。bda46134-f4d8-11ed-90ce-dac502259ad0.png2018 年,筆者服務(wù)的一家電商公司需要進(jìn)行 app 首頁(yè)接口的性能優(yōu)化。筆者花了大概兩天的時(shí)間完成了整個(gè)方案,采取的是兩級(jí)緩存模式,同時(shí)利用了 Guava 的惰性加載機(jī)制,整體架構(gòu)如下圖所示:bdb3db64-f4d8-11ed-90ce-dac502259ad0.png緩存讀取流程如下:1、業(yè)務(wù)網(wǎng)關(guān)剛啟動(dòng)時(shí),本地緩存沒有數(shù)據(jù),讀取 Redis 緩存,如果 Redis 緩存也沒數(shù)據(jù),則通過(guò) RPC 調(diào)用導(dǎo)購(gòu)服務(wù)讀取數(shù)據(jù),然后再將數(shù)據(jù)寫入本地緩存和 Redis 中;若 Redis 緩存不為空,則將緩存數(shù)據(jù)寫入本地緩存中。2、由于步驟 1 已經(jīng)對(duì)本地緩存預(yù)熱,后續(xù)請(qǐng)求直接讀取本地緩存,返回給用戶端。3、Guava 配置了 refresh 機(jī)制,每隔一段時(shí)間會(huì)調(diào)用自定義 LoadingCache 線程池(5 個(gè)最大線程,5 個(gè)核心線程)去導(dǎo)購(gòu)服務(wù)同步數(shù)據(jù)到本地緩存和 Redis 中。優(yōu)化后,性能表現(xiàn)很好,平均耗時(shí)在 5ms 左右。最開始我以為出現(xiàn)問(wèn)題的幾率很小,可是有一天晚上,突然發(fā)現(xiàn) app 端首頁(yè)顯示的數(shù)據(jù)時(shí)而相同,時(shí)而不同。也就是說(shuō):雖然 LoadingCache 線程一直在調(diào)用接口更新緩存信息,但是各個(gè) 服務(wù)器本地緩存中的數(shù)據(jù)并非完成一致。說(shuō)明了兩個(gè)很重要的點(diǎn):1、惰性加載仍然可能造成多臺(tái)機(jī)器的數(shù)據(jù)不一致2、LoadingCache 線程池?cái)?shù)量配置的不太合理,導(dǎo)致了線程堆積最終,我們的解決方案是:1、惰性加載結(jié)合消息機(jī)制來(lái)更新緩存數(shù)據(jù),也就是:當(dāng)導(dǎo)購(gòu)服務(wù)的配置發(fā)生變化時(shí),通知業(yè)務(wù)網(wǎng)關(guān)重新拉取數(shù)據(jù),更新緩存。2、適當(dāng)調(diào)大 LoadigCache 的線程池參數(shù),并在線程池埋點(diǎn),監(jiān)控線程池的使用情況,當(dāng)線程繁忙時(shí)能發(fā)出告警,然后動(dòng)態(tài)修改線程池參數(shù)。

6 沒有銀彈

沒有銀彈是 Fred Brooks 在 1987 年所發(fā)表的一篇關(guān)于軟件工程的經(jīng)典論文。論文強(qiáng)調(diào)真正的銀彈并不存在,而所謂的銀彈則是指沒有任何一項(xiàng)技術(shù)或方法可以能讓軟件工程的生產(chǎn)力在十年內(nèi)提高十倍。通俗來(lái)講:在技術(shù)領(lǐng)域中沒有一種通用的解決方案可以解決所有問(wèn)題。技術(shù)本質(zhì)上是為了解決問(wèn)題而存在的,每個(gè)問(wèn)題都有其獨(dú)特的環(huán)境和限制條件,沒有一種通用的技術(shù)或工具可以完美地解決所有問(wèn)題。雖然技術(shù)不斷發(fā)展和進(jìn)步,但是對(duì)于復(fù)雜的問(wèn)題,仍需要結(jié)合多種技術(shù)和方法,進(jìn)行系統(tǒng)性的思考和綜合性的解決方案設(shè)計(jì),才能得到最優(yōu)解決方案。回到文章開頭的問(wèn)題 ,如何說(shuō)服技術(shù)老大用 Redis ?假如應(yīng)用就是一個(gè)單體應(yīng)用,緩存可以不共享,通過(guò)定時(shí)任務(wù)刷新緩存對(duì)業(yè)務(wù)沒有影響,而且本地內(nèi)存可以 Hold 住緩存的對(duì)象大小,那么你的技術(shù)老大的方案沒有問(wèn)題。假如應(yīng)用業(yè)務(wù)比較復(fù)雜,需要使用緩存提升系統(tǒng)的性能,同時(shí)分布式緩存共享的特性對(duì)于研發(fā)來(lái)講開發(fā)更加快捷,Redis 確實(shí)是個(gè)不錯(cuò)的選擇,可以從研發(fā)成本、代碼維護(hù)、人力模型等多個(gè)角度和技術(shù)老大提出自己的觀點(diǎn)。總而言之,在技術(shù)領(lǐng)域中,沒有銀彈。我們需要不斷探索和研究新的技術(shù),但同時(shí)也需要認(rèn)識(shí)到技術(shù)的局限性,不盲目追求所謂的 “銀彈”,而是結(jié)合具體問(wèn)題和需求,選擇最適合的解決方案。
審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 框架
    +關(guān)注

    關(guān)注

    0

    文章

    403

    瀏覽量

    17509
  • 磁盤
    +關(guān)注

    關(guān)注

    1

    文章

    379

    瀏覽量

    25221
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    376

    瀏覽量

    10887
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何使用Rust連接Redis

    Redis是一款快速、開源、鍵值存儲(chǔ)數(shù)據(jù)庫(kù),被廣泛應(yīng)用于緩存、發(fā)布/訂閱系統(tǒng)、定時(shí)任務(wù)等場(chǎng)景中。Rust提供了很多Redis的客戶端庫(kù),本教程將會(huì)介紹如何使用Rust連接Redis,以及如何通過(guò)
    的頭像 發(fā)表于 09-19 16:22 ?2401次閱讀

    Redis Stream應(yīng)用案例

    讀取結(jié)果,在APP或者web頁(yè)面上進(jìn)行展示,從而整個(gè)系統(tǒng)形成一個(gè)閉環(huán)。作者簡(jiǎn)介夏德軍,花名夏周,阿里云Redis技術(shù)專家,負(fù)責(zé)阿里云Redis內(nèi)核開發(fā)和維護(hù)?;钴S于開源社區(qū),Redis
    發(fā)表于 06-26 17:15

    redis概述

    REmote DIctionary Server(Redis)是一個(gè)基于key-value鍵值對(duì)的持久化數(shù)據(jù)庫(kù)存儲(chǔ)系統(tǒng)。redis和大名鼎鼎的Memcached緩存服務(wù)軟件很像,但是redis支持
    發(fā)表于 07-17 07:38

    Redis使用總結(jié)

    Spring+SpringMVC+MyBatis+easyUI整合進(jìn)階篇(十四)Redis緩存正確的使用姿勢(shì)
    發(fā)表于 09-05 08:31

    laravel使用redis

    laravel操作redis筆記!
    發(fā)表于 09-24 09:40

    啟動(dòng)Redis的三種方法

    Redis筆記(1)——安裝、卸載、三種方法啟動(dòng)Redis,Redis命令使用(干貨十足),Redis兩種方法設(shè)置密碼,時(shí)間復(fù)雜度(更完善哦~)
    發(fā)表于 06-08 16:09

    如何使得redis中的數(shù)據(jù)不再有

    嵌入式Linux系統(tǒng)重啟后如何使得redis中的數(shù)據(jù)不再有今天在工作中遇到一個(gè)問(wèn)題:網(wǎng)頁(yè)展示redis中的數(shù)據(jù),然而再Linux系統(tǒng)重啟后網(wǎng)頁(yè)還能展示redis中的數(shù)據(jù),感覺很奇怪,到網(wǎng)上搜了下
    發(fā)表于 11-05 08:50

    labview讀寫操作REDIS

    本帖最后由 SevenLi8408 于 2022-9-15 08:07 編輯 分享一個(gè)好用的非關(guān)系型緩存數(shù)據(jù)庫(kù)的使用方法。REDIS桌面管理軟件https://github.com
    發(fā)表于 08-15 10:32

    什么是 Redis

    ? — ? 1 ?— 什么是 Redis? Redis(REmote DIctionary Service)是一個(gè)開源的鍵值對(duì)數(shù)據(jù)庫(kù)服務(wù)器。 Redis 更準(zhǔn)確的描述是一個(gè)數(shù)據(jù)結(jié)構(gòu)服務(wù)器。Re
    的頭像 發(fā)表于 05-22 15:32 ?1123次閱讀
    什么是 <b class='flag-5'>Redis</b>

    Redis的主從、哨兵、Redis Cluster集群

    ? 前言 今天跟小伙伴們一起學(xué)習(xí)Redis的主從、哨兵、Redis Cluster集群。 Redis主從 Redis哨兵 Redis Clu
    的頭像 發(fā)表于 06-12 14:58 ?850次閱讀
    <b class='flag-5'>Redis</b>的主從、哨兵、<b class='flag-5'>Redis</b> Cluster集群

    Redis 的數(shù)據(jù)清理策略

    本文整理 Redis 的數(shù)據(jù)清理策略所有代碼來(lái)自 Redis version :5.0, 不同版本的 Redis 策略可能有調(diào)整
    發(fā)表于 09-19 14:24 ?391次閱讀
    <b class='flag-5'>Redis</b> 的數(shù)據(jù)清理策略

    如何用Springboot整合Redis

    本篇文件我們來(lái)介紹如何用Springboot整合Redis。 1、Docker 安裝 Redis 1.1 下載鏡像 docker pull redis: 6 . 2 . 6 1.2 創(chuàng)建配置文件
    的頭像 發(fā)表于 10-08 14:56 ?592次閱讀
    如何用Springboot整合<b class='flag-5'>Redis</b>

    Java redis鎖怎么實(shí)現(xiàn)

    在Java中實(shí)現(xiàn)Redis鎖涉及到以下幾個(gè)方面:Redis的安裝配置、Redis連接池的使用、Redis數(shù)據(jù)結(jié)構(gòu)的選擇、實(shí)現(xiàn)分布式鎖的幾種方式等。 一、
    的頭像 發(fā)表于 12-04 10:47 ?1175次閱讀

    redis容器內(nèi)怎么查看redis日志

    redis是一款流行的開源內(nèi)存數(shù)據(jù)庫(kù),常用于緩存、消息隊(duì)列、任務(wù)管理等場(chǎng)景。在使用redis時(shí),了解如何查看redis日志對(duì)于排查問(wèn)題、監(jiān)控性能和分析應(yīng)用程序行為非常重要。在本文中,我們將介紹在
    的頭像 發(fā)表于 12-05 10:10 ?3701次閱讀

    Redis開源版與Redis企業(yè)版,怎么選用?

    點(diǎn)擊“藍(lán)字”關(guān)注我們數(shù)以千計(jì)的企業(yè)和數(shù)以百萬(wàn)計(jì)的開發(fā)人員Redis開源版來(lái)構(gòu)建應(yīng)用程序。但隨著用戶數(shù)量、數(shù)據(jù)量和地區(qū)性的增加,成本、可擴(kuò)展性、運(yùn)營(yíng)和可用性等問(wèn)題也隨之而來(lái)。Redis企業(yè)版
    的頭像 發(fā)表于 04-04 08:04 ?1101次閱讀
    <b class='flag-5'>Redis</b>開源版與<b class='flag-5'>Redis</b>企業(yè)版,怎么選用?