memcached是作为一个cache服务器而设计的。内存失效判断是:当前时间>=对象更新时间+失效时间。
memcache的缓存有效对象实现包括:
(1)有效时间计算
(2)回收对象策略
1、有效时间
(1)有效时间方式
有效时间有两种方式1)相对时间:多长时间,给出过期的时间长度
2)绝对时间:到期时间,给出过期的最后期限
时间处理(服务端memcached.c)
#define REALTIME_MAXDELTA 60*60*24*30 // 定义30天的秒数
//对象剩下生命时间(单位秒)
static rel_time_t realtime(const time_t exptime) {
if (exptime == 0) return 0;
if (exptime > REALTIME_MAXDELTA) { // 超过30天,使用绝对时间
if (exptime <= process_started) // 小于进程启动日期
return (rel_time_t)1; //过期
return (rel_time_t)(exptime - process_started); // 返回进程启动之后的时间差
} else { // 不超过30天,是相对时间
return (rel_time_t)(exptime + current_time); // exptime + (tvsec - process_started)
}
}
相对时间时,返回的值是:服务器当前时间之后的exptime - process_started秒
绝对时间时,返回的值是:服务器当前时间之后的(exptime -服务器当前时间) - process_started秒
可以看到,如果Client和Server时间不一致,使用绝对时间很容易导致缓存过期。
所以使用相对时间是比较安全的做法。
(2)更新对象有效时间
对象有效时间(exptime),只有在cache值更新的时候(set/replace操作)才会更新,get操作不会更新这个时间。add操作如果cache已存在,并不更新exptime,仅仅刷新最后访问时间,所以应该使用set或replace操作。
2、回收对象策略
(1)回收对象时机
1)对于失效的cache,在get操作的时候,才真正回收(可供新的cache使用).代码如下:
item *do_item_get_notedeleted(const char *key, const size_t nkey, bool *delete_locked) {
item *it = assoc_find(key, nkey);
if (delete_locked) *delete_locked = false;
if (it && (it->it_flags & ITEM_DELETED)) {
/* it's flagged as delete-locked. let's see if that condition
is past due, and the 5-second delete_timer just hasn't
gotten to it yet... */
if (!item_delete_lock_over(it)) {
if (delete_locked) *delete_locked = true;
it = 0;
}
}
if (it != NULL && settings.oldest_live != 0 && settings.oldest_live <= current_time &&
it->time <= settings.oldest_live) {
do_item_unlink(it); // MTSAFE - cache_lock held 超过设置的最大时间也减少其引用计数
it = 0;
}
if (it != NULL && it->exptime != 0 && it->exptime <= current_time) {
do_item_unlink(it); // MTSAFE - cache_lock held 过期对象减少引用计数
it = 0;
}
if (it != NULL) {
it->refcount++;
DEBUG_REFCNT(it, '+');
}
return it;
}
2)方法flush_all:
清空memcached所有缓存时,会执行item_flush_expired()来处理过期失效的cache。
3)开启LRU算法:
通过-M选项会关闭了LRU算法,如果启用LRU算法,那么在给item分配内存的时候,LRU算法会正确回收失效的缓存。
启用LRU算法时,一定要注意设置足够大的内存,否则未失效的对象也可能被踢出。所以-M选项一定要慎用!