問題是這樣的:假設要你設計乙個快取系統,你如何實現快取的更新機制!
例如某**具有高併發訪問量,為提高效能,其中的一些網頁頁面都是存放在快取中的,當需要更新快取中的頁面時,快取系統如何完成快取的更新?
在給出解決方案之前,先看看記憶體資料庫redis中字典的設計。
redis 中的字典
結構表示如下:
1
2
3
4
5
6
7
8
9
10
11
typedef
struct
dict
dict;
我們暫時不管其dict結構體中其他的成員,只關注其中的雜湊表成員: 1
dictht ht[
2];
redis的字典底層是用hash表來實現的,乙個雜湊表裡面可以有多個雜湊表節點, 而每個雜湊表節點就儲存了字典中的乙個鍵值對,但是,但是有趣的是這裡使用了兩個hash表。
為什麼使用兩個hash表呢?
因為在真實使用場景下,我們需要擴充套件或者收縮hash表的規模。出於節約記憶體的考慮,我們不可能一次性分配乙個巨大巨大的hash表,因此,只在初始時分配乙個合適大小的hash表,在使用過程中,當發現碰撞率過高時,我們就需要擴大hash表的規模。同樣是基於節約記憶體的考慮,當hash表的填充率很低時,我們可以適當的收縮hash表的規模。
使用兩個hash表是為了方便進行hash表的擴充套件和收縮。擴充套件或收縮雜湊表時我們需要將
ht[0]
裡面的所有鍵值對 rehash 到
ht[1]
裡面。而且這個將ht[0]裡面的鍵值對rehash到ht[1]不是一次性、集中式地完成的, 而是分多次、漸進式地完成的。
這樣做的原因在於, 如果
ht[0]
裡只儲存著四個鍵值對, 那麼伺服器可以在瞬間就將這些鍵值對全部 rehash 到
ht[1]
; 但是, 如果雜湊表裡儲存的鍵值對數量不是四個, 而是四百萬、四千萬甚至四億個鍵值對, 那麼要一次性將這些鍵值對全部 rehash 到
ht[1]
的話, 龐大的計算量可能會導致伺服器在一段時間內停止服務。
因此, 為了避免 rehash 對伺服器效能造成影響, 伺服器不是一次性將 ht[0]
裡面的所有鍵值對全部 rehash 到 ht[1]
, 而是分多次、漸進式地將 ht[0]
裡面的鍵值對慢慢地 rehash 到 ht[1]
。以下是雜湊表漸進式 rehash 的詳細步驟:
為 ht[1]
分配空間, 讓字典同時持有 ht[0]
和 ht[1]
兩個雜湊表。
在字典中維持乙個索引計數器變數 rehashidx
, 並將它的值設定為 0
, 表示 rehash 工作正式開始。
在 rehash 進行期間, 每次對字典執行新增、刪除、查詢或者更新操作時, 程式除了執行指定的操作以外, 還會順帶將 ht[0]
雜湊表在 rehashidx
索引上的所有鍵值對 rehash 到 ht[1]
, 當 rehash 工作完成之後, 程式將 rehashidx
屬性的值增一。
隨著字典操作的不斷執行, 最終在某個時間點上, ht[0]
的所有鍵值對都會被 rehash 至 ht[1]
, 這時程式將 rehashidx
屬性的值設為 -1
, 表示 rehash 操作已完成。
漸進式 rehash 的好處在於它採取分而治之的方式, 將 rehash 鍵值對所需的計算工作均灘到對字典的每個新增、刪除、查詢和更新操作上, 從而避免了集中式 rehash 而帶來的龐大計算量。
【ps:如果上面的描述不夠清楚,可以移步這裡:漸進式rehash】
ok,看完的redis的字典設計及其rehash機制,相信其能給我們帶來靈感。現在我們再來說說快取系統的更新機制設計問題。
如下是乙個可行的解決方案(假設我們的更新機制是每5分鐘更新一次快取):
1)設計兩個快取池,記為a、b,而a和b的內容都是從後端伺服器資料庫中獲取到的資料。正常情況下,客戶端的請求都是從快取池a中獲取快取內容,同時,設定乙個全域性的變數ref用於記錄當前正在訪問快取a的客戶端數量,來乙個客戶端請求將ref值加1,響應完乙個客戶端請求後ref減一。
2)當快取更新時間到時,如果ref不為0,則我們不能直接更新快取,因為這時有客戶端正在從快取池a取資料。這裡,我們可以借鑑redis的rehash思想,更新時間到,我們將客戶端的訪問都引導到b快取池,此時的快取池a不再接受新的客戶端資料請求,a的ref變數只減不增,當ref變數減少到0時,我們便可以更新a快取池中的內容了。
Oscache的強行更新機制
背景 在產品中也許不需要強行更新,但是測試的時候往往需要。part 1 當你強行更新快取時會發生如下步驟 step1 generalcacheadministrator.flushall step2 cache.flushall date date,string origin flushall的源 ...
Oscache的強行更新機制
背景 在產品中也許不需要強行更新,但是測試的時候往往需要。part 1 當你強行更新快取時會發生如下步驟 step1 generalcacheadministrator.flushall step2 cache.flushall date date,string origin flushall的源 ...
Oscache的強行更新機制
背景 在產品中也許不需要強行更新,但是測試的時候往往需要。part 1 當你強行更新快取時會發生如下步驟 step1 generalcacheadministrator.flushall step2 cache.flushall date date,string origin flushall的源 ...