from:
網際網路架構中快取無處不在,某廠牛人曾經說過:」快取就像清涼油,**不舒服,抹一下就好了」。高品質的儲存容量小,**高;低品質儲存容量大,**低,快取的目的就在於」擴充」高品質儲存的容量。本文**快取相關的一些問題。
lru替換演算法
快取的技術點包括記憶體管理和替換演算法。lru是使用最多的替換演算法,每次淘汰最久沒有使用的元素。lru快取實現分為兩個部分:hash表和lru鍊錶,hash表用於查詢快取中的元素,lru鍊錶用於淘汰。記憶體常以slab的方式管理。
上圖是memcache的記憶體管理示意圖,memcache以slab方式管理記憶體塊,從系統申請1mb大小的大塊記憶體並劃分為不同大小的chunk,不同slab的chunk大小依次為80位元組,80 * 1.25,80 * 1.25^2, …。向memcache中新增item時,memcache會根據item的大小選擇合適的chunk。
oceanbase最初也採用lru演算法,只是記憶體管理有些不同。oceanbase向系統申請2mb大小的大塊記憶體,插入item時直接追加到最後乙個2mb記憶體塊的尾部,當快取的記憶體量太大需要**時根據一定的策略整塊**2mb的記憶體,比如**最近最少使用的item所在的2mb記憶體塊。這樣的做法雖然不是特別精確,但是記憶體管理簡單,對於系統初期很有好處。
快取鎖
快取需要操作兩個資料結構:hash表和lru鍊錶。多執行緒操作cache時需要加鎖,比較直接的做法是整體加一把大鎖後再操作hash表和lru鍊錶。有如下的優化思路:
1, hash表和lru鍊錶使用兩把不同的鎖,且hash表鎖的粒度可以降低到每個hash桶一把鎖。這種做法的難點是需要處理兩種資料結構不一致導致的問題,假設操作順序為read hash -> del hash item -> del lru item -> read lru item,最後一次read lru item時item所在的記憶體塊可能已經被**或者重用,一般需要引入引用計數並考慮複雜的時序問題。
2, 採用多個lru鍊錶以減少lru表鎖粒度。hash表的鎖衝突可以通過增加hash桶的個數來解決,而lru鍊錶是乙個整體,難以分解。可以將快取的資料分成多個工作集,每個item屬於某個工作集,每個工作集乙個lru鍊錶。這樣做的主要問題是可能不均衡,比如某個工作集很熱,某些從整體上看比較熱的資料也可能被淘汰。
3, 犧牲lru的精確性以減少鎖。比如mysql中的lru演算法變形,大致如下:將lru鍊錶分成兩部分,前半部分和後半部分,如果訪問的item在前半部分,什麼也不做,而不是像傳統的lru演算法那樣將item移動到鍊錶頭部;又如linux page cache中的clock演算法。oceanbase目前的快取演算法也是通過犧牲精確性來減少鎖。前面提到,oceanbase快取以2mb的記憶體塊為單位進行淘汰,最開始採用lru策略,每次淘汰最近最少使用的item所在的2mb記憶體塊,然而,這樣做的問題是需要維護最近最少使用的item,即每次讀寫快取都需要加鎖。後續我們將淘汰策略修改為:每個2mb的記憶體塊記錄乙個訪問次數和乙個最近訪問時間,每次讀取item時,如果訪問次數大於所有2mb記憶體塊訪問次數的平均值,更新最近訪問時間;否則,將訪問次數加1。根據記錄的最近訪問時間淘汰2mb記憶體塊。雖然,這個演算法的快取命中率不容易評估,但是快取讀取只需要一些原子操作,不需要加鎖,大大減少了鎖粒度。
lirs思想
cache有兩個問題:乙個是前面提到的降低鎖粒度,另乙個是提高精準度,或者稱為提高命中率。lru在大多數情況下表現是不錯的,但是有如下的問題:
1, 順序掃瞄。順序掃瞄的情況下lru沒有命中情況,而且會淘汰其它將要被訪問的item從而汙染cache。
2, 迴圈的資料集大於快取大小。如果迴圈訪問且資料集大於快取大小,那麼沒有命中情況。
之所以會出現上述一些比較極端的問題,是因為lru只考慮訪問時間而沒有考慮訪問頻率,而lirs在這方面做得比較好。lirs將資料分為兩部分:lir(low inner-reference recency)和hir(high inner-reference recency),其中,lir中的資料是熱點,在較短的時間內被訪問了至少兩次。lirs可以看成是一種分級思想:第一級是hir,第二級是lir,資料先進入到第一級,當資料在較短的時間內被訪問兩次時成為熱點資料則進入lir,hir和lir內部都採用lru策略。這樣,lir中的資料比較穩定,解決了lru的上述兩個問題。lirs**中提出了一種實現方式,不過我們可以做一些變化,如可以實現兩級cache,cache元素先進入第一級cache,當訪問頻率達到一定值(比如2)時公升級到第二級,第一級和第二級均內部採用lru進行替換。oracle buffer cache中的touch count演算法也是採用了類似的思想。
ssd與快取
ssd發展很快,大有取代傳統磁碟之勢。ssd的發展是否會使得單機快取變得毫無必要我們無從得知,目前,memory + ssd + 磁碟的混合儲存方案還是比較靠譜的。ssd使用可以有如下不同的模式:
1, write-back:資料讀寫都走ssd,記憶體中的資料寫入到ssd即可,另外有單獨的執行緒定期將ssd中的資料刷到磁碟。典型的代表如facebook flashcache。
2, write-through:資料寫操作需要先寫到磁碟,記憶體和ssd合在一起看成兩級快取,即cache中相對較冷的資料在ssd,相對較熱的資料在記憶體。
當然,隨著ssd的應用,我想減少快取鎖粒度的重要性會越來越突出。
總結&推薦資料
到目前為止,我們在ssd,快取相關優化的工作還是比較少的。今後的一年左右時間,我們將會投入一定的精力在系統優化上,相信到時候再來總結的時候認識會更加深刻。我想,快取相關的優化工作首先要做的是根據需求制定乙個大致的評價標準,接著使用實際資料做一些實驗,最終可能會同時保留兩到三種實現方式或者配置略微有所不同的快取實現。快取相關的推薦資料如下:
Cache快取設計的一些思考
網際網路架構中快取無處不在,某廠牛人曾經說過 快取就像清涼油,不舒服,抹一下就好了 高品質的儲存容量小,高 低品質儲存容量大,低,快取的目的就在於 擴充 高品質儲存的容量。本文 快取相關的一些問題。lru替換演算法 快取的技術點包括記憶體管理和替換演算法。lru是使用最多的替換演算法,每次淘汰最久沒...
軟體設計的一些思考
軟體設計的一些思考 從事軟體開發工作已經五年了,仔細想想,雖然做了不少專案,但是在軟體技術上,感覺始終還是進步甚微,一方面和公司的情況有關,一方面,我想,也是自己個人總結和思考不夠吧。所以,慢慢的,還是有必要對自己的一些經驗做思考和總結。為什麼只談軟體設計,不談軟體開發呢,軟體開發涉及的不僅僅是設計...
關於空頁面設計的一些思考
空頁面是為了解決乙個什麼樣的問題,想達到什麼樣的效果,有哪些優化的空間?空頁面在app設計中的地位處於比較靠後的乙個地位,是將app的主流程頁面設計完整之後,再來進行一些頁面補充的時候,才會開始思考的事情。可就是這樣的乙個頁面,作為互動設計師也是有發揮空間的。思路如下圖 在思考的時候,思路是,空頁面...