用Redis時巫糙,有用到EXPIRE朗儒、PEXPIRE、SETEX之類命令去設(shè)置key的過期時間参淹。從2.8版開始還可以去做簡單的定時器服務(wù)醉锄。
原先也沒太具體去了解Redis的過期實現(xiàn)方式,但心中總覺得有塊石頭沒放下浙值,于是乎開3.0的源碼去翻看了恳不。
過期策略有3種:立即過期、定時過期开呐、訪問過期(惰性過期)烟勋。但看了源碼后规求,發(fā)現(xiàn)Redis并沒采用立即過期的策略,而是采用 定時過期 和 訪問過期 混合方式卵惦。使用立即過期的話阻肿,Linux環(huán)境下在Redis進(jìn)程里會有很多timerfd,在幾十萬個Key這種數(shù)量級開始沮尿,對cpu是很大的負(fù)擔(dān)丛塌。
redis.h
在redis.h文件里有redisDb結(jié)構(gòu)體
typedef struct redisDb {
dict *dict;
dict *expires;
......
}
expires這屬性是存放當(dāng)前db有過期時間的鍵,使用hash數(shù)據(jù)結(jié)構(gòu)畜疾。
定期刪除策略
代碼在redis.c的activeExpireCycle函數(shù)赴邻,需要傳個type參數(shù),用以區(qū)分是用“快速模式”還是“正常模式”啡捶。
在“正常模式”下乍楚,會遍歷每一個編號下的庫,然后最多隨機(jī)獲取20個帶過期時間的key(20是宏ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP的默認(rèn)值)届慈,倘若key太多則直接return了徒溪。接著調(diào)用redis.c/activeExpireCycleTryExpire函數(shù)嘗試去刪除它,能刪除成功則再發(fā)送expired的pub通知給訂閱者即可金顿。定期刪除的定時時長是100ms臊泌。
惰性過期
代碼分布在所有讀寫命令里,如SET揍拆、GET渠概、TTL、SADD嫂拴、HGET...等播揪。每次調(diào)用這些命令的實際執(zhí)行前,都會調(diào)用db.c/expireIfNeeded函數(shù)來刪除過期鍵筒狠,在刪除之前猪狈,也會發(fā)送expired的pub通知。
EXPIRE/PEXPIRE/SETEX的作用辩恼?
這幾個命令的真正作用是設(shè)置過期時間雇庙,如EXPIRE命令的處理在db.c/expireCommand開始,然后將過期時間單位從秒轉(zhuǎn)換成毫秒灶伊,接著判斷是否存在從節(jié)點疆前,最后傳播刪除了的expired key(pub del)。在主節(jié)點則設(shè)置過期時間聘萨,是在宏代碼里去設(shè)置值(聯(lián)合體結(jié)構(gòu))竹椒,最后在pub expire的通知。
后記
redis是單線程的米辐,但個人覺得在pub方面可以用另一條線程去處理胸完。