編輯:關於android開發
通過上面簡單的介紹與實現,現在我們基本已經了解了什麼是LRU,下面我們來看看LRU算法在redis 內部的實現細節,以及其會在什麼情況下帶來問題。在redis內部,是通過全局結構體struct redisServer 保存redis啟動之後相關的信息,比如:
- class Node:
- key=None
- value=None
- pre=None
- next=None
- def __init__(self,key,value):
- self.key=key
- self.value=value
- class LRUCache:
- capacity=0
- map={} # key is string ,and value is Node object
- head=None
- end=None
- def __init__(self,capacity):
- self.capacity=capacity
- def get(self,key):
- if key in self.map:
- node=self.map[key]
- self.remove(node)
- self.setHead(node)
- return node.value
- else:
- return -1
- def getAllKeys(self):
- tmpNode=None
- if self.head:
- tmpNode=self.head
- while tmpNode:
- print (tmpNode.key,tmpNode.value)
- tmpNode=tmpNode.next
- def remove(self,n):
- if n.pre:
- n.pre.next=n.next
- else:
- self.head=n.next
- if n.next:
- n.next.pre=n.pre
- else:
- self.end=n.pre
- def setHead(self,n):
- n.next=self.head
- n.pre=None
- if self.head:
- self.head.pre=n
- self.head=n
- if not self.end:
- self.end=self.head
- def set(self,key,value):
- if key in self.map:
- oldNode=self.map[key]
- oldNode.value=value
- self.remove(oldNode)
- self.setHead(oldNode)
- else:
- node=Node(key,value)
- if len(self.map) >= self.capacity:
- self.map.pop(self.end.key)
- self.remove(self.end)
- self.setHead(node)
- else:
- self.setHead(node)
- self.map[key]=node
- def main():
- cache=LRUCache(100)
- #d->c->b->a
- cache.set('a','1')
- cache.set('b','2')
- cache.set('c',3)
- cache.set('d',4)
- #遍歷lru鏈表
- cache.getAllKeys()
- #修改('a','1') ==> ('a',5),使該節點從LRU尾端移動到開頭.
- cache.set('a',5)
- #LRU鏈表變為 a->d->c->b
- cache.getAllKeys()
- #訪問key='c'的節點,是該節點從移動到LRU頭部
- cache.get('c')
- #LRU鏈表變為 c->a->d->b
- cache.getAllKeys()
- if __name__ == '__main__':
- main()
redisServer 中包含了redis服務器啟動之後的基本信息(PID,配置文件路徑,serverCron運行頻率hz等),外部可調用模塊信息,網絡信息,RDB/AOF信息,日志信息,復制信息等等。 我們看到上述結構體中lruclock:LRU_BITS,其中存儲了服務器自啟動之後的lru時鐘,該時鐘是全局的lru時鐘。該時鐘100ms(可以通過hz來調整,默認情況hz=10,因此每1000ms/10=100ms執行一次定時任務)更新一次。
- struct redisServer {
- pid_t pid; /* Main process pid. */
- char *configfile; /* Absolute config file path, or NULL */
- …..
- unsigned lruclock:LRU_BITS; /* Clock for LRU eviction */
- ...
- };
因此lrulock最大能到(2**24-1)/3600/24= 194天,如果超過了這個時間,lrulock重新開始。對於redisserver來說,server.lrulock表示的是一個全局的lrulock,那麼對於每個redisObject都有一個自己的lrulock。這樣每redisObject就可以根據自己的lrulock和全局的server.lrulock比較,來確定是否能夠被淘汰掉。
- server.lruclock = getLRUClock();
- getLRUClock函數如下:
- #define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
- #define LRU_BITS 24
- #define LRU_CLOCK_MAX ((1<
lru */ - /* Return the LRU clock, based on the clock resolution. This is a time
- * in a reduced-bits format that can be used to set and check the
- * object->lru field of redisObject structures. */
- unsigned int getLRUClock(void) {
- return (mstime()/LRU_CLOCK_RESOLUTION) & LRU_CLOCK_MAX;
- }
- typedef struct redisObject {
- unsigned type:4;
- unsigned encoding:4;
- unsigned lru:LRU_BITS; /* LRU time (relative to server.lruclock) or
- * LFU data (least significant 8 bits frequency
- * and most significant 16 bits decreas time). */
- int refcount;
- void *ptr;
- } robj
- /* Low level key lookup API, not actually called directly from commands
- * implementations that should instead rely on lookupKeyRead(),
- * lookupKeyWrite() and lookupKeyReadWithFlags(). */
- robj *lookupKey(redisDb *db, robj *key, int flags) {
- dictEntry *de = dictFind(db->dict,key->ptr);
- if (de) {
- robj *val = dictGetVal(de);
- /* Update the access time for the ageing algorithm.
- * Don't do it if we have a saving child, as this will trigger
- * a copy on write madness. */
- if (server.rdb_child_pid == -1 &&
- server.aof_child_pid == -1 &&
- !(flags & LOOKUP_NOTOUCH))
- {
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- unsigned long ldt = val->lru >> 8;
- unsigned long counter = LFULogIncr(val->lru & 255);
- val->lru = (ldt << 8) | counter;
- } else {
- val->lru = LRU_CLOCK();
- }
- }
- return val;
- } else {
- return NULL;
- }
- }
這裡暫不討論LFU,TTL淘汰算法和noeviction的情況,僅僅討論lru所有場景下的,淘汰策略具體實現。(LFU和TTL將在下一篇文章中詳細分析)。
- # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
- # is reached. You can select among five behaviors:
- #
- # volatile-lru -> Evict using approximated LRU among the keys with an expire set. //在設置了過期時間的key中,使用近似的lru淘汰策略
- # allkeys-lru -> Evict any key using approximated LRU. //所有的key均使用近似的lru淘汰策略
- # volatile-lfu -> Evict using approximated LFU among the keys with an expire set. //在設置了過期時間的key中,使用lfu淘汰策略
- # allkeys-lfu -> Evict any key using approximated LFU. //所有的key均使用lfu淘汰策略
- # volatile-random -> Remove a random key among the ones with an expire set. //在設置了過期時間的key中,使用隨機淘汰策略
- # allkeys-random -> Remove a random key, any key. //所有的key均使用隨機淘汰策略
- # volatile-ttl -> Remove the key with the nearest expire time (minor TTL) //使用ttl淘汰策略
- # noeviction -> Don't evict anything, just return an error on write operations . //不允許淘汰,在寫操作發生,但內存不夠時,將會返回錯誤
- #
- # LRU means Least Recently Used
- # LFU means Least Frequently Used
- #
- # Both LRU, LFU and volatile-ttl are implemented using approximated
- # randomized algorithms.
主動淘汰是通過activeExpireCycle 來實現的,這部分的邏輯如下:
- void databasesCron(void){
- /* Expire keys by random sampling. Not required for slaves
- * as master will synthesize DELs for us. */
- if (server.active_expire_enabled && server.masterhost == NULL)
- activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
- …..
- }
每次執行命令前,都會調用freeMemoryIfNeeded來檢查內存的情況,並釋放相應的內存,如果釋放後,內存仍然不夠,直接向請求的客戶端返回OOM。具體的步驟如下:
- processCommand 函數關於內存淘汰策略的邏輯:
- /* Handle the maxmemory directive.
- *
- * First we try to free some memory if possible (if there are volatile
- * keys in the dataset). If there are not the only thing we can do
- * is returning an error. */
- if (server.maxmemory) {
- int retval = freeMemoryIfNeeded();
- /* freeMemoryIfNeeded may flush slave output buffers. This may result
- * into a slave, that may be the active client, to be freed. */
- if (server.current_client == NULL) return C_ERR;
- /* It was impossible to free enough memory, and the command the client
- * is trying to execute is denied during OOM conditions? Error. */
- if ((c->cmd->flags & CMD_DENYOOM) && retval == C_ERR) {
- flagTransaction(c);
- addReply(c, shared.oomerr);
- return C_OK;
- }
- }
2.內存使用率過高,則會導致內存不夠,從而發起被動淘汰策略,從而使應用訪問超時。 3.合理的調整hz參數,從而控制每次主動淘汰的頻率,從而有效的緩解過期的key數量太多帶來的上述超時問題。
- timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100
- ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC=25
- server.hz>=1(默認為10)
- timelimit<=250ms
Android熱補丁技術—dexposed原理簡析(手機淘寶采用方案) 本文由嵌入式企鵝圈原創團隊成員、阿裡資深工程師Hao分享。 上篇文章《Android無線開發的幾種
Android studio環境搭建,androidstudio搭建首先要下載jdk,下載好以後配置環境變量,這裡略過,不會的可以百度搜索,這裡附上jdk下載地址:htt
Android-----完全隱藏軟鍵盤,android-----隱藏隱藏軟鍵盤一直是我頭痛的事情,沒有找到一種真正能隱藏的方法。點擊EditText的時候總是彈出軟鍵盤。
Android——eclipse下運行android項目報錯 Conversion to Dalvik format failed with error 1解決,andr