最近加班比較累,完全不想寫作了。。
剛看到一篇有趣的文章,是redis的作者antirez對redis的LRU算法的回顧。LRU算法是Least Recently Used的意思,將最近最少使用的資源丟掉。Redis經常被用作cache,如果能夠將不常用的key移除,盡量保留常用的,那內存的利用率就相當高了。當然,LRU也有弱點,考慮下面一種情況:
~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~| ~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~| ~~~~~~~~~~C~~~~~~~~~C~~~~~~~~~C~~~~~~| ~~~~~D~~~~~~~~~~D~~~~~~~~~D~~~~~~~~~D|
假設有4個key,A以5秒1次的頻率被訪問,B訪問頻率是2秒1次,C和D的訪問頻率都是10秒1次。因為B有最高的訪問頻率,所以B的空閑訪問時間是最小的。它的上次訪問時間也是第二最近的。但是D是一個特例,它的訪問頻率是10秒1次,但是它的訪問時間是最近的。
當然,長期來看,這個算法還是不錯的。通常訪問頻率高的key都有相對比較小的空閑時間。LRU算法會換掉最近最少使用的key,即空閑時間最長的。這個算法實現起來非常簡單,我們秩序要跟蹤一個key上次訪問的時間即可,甚至有時候都不用,只要維持一個鏈表,有訪問的就移動到表頭,需要換掉元素的時候,就移除鏈表尾部的。
Redis最早並不支持LRU替換。通過修改Redis對象結構,作者擠出了24bit空間來做這個事情。因為指針太大,也沒有空間來用鏈表實現。24bit用來存時間戳的低位已經足夠了,可以支撐194天了,key的數據刷新頻繁,夠用了。
如何選擇最大空閑時間的key來換出也是一個問題,Redis的key存放在一個平坦的哈希表里,添加一個額外的數據結構來完成代價太大。既然LRU是理想換頁的一個漸近算法,我們何不嘗試下LRU的漸近算法呢?
Redis的原始實現非常簡單:當需要換出一個Key的時候,選任意3個key,,換出其中空閑時間最長的。相當於對整個key空間進行隨機采樣,並換出較好的選擇。后來這個算法演變成N個隨機key,算法優化后,默認變成采樣5個key,速度沒有損失。雖然實現是如此簡單,但工作起來非常好。如果你仔細思考,你會發現你不可能用這個算法作出最佳決定,但是你也不大可能作出非常壞的決定。如果數據集里有一堆經常訪問的key,5選1不大可能都挑中“熱”的key。