為什么寫?
和大家一樣,我有每天逛逛博客園的習慣,今天在博客園看到了“一只攻城獅”寫的《初遇 Asp.net MVC 數據庫依賴緩存那些事兒》。該朋友利用.Net的SqlCacheDependency緩存依賴,解決了緩存數據何時更新的問題。
但是該思路具有一定的局限性,如:要利用數據庫的存儲過程,來通知客戶端更新緩存,這就離不開微軟的Sql Server那套體制,如果利用別的數據庫,恐怕就沒有那么好實現了。且存儲過程需要在數據庫中執行,不利於將業務向服務程序轉移。
程序員比較忌諱造輪子,相信程序員寫博客也是如此,因此,我還是想站在巨人的肩膀上,借用“一只攻城獅”在《初遇 Asp.net MVC 數據庫依賴緩存那些事兒》寫的背景,來引出我想說的內容,如果“一只攻城獅”覺得有何不妥之處,請聯系我做下架處理。
--------------------------------------------------------------引用開始-----------------------------------------------
最近做一個非常簡單的功能,就是使用ajax請求的方式從服務端請求一段下拉表的數據。
以前也有做過這個功能,只不過這次做這個功能的時候冒出了一個想法:
我請求的這段數據它是一段相對比較固定的數據,也就是說它不怎么改變,也許幾個月才會改變一次。由於這種數據的變化周期很長,所以以前做這種功能的時候,會使用緩存進行優化,可以直接從緩存中讀取數據,避免每一次接收了ajax請求后都要向數據庫要數據,減少服務器與數據庫之間的交互,減輕數據庫服務器的壓力。但是問題來了,數據的變化周期再長終究是要變化的,當數據庫中的數據變化的時候你就要對舊有的緩存內容進行移除(remove)操作。
.......................{中間省略XXX字,中間作者大致講訴了設置了緩存定期過期}.................................
緩存定期過期有一個壞處:在還沒到達過期時間的這段時間里,請求的數據依然是原來的緩存中數據,如果數據庫數據在這期間進行了更新,那么緩存數據和數據庫中的數據並不一致。
其中設置的絕對過期時間點要根據實際的數據刷新的可容忍度來進行設定,而恰好在我的這個應用場景中的可容忍度最不能把握,它要求的是 當數據庫中的數據改變以后,緩存中對應的數據在下一次請求結束后一定要馬上跟着改變,當然你也可以把過期時間盡可能的調小,調到一秒。當然,這樣的話還是要頻繁的向數據庫進行請求,那不是背離了我們原本使用緩存優化的目的了嗎?
所以現在的問題是:有沒有一種方法能讓數據庫和服務器程序建立一種聯系,這種聯系好比是一種“心靈感應”,當數據庫表中的數據發生變化的時候,馬上就能讓服務器中的對應的緩存項“感應”到這個變化,從而讓原來的緩存項失效呢?
--------------------------------------------------------------引用結束------------------------------------------
我的做法
綜上所述,客戶端(或瀏覽器)緩存數據的痛點在於,數據何時更新?如何讓客戶端知道,服務端數據變了?
分四步走。
第一步,初次請求數據時
客戶端在初次請求數據時,會把客戶端想要的數據連同數據的版本號(數據上次的更新時間)一起發送給客戶端,數據版本號時存在Redis數據庫中的,我們知道,Redis中的數據存儲在內存中且讀取數據比關系型數據庫快的不是一點點。
客戶端收到數據后,會把收到的數據和數據版本號緩存下來。
第二步,當數據庫數據更新時
當數據庫數據更新時,服務端在更新關系型數據庫的同時會把Redis的數據版本號更新為當前時間。
第三步,客戶端用數據時:
客戶端需要使用緩存數據時,會向服務端索要數據版本號(也就是數據上次的更新時間),如果該數據版本號與客戶端緩存的數據版本號一致,那么,客戶端緩存的數據時安全可用的,如果不一致,那么說明數據已經更新了,客戶端把新的版本號緩存下來並重新獲取。那么,執行第四步。
第四步,重新獲取數據
重新獲取數據時,就不用攜帶版本號了,客戶端在第三步時已經獲取並緩存下來了。
利弊
好處:
1.當請求的數據量交大但是變動又不頻繁時,客戶端與服務端不必頻繁地交換大型數據,只需交換數據版本號即可。
2.數據版本號存儲在Redis數據庫中,不僅讀取速度快,而且數據量小,所以響應快,交換成本低。
3.該思路通用性強,適合任何類型的關系型數據庫與Nosql數據庫搭配使用。
弊端:
1.客戶端在每次使用數據前,都要與服務端進行一次通訊進行校驗數據版本號。
好與壞不是絕對的,適合的才是最好的,以上是我的解決思路,大家有不同觀點,歡迎留言討論,也感謝“一只攻城獅”提供討論背景!