Memcached的CAS机制的实现

转载自:http://1.simpcl.sinaapp.com/?p=1

CAS,又称Compare-and-Swap,代表一种原子操作。

Memcached的CAS机制解决的问题及其原理:

1. 实现了Check-and-Set原子操作功能;
2. 其使用方式为:首先使用gets指令一个key-value及key对应value的版本号;其次操作产生新的value值;最后使用cas指令重新提交key-value,并附带刚刚获得到的版本号;
3. 当服务端判断cas操作中的版本号不是最新的时,则认为改key的值已经被修改,本次cas操作失败。程序设计人员通过CAS机制可实现自增和自减的原子操作;

Memcached的CAS机制的实现:

1. Memcached CAS的核心是一个64-bit唯一的版本。服务端会为每个key生成一个64-bit唯一的整数值作为版本号,并保存在item结构体中,具体的存储结构见结构体item的定义:

typedef struct _stritem {
    struct _stritem *next;
    struct _stritem *prev;
    struct _stritem *h_next;    /* hash chain next */
    rel_time_t      time;       /* least recent access */
    rel_time_t      exptime;    /* expire time */
    int             nbytes;     /* size of data */
    unsigned short  refcount;
    uint8_t         nsuffix;    /* length of flags-and-length string */
    uint8_t         it_flags;   /* ITEM_* above */
    uint8_t         slabs_clsid;/* which slab class we're in */
    uint8_t         nkey;       /* key length, w/terminating null and padding */
    /* this odd type prevents type-punning issues when we do
     * the little shuffle to save space when not using CAS. */
    union {
        uint64_t cas;
        char end;
    } data[];
    /* if it_flags & ITEM_CAS we have 8 bytes CAS */
    /* then null-terminated key */
    /* then " flags length\r\n" (no terminating null) */
    /* then data with terminating \r\n (no terminating null; it's binary!) */
} item;

cas的版本号的值存储在item中key之前的位置处。

2. Memcached CAS的开启和关闭由settings.use_cas选项来控制。结构体struct settings存储了Memcached服务器的各种选项存储,其定义的代码如下:

struct settings {
    size_t maxbytes;
    int maxconns;
    int port;
    int udpport;
    char *inter;
    int verbose;
    rel_time_t oldest_live; /* ignore existing items older than this */
    int evict_to_free;
    char *socketpath;   /* path to unix socket if using local socket */
    int access;  /* access mask (a la chmod) for unix domain socket */
    double factor;          /* chunk size growth factor */
    int chunk_size;
    int num_threads;        /* number of worker (without dispatcher) libevent threads to run */
    int num_threads_per_udp; /* number of worker threads serving each udp socket */
    char prefix_delimiter;  /* character that marks a key prefix (for stats) */
    int detail_enabled;     /* nonzero if we're collecting detailed stats */
    int reqs_per_event;     /* Maximum number of io to process on each
                               io-event. */
    bool use_cas;
    enum protocol binding_protocol;
    int backlog;
    int item_size_max;        /* Maximum item size, and upper end for slabs */
    bool sasl;              /* SASL on/off */
    bool maxconns_fast;     /* Whether or not to early close connections */
    bool slab_reassign;     /* Whether or not slab reassignment is allowed */
    bool slab_automove;     /* Whether or not to automatically move slabs */
    int hashpower_init;     /* Starting hash power level */
};

默认情况下settings.use_cas的值为true,在settings_init调用的时候进行初始化设置:

static void settings_init(void) {
    settings.use_cas = true;
    ... ...
}

当启动Memcached服务时,如果指定了-C选项,则关闭cas机制,改部分代码在main函数中:

int main (int argc, char **argv) {
    ... ...
        case 'C' :
            settings.use_cas = false;
            break;
    ... ...
}

3. Memcached在分配item的内存时会根据是否开启settings.use_cas选项来分配内存和设置item->flags:

item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
    ... ...
    if (settings.use_cas) {
        ntotal += sizeof(uint64_t);
    }
    ... ...
    it->it_flags = settings.use_cas ? ITEM_CAS : 0;
    ... ...
}

4. Memcached CAS机制的相关实现:

在Memcached中,所有的命令处理逻辑在process_command中实现,该函数的实现根据不同的命令又细分为不同的处理函数。

gets指令调用process_get_command,该函数当最后的参数为true时,代表处理带cas的get操作,会在response中包含cas的值。

cas指令调用process_update_command,该函数当最后的参数true代表,处理带cas的update操作,最终会调用do_store_item函数来处理update操作。相关的cas的逻辑代码有:

/* validate cas operation */
        if(old_it == NULL) {
            // LRU expired
            stored = NOT_FOUND;
            pthread_mutex_lock(&c->thread->stats.mutex);
            c->thread->stats.cas_misses++;
            pthread_mutex_unlock(&c->thread->stats.mutex);
        }
        else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {
            // cas validates
            // it and old_it may belong to different classes.
            // I'm updating the stats for the one that's getting pushed out
            pthread_mutex_lock(&c->thread->stats.mutex);
            c->thread->stats.slab_stats[old_it->slabs_clsid].cas_hits++;
            pthread_mutex_unlock(&c->thread->stats.mutex);

            item_replace(old_it, it, hv);
            stored = STORED;
        } else {
            pthread_mutex_lock(&c->thread->stats.mutex);
            c->thread->stats.slab_stats[old_it->slabs_clsid].cas_badval++;
            pthread_mutex_unlock(&c->thread->stats.mutex);

            if(settings.verbose > 1) {
                fprintf(stderr, "CAS:  failure: expected %llu, got %llu\n",
                        (unsigned long long)ITEM_get_cas(old_it),
                        (unsigned long long)ITEM_get_cas(it));
            }
            stored = EXISTS;
        }

cas指令的cas版本值的递增在do_item_link函数中完成:

int do_item_link(item *it, const uint32_t hv) {
    ... ...
    /* Allocate a new CAS ID on link. */
    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
    ... ...
}

5. 其它:

除了显式的gets和cas操作为,incr/decr操作也会使用cas机制;
append/prepend操作也会涉及到cas的相关操作;

Posted in Memcached

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值