摘要:緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。這是緩存最原始的意義,同時也引申出了緩存最普遍的用法。但是現實中還有一種緩存,是主動更新的。
緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過memcached類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。
說起緩存,可能大家最直接想到的就是:“在數據庫前面擋一層”。這是緩存最原始的意義,同時也引申出了緩存最普遍的用法。
原始模式 代碼示例1(原始模式)://從緩存中獲取數據[較快的方式] data = getfromcache(id) if data == null then //從數據庫中獲取數據[較慢的方式] data = getfromdb(id) //緩存1天 setintocache(id, data, 86400) return data end return data緩存加鎖
上面這種情況下,當同時有N個請求到達,都同時執行getfromcache,那么都會發現data在緩存中不存在,然后都會去調用getfromdb,以及setintocache。這是不必要的,那么我們有沒有辦法減少這些并發呢。
最直接的想法是加鎖,當進入if條件中時,加一把鎖,讓其他進程不再執行下面的邏輯,而是等第一個進程的setintocache執行完成后,再重新執行一次getfromcache。
那這個鎖如何加呢?這里推薦一種省時省力的方法。通過直接在緩存value中設置過期時間來實現。
比如緩存的value值為data,那我們修改一下,把它放到一個json中,改成
{data:data,atime:1429618765}
我們增加了一個atime來記錄緩存生成的時間。而邏輯就變成下面這樣。
代碼示例2(緩存加鎖)://從緩存中獲取數據[較快的方式] data = getfromcache(id) data = json.decode(data) //如果通過檢查緩存生成時間,發現緩存已經過于陳舊,那么就將緩存過期時間設置為現在開始的5分鐘以后(這樣其他并發進程就會以為此緩存還未過期,還會繼續使用5分鐘,只讓當前這一個請求去重建緩存) if data != null && data.atime+86400 < now then data.atime = now+300-86400 data = json.encode(data) //對真正的cache來說,緩存10天或者更長時間 setintocache(id, data, 864000) //這里把data設置成null是為了走到下面的if中去重建緩存 data = null end if data == null then //從數據庫中獲取數據[較慢的方式] data = getfromdb(id) data = {data:data, atime:now} data = json.encode(data) //對真正的cache來說,緩存10天或者更長時間 setintocache(id, data, 864000) return data end return data
你可以會發現,這里也會存在并發啊,和上面例1一樣,第一個getfromcache到setintocache之間,如果同時有N個請求到來,不還是都會執行這段操作,都會去查庫嗎。
沒錯,是這樣的。但是我們仔細看一下,例1中,從getfromcache到setintocache之間,經歷了一次漫長的getfromdb操作,這個時間耗費可能是上百毫秒的。而我們例2中,并沒有進行什么操作,這個時間耗費只在毫秒甚至微秒級的。
所以例1中getfromcache到setintocache之間的并發是遠大于例2中的。例2中通過減小時間窗口,有效的模擬了鎖機制。同時還沒有增強額外的存儲復雜度。所以是推薦的一種方式。
可以說,我們所有的緩存都應該是例2的方式,他在各方面都優于例1(多保存的一個atime字段耗費的內存基本可以忽略不計。且atime很多時候對于調試程序還很有用)。
主動更新緩存那這樣就夠了嗎?對于被動過期型的緩存,這樣基本就可以了。但是現實中還有一種緩存,是主動更新的。試想有一種緩存,我們要求必須和數據庫中的數據一致,不能出現陳舊數據。那么上面的緩存方式就不合適了。
我們必然會添加一個流程:即當數據庫有更新時,同時更新緩存,因為緩存會自己重建,也可以修改為當數據庫有更新時,同時刪除緩存。
這里提到刪除或者更新緩存,就有點意思了。我們上面講到的都是非常簡單的緩存,即一個id對應一個key。那么試想,如果我們有一個分頁緩存,緩存了某一個文章最新的前10頁數據。分別的key是page_1,page_2...page10。
那么當我們有一條新數據產生,這10頁就都失效了,需要更新或者刪除10次。這顯然是不太科學的做法。
那么我們應該怎么做呢。我們可以借用上面例2中的方法,例2中,我們在緩存中增加了一個atime字段,標識為緩存的生成時間。我們既然知道緩存什么時候生成的,那問題就好解決了。我們在每次有新數據產生時,都去更新一個updatetime字段。然后獲取分頁緩存的時候,看一下這個updatetime字段是不是在atime之后,如果是,那么說明這份緩存太舊了,需要走更新流程。
代碼示例3(避免批量更新)://從緩存中獲取數據[較快的方式][這里的兩次get普通的緩存系統都支持一個請求完成] data = getfromcache(id) updatetime = getupdatetime(id) data = json.decode(data) //如果通過檢查緩存生成時間,發現緩存已經過于陳舊,那么就將緩存過期時間設置為現在開始的5分鐘以后(這樣其他并發進程就會以為此緩存還未過期,還會繼續使用5分鐘,只讓當前這一個請求去重建緩存) if data != null && (data.atime+86400 < now || date.atime < updatetime) then data.atime = now+300-86400 data = json.encode(data) //對真正的cache來說,緩存10天或者更長時間 setintocache(id, data, 864000) //這里把data設置成null是為了走到下面的if中去重建緩存 data = null end if data == null then //從數據庫中獲取數據[較慢的方式] data = getfromdb(id) data = {data:data, atime:now} data = json.encode(data) //對真正的cache來說,緩存10天或者更長時間 setintocache(id, data, 864000) return data end return data
這僅僅是在代碼示例2的基礎上增加了下面這一個條件判斷而已
date.atime < updatetime
這樣,無論是緩存保存時間過期了,還是緩存本身有更新,都會觸發帶鎖機制的緩存更新。
好了,先說到這里,回頭有想起來的再做更新。原文地址
順便插播一則招聘廣告。(碼字不易,求別刪招聘廣告,謝!)
易手機坐標深圳,做一款易用安全的老年智能手機,做老年手機第一品牌。現在灰常需要服務端同學入伙。有興趣的同學請私信或簡歷發郵箱:ligang#pingyijinren.com
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30217.html
摘要:緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。這是緩存最原始的意義,同時也引申出了緩存最普遍的用法。但是現實中還有一種緩存,是主動更新的。 緩存是一個常談常新的話題,作為一名服務端的技術,如果你入行一年都還沒用過memcached類產品,那只能說你的公司實在太小了,或者你干的活實在太邊緣了。 說起...
摘要:以下為大家整理了阿里巴巴史上最全的面試題,涉及大量面試知識點和相關試題。的內存結構,和比例。多線程多線程的幾種實現方式,什么是線程安全。點擊這里有一套答案版的多線程試題。線上系統突然變得異常緩慢,你如何查找問題。 以下為大家整理了阿里巴巴史上最全的 Java 面試題,涉及大量 Java 面試知識點和相關試題。 JAVA基礎 JAVA中的幾種基本數據類型是什么,各自占用多少字節。 S...
摘要:并總結經典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快速搭建項目。 本文是關注微信小程序的開發和面試問題,由基礎到困難循序漸進,適合面試和開發小程序。并總結vue React html css js 經典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快...
閱讀 1436·2021-09-02 19:23
閱讀 1586·2021-08-11 11:19
閱讀 641·2019-08-30 15:55
閱讀 1652·2019-08-30 12:50
閱讀 2241·2019-08-30 11:23
閱讀 2180·2019-08-29 13:13
閱讀 1500·2019-08-28 18:13
閱讀 3344·2019-08-26 11:53