memcached缓存命中机制与PostgreSQL非常相似,都是对数据缓存进行分区,根据哈希码在对应分区内匹配键值。不同的是PG分区使用常数(早先为16、9.5开始改为128,可以支持更大缓存),分区数量较少;而memcached根据线程数确定初始分区数,数量相对较大,并且有后台维护程序动态管理。
分区数量确定,在线程初始化部分,thread.c中函数thread_init:
/* Want a wide lock table, but don't waste memory */ if (nthreads < 3) { power = 10; } else if (nthreads < 4) { power = 11; } else if (nthreads < 5) { power = 12; } else { /* 8192 buckets, and central locks don't scale much past 5 threads */ power = 13; } item_lock_count = hashsize(power); item_lock_hashpower = power;
默认线程数为4,hashpower为12。
thread.c中的store_item函数,以键值计算哈希码:
hv = hash(ITEM_key(item), item->nkey);
items.c中的item_get函数调用 assoc中的assoc_find函数, 根据哈希码确定键值取得分区链表后,从中查找匹配的键值:
while (it) { if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) { ret = it; break; } it = it->h_next; ++depth; }
分区扩展机制,:
/* We are done expanding.. just wait for next invocation */ mutex_lock(&cache_lock); started_expanding = false; pthread_cond_wait(&maintenance_cond, &cache_lock); /* Before doing anything, tell threads to use a global lock */ mutex_unlock(&cache_lock); slabs_rebalancer_pause(); switch_item_lock_type(ITEM_LOCK_GLOBAL); mutex_lock(&cache_lock); assoc_expand(); mutex_unlock(&cache_lock);
这是后台维护线程的部分代码,当条目数达到临界值(hashsize(hashpower) * 3) / 2)时触发maintenance_cond,条件触发在assoc.c中的assoc_start_expand函数中。