1. 程式人生 > >談談服務端快取的幾種用法

談談服務端快取的幾種用法

/從快取中獲取資料[較快的方式]
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之後,如果是,那麼說明這份快取太舊了,需要走更新流程。

//從快取中獲取資料[較快的方式][這裡的兩次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

這樣,無論是快取儲存時間過期了,還是快取本身有更新,都會觸發帶鎖機制的快取更新。

好了,先說到這裡,回頭有想起來的再做更新。