memcache 1.4.24源码分析

  1. 内存管理
    最底层为slab。
/* powers-of-N allocation structures */

typedef struct {
    unsigned int size;      /* sizes of items 此级slab的chunk大小时多少 */
    unsigned int perslab;   /* how many items(chunk) per slab */

    void *slots;           /* list of item ptrs */
    unsigned int sl_curr;   /* total free items in list */

    unsigned int slabs;     /* how many slabs(每个slabs是1M) were allocated for this class */

    void **slab_list;       /* array of slab pointers */
    unsigned int list_size; /* size of prev array */

    unsigned int killing;  /* index+1 of dying slab, or zero if none */
    size_t requested; /* The number of requested bytes */
} slabclass_t;

slab分很多等级,每个slab按块分配,slab内部有很多chunk。第二级slab的内部chunk大小是第一级的factor倍(1.25),第三级是第二级的1.25倍,依次递增。每个等级slab可以有多个,最多64级。
slab rebalance在各个slab级别之间根据闲忙进行slab调整。

/**
 * Structure for storing items within memcached.
 */
typedef struct _stritem {
    /* Protected by LRU locks */
    struct _stritem *next;
    struct _stritem *prev;
    /* Rest are protected by an item lock */
    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 (根据slabid区分在哪个队列063 hot 。。。)*/
    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;

每个item分配时根据大小,找到一个刚好大于等于item长度的最小的chunk(一个chunk只能存储一个item,如果不正好就浪费掉,为了方便内存回收),同时从对应的slab的freelist中摘除,分别根据slab大小划分的,hot warm cold noexp(分配时才放入)四个队列。

typedef struct {
    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 */
    uint32_t        remaining;  /* Max keys to crawl per slab per invocation */
} crawler;

crawler进行全局遍历,根据一个插入的item由list尾部向前,来进行过期数据删除。

淘汰策略有注意,如果一个数据没过期,一个过期了,没过期的经常访问,也会先淘汰没过期不经常访问的数据。

为进行快速检索,根据key 进行hash,放入hash表,用链(h_next指针)进行冲突处理。
assoc_maintenance
当hash表容量到 hash_items > (hashsize(hashpower) * 3) / 2 成都,进行hash翻倍膨胀。

线程模型

/* An item in the connection queue. */
typedef struct conn_queue_item CQ_ITEM;
struct conn_queue_item {
    int               sfd;
    enum conn_states  init_state;
    int               event_flags;
    int               read_buffer_size;
    enum network_transport     transport;
    CQ_ITEM          *next;
};

/* A connection queue. */
typedef struct conn_queue CQ;
struct conn_queue {
    CQ_ITEM *head;
    CQ_ITEM *tail;
    pthread_mutex_t lock;
};
typedef struct {
    pthread_t thread_id;        /* unique ID of this thread */
    struct event_base *base;    /* libevent handle this thread uses */
    struct event notify_event;  /* listen event for notify pipe */
    int notify_receive_fd;      /* receiving end of notify pipe */
    int notify_send_fd;         /* sending end of notify pipe */
    struct thread_stats stats;  /* Stats generated by this thread */
    struct conn_queue *new_conn_queue; /* queue of new connections to handle */
    cache_t *suffix_cache;      /* suffix cache( 此处cache仅仅是线程处理的专属小片内存,和真正的memcache中的cache一词毫无关系 */
} LIBEVENT_THREAD;

每个work线程一个独立libevent实例(因为线程不安全),同时还有一个与主进程通信管道,及一个连接队列。
主线程中,server_socket 调用 dispatch_conn_new 函数接收请求,将通过轮询向work线程通过pipe写入‘c‘,将连接放入线程专属队列。thread_libevent_process函数,激活线程,从队列中读取connect对象,并建立线程专属connection,然后进入此connection的event_handler循环.

你可能感兴趣的:(memcache 1.4.24源码分析)