memcache研究札记 之 item_locks锁数组
memcache研究笔记 之 item_locks锁数组
如果收到了管道读回调事件:
mc里面用了大量的锁,简直是随处可见,在确保不死锁的同时保证性能!里面精髓之处值得琢磨学习
mc用了哪些锁:conn_lock、slabs_lock、slabs_rebalance_lock、hold_lock、item_locks
本篇针对item_locks的粒度变化做了研究:
static pthread_mutex_t *item_locks;
item_lock_count = hashsize(power); 默认4个threads,threads与item_locks成正比!
item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));
item_locks[(hv & hashmask(hashpower)) % item_lock_count] 桶对应的锁
锁有两种粒度:
enum item_lock_types {
ITEM_LOCK_GRANULAR = 0, 细粒度的锁
ITEM_LOCK_GLOBAL //在扩展的时候会被切换到全局锁
};
memcached在扩容操作时,加的都是全局锁,就是所有item(所有hash桶中的)都是一把锁,在扩容结束时,item锁被重新切换回hash桶上的锁,这里锁是分段加锁的(几个桶一个锁,这个具体数值取决与初始的worker的数量,worker数量越多,锁越细,越少hash桶公用一个锁)。
::switch_item_lock_type
void switch_item_lock_type(enum item_lock_types type) {
char buf[1];
int i;
switch (type) {
case ITEM_LOCK_GRANULAR:
buf[0] = 'l';
break;
case ITEM_LOCK_GLOBAL:
buf[0] = 'g';
break;
default:
fprintf(stderr, "Unknown lock type: %d\n", type);
assert(1 == 0);
break;
}
pthread_mutex_lock(&init_lock);
init_count = 0;
for (i = 0; i < settings.num_threads; i++) {
if (write(threads[i].notify_send_fd, buf, 1) != 1) { /* Listen for notifications from other threads */往管道写状态
perror("Failed writing to notify pipe");
/* TODO: This is a fatal problem. Can it ever happen temporarily? */
}
}
wait_for_thread_registration(settings.num_threads); //等待每个线程处理完设置完锁的类型,然后一直等待!!![标签1]
pthread_mutex_unlock(&init_lock);
}
::setup_thread (LIBEVENT_THREAD *me) 就是在这里注册了管道读回调事件thread_libevent_process
/* Listen for notifications from other threads */
event_set(&me->notify_event, me->notify_receive_fd, EV_READ | EV_PERSIST, thread_libevent_process,
me);
::thread_libevent_process
case 'g':
me->item_lock_type = ITEM_LOCK_GLOBAL;
register_thread_initialized(); //pthread_cond_signal(&init_cond);锁类型更新后发出条件信号告诉[标签1]类型已经改变 [标签2]
break;
再来看下item和item_lock_types 的关系,这些明白了,在wait_for_thread_registration完成后lock_type变了,如果遇到hash表扩容的话,变成全局锁,否则使用小粒度的局部锁来管理被划定的几个桶!
void item_lock(uint32_t hv) {
uint8_t *lock_type = pthread_getspecific(item_lock_type_key);
if (likely(*lock_type == ITEM_LOCK_GRANULAR)) {
mutex_lock(&item_locks[(hv & hashmask(hashpower)) % item_lock_count]);
} else {
mutex_lock(&item_global_lock);
}
}
上面完成了switch_item_lock_type的意图:锁粒度的升降级