摘要:緩存是一個(gè)常談常新的話題,作為一名服務(wù)端的技術(shù),如果你入行一年都還沒(méi)用過(guò)類產(chǎn)品,那只能說(shuō)你的公司實(shí)在太小了,或者你干的活實(shí)在太邊緣了。這是緩存最原始的意義,同時(shí)也引申出了緩存最普遍的用法。但是現(xiàn)實(shí)中還有一種緩存,是主動(dòng)更新的。
緩存是一個(gè)常談常新的話題,作為一名服務(wù)端的技術(shù),如果你入行一年都還沒(méi)用過(guò)memcached類產(chǎn)品,那只能說(shuō)你的公司實(shí)在太小了,或者你干的活實(shí)在太邊緣了。
說(shuō)起緩存,可能大家最直接想到的就是:“在數(shù)據(jù)庫(kù)前面擋一層”。這是緩存最原始的意義,同時(shí)也引申出了緩存最普遍的用法。
原始模式 代碼示例1(原始模式)://從緩存中獲取數(shù)據(jù)[較快的方式] data = getfromcache(id) if data == null then //從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)[較慢的方式] data = getfromdb(id) //緩存1天 setintocache(id, data, 86400) return data end return data緩存加鎖
上面這種情況下,當(dāng)同時(shí)有N個(gè)請(qǐng)求到達(dá),都同時(shí)執(zhí)行g(shù)etfromcache,那么都會(huì)發(fā)現(xiàn)data在緩存中不存在,然后都會(huì)去調(diào)用getfromdb,以及setintocache。這是不必要的,那么我們有沒(méi)有辦法減少這些并發(fā)呢。
最直接的想法是加鎖,當(dāng)進(jìn)入if條件中時(shí),加一把鎖,讓其他進(jìn)程不再執(zhí)行下面的邏輯,而是等第一個(gè)進(jìn)程的setintocache執(zhí)行完成后,再重新執(zhí)行一次getfromcache。
那這個(gè)鎖如何加呢?這里推薦一種省時(shí)省力的方法。通過(guò)直接在緩存value中設(shè)置過(guò)期時(shí)間來(lái)實(shí)現(xiàn)。
比如緩存的value值為data,那我們修改一下,把它放到一個(gè)json中,改成
{data:data,atime:1429618765}
我們?cè)黾恿艘粋€(gè)atime來(lái)記錄緩存生成的時(shí)間。而邏輯就變成下面這樣。
代碼示例2(緩存加鎖)://從緩存中獲取數(shù)據(jù)[較快的方式] data = getfromcache(id) data = json.decode(data) //如果通過(guò)檢查緩存生成時(shí)間,發(fā)現(xiàn)緩存已經(jīng)過(guò)于陳舊,那么就將緩存過(guò)期時(shí)間設(shè)置為現(xiàn)在開(kāi)始的5分鐘以后(這樣其他并發(fā)進(jìn)程就會(huì)以為此緩存還未過(guò)期,還會(huì)繼續(xù)使用5分鐘,只讓當(dāng)前這一個(gè)請(qǐng)求去重建緩存) if data != null && data.atime+86400 < now then data.atime = now+300-86400 data = json.encode(data) //對(duì)真正的cache來(lái)說(shuō),緩存10天或者更長(zhǎng)時(shí)間 setintocache(id, data, 864000) //這里把data設(shè)置成null是為了走到下面的if中去重建緩存 data = null end if data == null then //從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)[較慢的方式] data = getfromdb(id) data = {data:data, atime:now} data = json.encode(data) //對(duì)真正的cache來(lái)說(shuō),緩存10天或者更長(zhǎng)時(shí)間 setintocache(id, data, 864000) return data end return data
你可以會(huì)發(fā)現(xiàn),這里也會(huì)存在并發(fā)啊,和上面例1一樣,第一個(gè)getfromcache到setintocache之間,如果同時(shí)有N個(gè)請(qǐng)求到來(lái),不還是都會(huì)執(zhí)行這段操作,都會(huì)去查庫(kù)嗎。
沒(méi)錯(cuò),是這樣的。但是我們仔細(xì)看一下,例1中,從getfromcache到setintocache之間,經(jīng)歷了一次漫長(zhǎng)的getfromdb操作,這個(gè)時(shí)間耗費(fèi)可能是上百毫秒的。而我們例2中,并沒(méi)有進(jìn)行什么操作,這個(gè)時(shí)間耗費(fèi)只在毫秒甚至微秒級(jí)的。
所以例1中g(shù)etfromcache到setintocache之間的并發(fā)是遠(yuǎn)大于例2中的。例2中通過(guò)減小時(shí)間窗口,有效的模擬了鎖機(jī)制。同時(shí)還沒(méi)有增強(qiáng)額外的存儲(chǔ)復(fù)雜度。所以是推薦的一種方式。
可以說(shuō),我們所有的緩存都應(yīng)該是例2的方式,他在各方面都優(yōu)于例1(多保存的一個(gè)atime字段耗費(fèi)的內(nèi)存基本可以忽略不計(jì)。且atime很多時(shí)候?qū)τ谡{(diào)試程序還很有用)。
主動(dòng)更新緩存那這樣就夠了嗎?對(duì)于被動(dòng)過(guò)期型的緩存,這樣基本就可以了。但是現(xiàn)實(shí)中還有一種緩存,是主動(dòng)更新的。試想有一種緩存,我們要求必須和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致,不能出現(xiàn)陳舊數(shù)據(jù)。那么上面的緩存方式就不合適了。
我們必然會(huì)添加一個(gè)流程:即當(dāng)數(shù)據(jù)庫(kù)有更新時(shí),同時(shí)更新緩存,因?yàn)榫彺鏁?huì)自己重建,也可以修改為當(dāng)數(shù)據(jù)庫(kù)有更新時(shí),同時(shí)刪除緩存。
這里提到刪除或者更新緩存,就有點(diǎn)意思了。我們上面講到的都是非常簡(jiǎn)單的緩存,即一個(gè)id對(duì)應(yīng)一個(gè)key。那么試想,如果我們有一個(gè)分頁(yè)緩存,緩存了某一個(gè)文章最新的前10頁(yè)數(shù)據(jù)。分別的key是page_1,page_2...page10。
那么當(dāng)我們有一條新數(shù)據(jù)產(chǎn)生,這10頁(yè)就都失效了,需要更新或者刪除10次。這顯然是不太科學(xué)的做法。
那么我們應(yīng)該怎么做呢。我們可以借用上面例2中的方法,例2中,我們?cè)诰彺嬷性黾恿艘粋€(gè)atime字段,標(biāo)識(shí)為緩存的生成時(shí)間。我們既然知道緩存什么時(shí)候生成的,那問(wèn)題就好解決了。我們?cè)诿看斡行聰?shù)據(jù)產(chǎn)生時(shí),都去更新一個(gè)updatetime字段。然后獲取分頁(yè)緩存的時(shí)候,看一下這個(gè)updatetime字段是不是在atime之后,如果是,那么說(shuō)明這份緩存太舊了,需要走更新流程。
代碼示例3(避免批量更新)://從緩存中獲取數(shù)據(jù)[較快的方式][這里的兩次get普通的緩存系統(tǒng)都支持一個(gè)請(qǐng)求完成] data = getfromcache(id) updatetime = getupdatetime(id) data = json.decode(data) //如果通過(guò)檢查緩存生成時(shí)間,發(fā)現(xiàn)緩存已經(jīng)過(guò)于陳舊,那么就將緩存過(guò)期時(shí)間設(shè)置為現(xiàn)在開(kāi)始的5分鐘以后(這樣其他并發(fā)進(jìn)程就會(huì)以為此緩存還未過(guò)期,還會(huì)繼續(xù)使用5分鐘,只讓當(dāng)前這一個(gè)請(qǐng)求去重建緩存) if data != null && (data.atime+86400 < now || date.atime < updatetime) then data.atime = now+300-86400 data = json.encode(data) //對(duì)真正的cache來(lái)說(shuō),緩存10天或者更長(zhǎng)時(shí)間 setintocache(id, data, 864000) //這里把data設(shè)置成null是為了走到下面的if中去重建緩存 data = null end if data == null then //從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)[較慢的方式] data = getfromdb(id) data = {data:data, atime:now} data = json.encode(data) //對(duì)真正的cache來(lái)說(shuō),緩存10天或者更長(zhǎng)時(shí)間 setintocache(id, data, 864000) return data end return data
這僅僅是在代碼示例2的基礎(chǔ)上增加了下面這一個(gè)條件判斷而已
date.atime < updatetime
這樣,無(wú)論是緩存保存時(shí)間過(guò)期了,還是緩存本身有更新,都會(huì)觸發(fā)帶鎖機(jī)制的緩存更新。
好了,先說(shuō)到這里,回頭有想起來(lái)的再做更新。原文地址
順便插播一則招聘廣告。(碼字不易,求別刪招聘廣告,謝!)
易手機(jī)坐標(biāo)深圳,做一款易用安全的老年智能手機(jī),做老年手機(jī)第一品牌?,F(xiàn)在灰常需要服務(wù)端同學(xué)入伙。有興趣的同學(xué)請(qǐng)私信或簡(jiǎn)歷發(fā)郵箱:ligang#pingyijinren.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/61769.html
摘要:緩存是一個(gè)常談常新的話題,作為一名服務(wù)端的技術(shù),如果你入行一年都還沒(méi)用過(guò)類產(chǎn)品,那只能說(shuō)你的公司實(shí)在太小了,或者你干的活實(shí)在太邊緣了。這是緩存最原始的意義,同時(shí)也引申出了緩存最普遍的用法。但是現(xiàn)實(shí)中還有一種緩存,是主動(dòng)更新的。 緩存是一個(gè)常談常新的話題,作為一名服務(wù)端的技術(shù),如果你入行一年都還沒(méi)用過(guò)memcached類產(chǎn)品,那只能說(shuō)你的公司實(shí)在太小了,或者你干的活實(shí)在太邊緣了。 說(shuō)起...
摘要:以下為大家整理了阿里巴巴史上最全的面試題,涉及大量面試知識(shí)點(diǎn)和相關(guān)試題。的內(nèi)存結(jié)構(gòu),和比例。多線程多線程的幾種實(shí)現(xiàn)方式,什么是線程安全。點(diǎn)擊這里有一套答案版的多線程試題。線上系統(tǒng)突然變得異常緩慢,你如何查找問(wèn)題。 以下為大家整理了阿里巴巴史上最全的 Java 面試題,涉及大量 Java 面試知識(shí)點(diǎn)和相關(guān)試題。 JAVA基礎(chǔ) JAVA中的幾種基本數(shù)據(jù)類型是什么,各自占用多少字節(jié)。 S...
摘要:談起閉包,它可是兩個(gè)核心技術(shù)之一異步基于打造前端持續(xù)集成開(kāi)發(fā)環(huán)境本文將以一個(gè)標(biāo)準(zhǔn)的項(xiàng)目為例,完全拋棄傳統(tǒng)的前端項(xiàng)目開(kāi)發(fā)部署方式,基于容器技術(shù)打造一個(gè)精簡(jiǎn)的前端持續(xù)集成的開(kāi)發(fā)環(huán)境。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥(niǎo),不論是面試求職,還是日...
摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開(kāi)發(fā)和面試問(wèn)題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開(kāi)發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...
閱讀 909·2021-11-15 11:38
閱讀 2555·2021-09-08 09:45
閱讀 2864·2021-09-04 16:48
閱讀 2599·2019-08-30 15:54
閱讀 960·2019-08-30 13:57
閱讀 1656·2019-08-29 15:39
閱讀 530·2019-08-29 12:46
閱讀 3554·2019-08-26 13:39