memcached源码分析-----item锁级别与item引用计数

本文详细分析了memcached中item的锁级别管理,包括段级别锁和全局级别锁的使用,以及如何在哈希表扩展时切换锁级别。同时,文章介绍了引用计数在memcached中的作用,如何避免读写冲突,以及在引用计数为0时如何删除item。此外,还提及了tail_repair_time的概念,用于处理worker线程意外挂断的情况。
摘要由CSDN通过智能技术生成



        转载请注明出处:http://blog.csdn.net/luotuo44/article/details/42913549



锁级别:

        从前面的《扩展哈希表》知道:哈希表进行扩展时,有一个专门的线程负责将item数据从旧哈希表迁移到新哈希表(因此,也称这个线程为迁移线程)。此外,还有一些worker线程会时不时访问item(包括插入、删除和获取)。这些线程的对item所做的操作基本上都是互斥的,必须加锁控制。

        如果只使用一个锁,抢到锁能使用哈希表,抢不到则不能使用。那么memcached的效率将变得相当低。为此,memcached采用类似数据库的策略:使用不同级别的锁。memcached定义了两个级别的锁:段级别和全局级别。在平时(不进行哈希表扩展时),使用段级别的锁。在扩展哈希表时,使用全局级别的锁。

        段级别是什么级别?将哈希表按照几个桶一段几个桶一段地平均分,一个段对应有多个桶,每一个段对应有一个锁。所以整个哈希表有多个段级别锁。由于段级别锁的数量在程序的一开始就已经确定了,不会再变的了。而随着哈希表的扩展,桶的数量是会增加的。所以随着哈希表的扩展,越来越多的桶对应一个段,也就是说越来越多的桶对应一个锁。


        在哈希表扩展阶段,迁移线程和workers线程都使用全局锁。这些线程竞争全局锁,抢到锁才允许对哈希表的item进行操作。在非扩展阶段,迁移线程处于休眠状态,workers线程使用段级别锁,抢到了某个段锁就允许访问对应的多个桶。因此如果不同的worker线程访问不同的段,那么就可以同时访问了,增加了并发量。

      

        下面看一下段级别锁和全局级别锁的定义。thread_init函数分配并初始化段级别锁。

static pthread_mutex_t *item_locks;//指向段锁数组的指针
/* size of the item lock hash table */
static uint32_t item_lock_count;//段锁的数量
static unsigned int item_lock_hashpower;
static pthread_mutex_t item_global_lock;//全局锁

#define hashsize(n) ((unsigned long int)1<<(n))

void thread_init(int nthreads, struct event_base *main_base) {
    int         i;
    int         power;

    pthread_mutex_init(&cache_lock, NULL);

    pthread_mutex_init(&init_lock, NULL);
    pthread_cond_init(&init_cond, NULL);

	//nthreads是workers线程的数量,由main函数调用时传入来
    if (nthreads < 3) {
        power = 10;
    } else if (nthreads < 4) {
        power = 11;
    } else if (nthreads < 5) {
        power = 12;
    } else {//最大为13
        /* 8192 buckets, and central locks don't scale much past 5 threads */
        power = 13;
    }

	//power是2的幂
    item_lock_count = hashsize(power);
    item_lock_hashpower = power;

	//哈希表中段级别的锁。并不是一个桶就对应有一个锁。而是多个桶共用一个锁
    item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));
    if (! item_locks) {
        perror("Can't allocate item locks");
        exit(1);
    }
    for (i = 0; i < item_lock_count; i++) {
        pthread_mutex_init(&item_locks[i], NULL);
    }


    pthread_mutex_init(&item_global_lock, NULL);

    ...
}


切换锁级别:

        现在看一下怎么使用段级别锁和全局级别锁。迁移线程并不会使用段级别锁,在assoc.c的assoc_maintenance_thread函数中,迁移线程只会调用item_lock_global()函数锁上全局锁item_global_lock。这里主要是看workers线程是怎么使用段级别锁和全局级别锁的。


worker线程的锁级别:

        workers线程如果要访问哈希表的item,会先调用item_lock函数进行加锁。item_lock函数会根据需要自动选择使用段级别锁还是全局级别锁。下面是具体的代码。

//memcached.h文件
//item锁级别
enum item_lock_types {
    ITEM_LOCK_GRANULAR = 0, //段级别
    ITEM_LOCK_GLOBAL //全局级别
};

//thread.c文件
static pthread_key_t item_lock_type_key;//线程私有数据的键值

void item_lock(uint32_t hv) {
	//获取线程私有变量
    uint8_t *lock_type = pthread_getspecific(item_lock_
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值