天天看點

《Redis實戰》一2.4 資料行緩存

本節書摘來異步社群《redis實戰》一書中的第2章,第2.4節,作者: 【美】josiah l. carlson(約西亞 l.卡爾森)譯者: 黃健宏 責編: 楊海玲,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

到目前為止,我們已經将原本由關系資料庫和網頁浏覽器實作的登入和訪客會話轉移到了redis上面實作;将原本由關系資料庫實作的購物車也放到了redis上面實作;還将所有頁面緩存到了redis裡面。這一系列工作提升了網站的性能,降低了關系資料庫的負載并減少了網站成本。

fake web retailer的商品頁面通常隻會從資料庫裡面載入一兩行資料,包括已登入使用者的使用者資訊(這些資訊可以通過ajax動态地載入,是以不會對頁面緩存造成影響)和商品本身的資訊。即使是那些無法被整個緩存起來的頁面——比如使用者賬号頁面、記錄使用者以往購買商品的頁面等等,程式也可以通過緩存頁面載入時所需的資料庫行來減少載入頁面所需的時間。

為了展示資料行緩存的作用,我們假設fake web retailer為了清空舊庫存和吸引客戶消費,決定開始新一輪的促銷活動:這個活動每天都會推出一些特價商品供使用者搶購,所有特價商品的數量都是限定的,賣完即止。在這種情況下,網站是不能對整個促銷頁面進行緩存的,因為這可能會導緻使用者看到錯誤的特價商品剩餘數量,但是每次載入頁面都從資料庫裡面取出特價商品的剩餘數量的話,又會給資料庫帶來巨大的壓力,并導緻我們需要花費額外的成本來擴充資料庫。

為了應對促銷活動帶來的大量負載,我們需要對資料行進行緩存,具體的做法是:編寫一個持續運作的守護程序函數,讓這個函數将指定的資料行緩存到redis裡面,并不定期地對這些緩存進行更新。緩存函數會将資料行編碼(encode)為json字典并存儲在redis的字元串裡面,其中,資料列(column)的名字會被映射為json字典的鍵,而資料行的值則會被映射為json字典的值,圖 2-1展示了一個被緩存的資料行示例。

《Redis實戰》一2.4 資料行緩存

程式使用了兩個有序集合來記錄應該在何時對緩存進行更新:第一個有序集合為排程(schedule)有序集合,它的成員為資料行的行id,而分值則是一個時間戳,這個時間戳記錄了應該在何時将指定的資料行緩存到redis裡面;第二個有序集合為延時(delay)有序集合,它的成員也是資料行的行id,而分值則記錄了指定資料行的緩存需要每隔多少秒更新一次。

使用json而不是其他格式 因為json簡明易懂,并且據我們所知,目前所有擁有redis用戶端的程式設計語言都帶有能夠高效地編碼和解碼json格式的函數庫,是以這裡的緩存函數使用了json格式來表示資料行,而沒有使用xml、google的protocol buffer、thrift、bson、messagepack或者其他序列化格式。在實際應用中,讀者可以根據自己的需求和喜好來選擇編碼資料行的格式。

嵌套多個結構 使用過其他非關系資料庫的使用者可能會期望redis也擁有嵌套多個結構的能力,比如說,一個剛開始使用redis的使用者可能會期盼着散列能夠包含有序集合值或者清單值。盡管嵌套結構這個特性在概念上并無不妥,但這個特性很快就會引起類似以下這樣的問題:“對于一個位于嵌套第5層的散列,我們如何才能對它的值執行自增操作呢?”為了保證指令文法的簡單性,redis并不支援嵌套結構特性。如果有需要的話,讀者可以通過使用鍵名來模拟嵌套結構特性:比如使用鍵user:123表示存儲使用者資訊的散列,并使用鍵user:123:posts表示存儲使用者最近發表文章的有序集合;又或者直接将嵌套結構存儲到json或者其他序列化格式裡面(第11章将介紹使用lua腳本在伺服器端直接以json格式或者messagepack格式對資料進行編碼的方法)。

為了讓緩存函數定期地緩存資料行,程式首先需要将行id和給定的延遲值添加到延遲有序集合裡面,然後再将行id和目前時間的時間戳添加到排程有序集合裡面。實際執行緩存操作的函數需要用到資料行的延遲值,如果某個資料行的延遲值不存在,那麼這個排程商品将會被移除。如果我們想要移除某個資料行已有的緩存,并且讓緩存函數不再緩存那個資料行,那麼隻需要把那個資料行的延遲值設定為小于或等于0就可以了。代碼清單2-7展示了負責排程緩存和終止緩存的函數。

代碼清單2-7 schedule_row_cache()函數

《Redis實戰》一2.4 資料行緩存

現在我們已經完成了排程部分,那麼接下來該如何對資料行進行緩存呢?負責緩存資料行的函數會嘗試讀取排程有序集合的第一個元素以及該元素的分值,如果排程有序集合沒有包含任何元素,或者分值存儲的時間戳所指定的時間尚未來臨,那麼函數會先休眠50毫秒,然後再重新進行檢查。當緩存函數發現一個需要立即進行更新的資料行時,緩存函數會檢查這個資料行的延遲值:如果資料行的延遲值小于或者等于0,那麼緩存函數會從延遲有序集合和排程有序集合裡面移除這個資料行的id,并從緩存裡面删除這個資料行已有的緩存,然後再重新進行檢查;對于延遲值大于0的資料行來說,緩存函數會從資料庫裡面取出這些行,将它們編碼為json格式并存儲到redis裡面,然後更新這些行的排程時間。執行以上工作的緩存函數如代碼清單2-8所示。

代碼清單2-8 守護程序函數cache_rows()

《Redis實戰》一2.4 資料行緩存

通過組合使用排程函數和持續運作緩存函數,我們實作了一種重複進行排程的自動緩存機制,并且可以随心所欲地控制資料行緩存的更新頻率:如果資料行記錄的是特價促銷商品的剩餘數量,并且參與促銷活動的使用者非常多的話,那麼我們最好每隔幾秒更新一次資料行緩存;另一方面,如果資料并不經常改變,或者商品缺貨是可以接受的,那麼我們可以每分鐘更新一次緩存。

在這一節中,我們學習了如何将資料行緩存到redis裡面,在接下來的一節中,我們将通過隻緩存一部分頁面來減少實作頁面緩存所需的記憶體數量。