`
kenby
  • 浏览: 725393 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Memcached源码分析之内存管理篇

 
阅读更多

使用命令 set(key, value) 向 memcached 插入一条数据, memcached 内部是如何组织数据呢

 

一 把数据组装成 item

memcached 接受到客户端的数据后, 把数据组装成 item, item 的格式如下:



 
   图1 struct item 的结构

 

源码中这样定义 struct item:

 

/**
 * Structure for storing items within memcached.
 */
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;

 

从源码可以得出 item 的结构分两部分, 第一部分定义 item 结构的属性, 包括连接其它 item 的指针 (next, prev), 

还有最近访问时间(time), 过期的时间(exptime), 以及数据部分的大小, 标志位, key的长度, 引用次数, 以及 item 是

从哪个 slabclass 分配而来. 

 

 

item 结构体的定义使用了一个常用的技巧: 定义空数组 data, 用来指向 item 数据部分的首地址, 使用空数组的

好处是 data 指针本身不占用任何存储空间, 为 item 分配存储空间后, data 自然而然就指向数据部分的首地址. 

 

第二部分是 item 的数据, 由 CAS, key, suffix, value 组成.

 

二 为 item 分配存储空间

把数据组装成 item 之前, 必须为 item 分配存储空间, memcached 不是直接从操作系统分配内存的, memcached

内部使用了类似内存池的东西, 即slab机制, 来管理内存. 内存的分配和回收都交给 slab 子系统实现. 所以我们先理解

slab, 再回过头来看如何为 item 分配存储空间.

 

三 使用 slab 管理内存

memcached 中, 内存的分配和回收, 都是通过 slab 实现的, slab机制相当于内存池机制, 实现从操作系统分配一大块

内存, 然后 memcached 自己管理这块内存, 负责分配与回收. 接下来我们详细剖析 slab 机制.

 

3.1 关于 slabclass

像一般的内存池一样,  从操作系统分配到一大块内存后, 为了方便管理, 把这大块内存划分为各种大小的 chunk,

chunk的大小按照一定比例逐渐递增, 如下图所示:


  图2: 各个 slabclass 的 chunk size 按比例递增

 

从 slab 分配内存的时候, 根据请求内存块的大小, 找到大小最合适的 chunk 所在的 slabclass, 然后从这个

slabclass 找空闲的 chunk 分配出去. 所谓最合适就是指 chunk 的大小能够满足要求, 而且碎片最小.

如下图所示:

图3 寻找最合适的 slabclass

 

这种分配方式的缺点是存在内存碎片, 例如, 将 100字节的 item 存储到一个 128 字节的 chunk, 就有 28 字节

的内存浪费, 如下图所示:

图5 内存碎片

 

3.2 slabclass 的内部实现

slabclass 是由 chunk size 确定的, 同一个 slabclass 内的 chunk 大小都一样,  每一个 slabclass 要负责管理

一些内存, 初始时, 系统为每个 slabclass 分配一个 slab, 一个 slab 就是一个内存块,  其大小等于 1M.  然后每个

slabclass 再把 slab 切分成一个个 chunk, 算一下, 一个 slab 可以切分得到 1M/chunk_size 个chunk. 

 

先来看一下源码中 slabclass 的定义:

 

 

typedef struct {
    unsigned int size;      /* sizes of items */
    unsigned int perslab;   /* how many items per slab */

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

    void *end_page_ptr;         /* pointer to next free item at end of page, or 0 */
    unsigned int end_page_free; /* number of items remaining at end of last alloced page */

    unsigned int slabs;     /* how many slabs 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;

 

 

slabclass 的结构图如下所示:

图6 slabclass 结构图

 结合 slabclass 的结构图, 我们说明一下 slabclass 结构体的各个属性:

(1) size 和 perslab

size 定义该 slabclass 的 chunk 大小, perslab 表示每个 slab 可以切分成多少个 chunk, 如果一个 slab 等于

1M, 那么就有 perslab = 1M / size

 

(2) slots 和 sl_curr

slots 是回收的 item 链表, 从某个 slabclass 分配出去一个 item, 当 item 回收的时候,

不是把这 item 使用的内存交还给 slab, 而是让这个 item 挂在 slots 链表的尾部. sl_curr 表示当前链表中

有多少个回收而来的空闲 item.

 

(3) slab_list 和 list_size

前面说过, 初始时, memcached 为每个 slabclass 分配一个 slab, 当这个 slab 内存块使用完后, memcached 

就分配一个新的 slab, 所以 slabclass 可以拥有多个 slab, 这些 slab 就是通过 slab_list 数组来管理的, list_size

表示当前 slabclass 有多少个 slab.

 

(4) end_page_ptr 和 end_page_free

在 subclass 内, 只有最后一个 slab 存在空闲的内存, 其它 slab 的 chunk 都分配出去了, end_page_ptr

指向最后一个 slab 中的空闲内存块, end_page_free 表示最后一个 slab 中还剩下多少个空闲 chunk.  图6

中绿色部分的 chunk 表示空闲 chunk

 

(5) static item *heads[LARGEST_ID];

      static item *tails[LARGEST_ID];

 当 memcached 没有足够的内存使用时, 必须选择性地回收一些 item, 回收采用 LRU 算法, 这就需要维护

一个按照最近访问时间排序的 LRU 队列. 在 memcached 中,

每个 slabclass 维护一个链表, 比如 slabclass[i] 的链表头指针为 heads[i], 尾指针为 tails[i],

已分配出去的 item 都存储在链表中. 而且链表中 item 按照最近访问时间排序, 这样一些链表相当于

LRU 队列. 

 

 

四 源码分析

4.1 do_slabs_newslab

前面说过每个 slabclass 都拥有一些 slab, 当所有 slab 都用完时, memcached 会给它分配一个新的 slab, 

do_slabs_newslab 就是做这个工作的.

 

 

static int do_slabs_newslab(const unsigned int id) {
    slabclass_t *p = &slabclass[id];
    int len = settings.slab_reassign ? settings.item_size_max
        : p->size * p->perslab;
    char *ptr;

    if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
        (grow_slab_list(id) == 0) ||
        ((ptr = memory_allocate((size_t)len)) == 0)) {

        MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
        return 0;
    }

    memset(ptr, 0, (size_t)len);
    p->end_page_ptr = ptr;
    p->end_page_free = p->perslab;

    p->slab_list[p->slabs++] = ptr;
    mem_malloced += len;
    MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);

    return 1;
}

 

分配一个新的 slab, 必须把该 slab 的首地址安插在 slab_list 数组中, 所以先调用 grow_slab_list 来确保 slab_list 

数组有足够的容量, 如果容量不足, grow_slab_list 会对 slab_list 扩容.

 

然后调用 memory_allocate 分配 1M 的内存空间, memory_allocate 从预先分配的内存取下 1M 大小的

空闲内存块,作为新的 slab. 

 

最后调整 end_page_ptr, 新分配的 slab 全部都是空闲内存块, 所以 end_page_ptr 指向新 slab 的首地址.

这个新 slab 的首地址也被安插在 slab_list 数组中.

 

4.2 do_slabs_alloc

这个函数从指定的 slabclass, 即 slabclass[id], 分配大小为 size 的内存块供申请者使用.

 分配的原则是, 优先从 slots 指向的空闲链表中分配, 空闲链表没有, 才从 slab 中分配一个空闲的 chunk.

 

 

static void *do_slabs_alloc(const size_t size, unsigned int id) {
    slabclass_t *p;
    void *ret = NULL;
    item *it = NULL;

    if (id < POWER_SMALLEST || id > power_largest) {
        MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);
        return NULL;
    }

    p = &slabclass[id];
    assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);

	/* 如果不使用 slab 机制, 则直接从操作系统分配 */
#ifdef USE_SYSTEM_MALLOC
    if (mem_limit && mem_malloced + size > mem_limit) {
        MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
        return 0;
    }
    mem_malloced += size;
    ret = malloc(size);
    MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret);
    return ret;
#endif

    /* fail unless we have space at the end of a recently allocated page,
       we have something on our freelist, or we could allocate a new page */
	/* 确保最后一个slab 有空闲的chunk */
    if (! (p->end_page_ptr != 0 || p->sl_curr != 0 ||
           do_slabs_newslab(id) != 0)) {
        /* We don't have more memory available */
        ret = NULL;
    } else if (p->sl_curr != 0) {
		/* 从空闲list分配一个item */
        /* return off our freelist */
        it = (item *)p->slots;
        p->slots = it->next;
        if (it->next) it->next->prev = 0;
        p->sl_curr--;
        ret = (void *)it;
    } else {
		/* 从最后一个slab中分配一个空闲chunk */
        /* if we recently allocated a whole page, return from that */
        assert(p->end_page_ptr != NULL);
        ret = p->end_page_ptr;
        if (--p->end_page_free != 0) {
            p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size;
        } else {
            p->end_page_ptr = 0;
        }
    }

    if (ret) {
        p->requested += size;
        MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
    } else {
        MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
    }

    return ret;
}
 

4.3 do_slabs_free

把 ptr 指向的 item 归还给 slabclass[id]

操作很简单, 把 ptr 指向的 item 挂在 slots 空闲链表的最前面

 

 

static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
    slabclass_t *p;
    item *it;

    assert(((item *)ptr)->slabs_clsid == 0);
    assert(id >= POWER_SMALLEST && id <= power_largest);
    if (id < POWER_SMALLEST || id > power_largest)
        return;

    MEMCACHED_SLABS_FREE(size, id, ptr);
    p = &slabclass[id];

#ifdef USE_SYSTEM_MALLOC
    mem_malloced -= size;
    free(ptr);
    return;
#endif

	/* 把 item 归还给slots指向的空闲链表, 插在链表的最前面 */
    it = (item *)ptr;
    it->it_flags |= ITEM_SLABBED;
    it->prev = 0;
    it->next = p->slots;
    if (it->next) it->next->prev = it;
    p->slots = it;

    p->sl_curr++;
    p->requested -= size;
    return;
}
 

4.4 do_item_alloc

从 slab 系统分配一个空闲 item

 

item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
    uint8_t nsuffix;
    item *it = NULL;
    char suffix[40];
    size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
    if (settings.use_cas) {
        ntotal += sizeof(uint64_t);
    }

    unsigned int id = slabs_clsid(ntotal);
    if (id == 0)
        return 0;

    mutex_lock(&cache_lock);
    /* do a quick check if we have any expired items in the tail.. */
    item *search;
    rel_time_t oldest_live = settings.oldest_live;

    search = tails[id];
    if (search != NULL && (refcount_incr(&search->refcount) == 2)) {
		/* 先检查 LRU 队列最后一个 item 是否超时, 超时的话就把这个 item 分配给用户 */
        if ((search->exptime != 0 && search->exptime < current_time)
            || (search->time <= oldest_live && oldest_live <= current_time)) {  // dead by flush
            STATS_LOCK();
            stats.reclaimed++;
            STATS_UNLOCK();
            itemstats[id].reclaimed++;
            if ((search->it_flags & ITEM_FETCHED) == 0) {
                STATS_LOCK();
                stats.expired_unfetched++;
                STATS_UNLOCK();
                itemstats[id].expired_unfetched++;
            }
            it = search;
            slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);
			/* 把这个 item 从 LRU 队列和哈希表中移除 */
            do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
            /* Initialize the item block: */
            it->slabs_clsid = 0;
		/* 没有超时的 item, 那就尝试从 slabclass 分配, 运气不好的话, 分配失败,
		   那就把 LRU 队列最后一个 item 剔除, 然后分配给用户 */
        } else if ((it = slabs_alloc(ntotal, id)) == NULL) {
            if (settings.evict_to_free == 0) {
                itemstats[id].outofmemory++;
                pthread_mutex_unlock(&cache_lock);
                return NULL;
            }
            itemstats[id].evicted++;
            itemstats[id].evicted_time = current_time - search->time;
            if (search->exptime != 0)
                itemstats[id].evicted_nonzero++;
            if ((search->it_flags & ITEM_FETCHED) == 0) {
                STATS_LOCK();
                stats.evicted_unfetched++;
                STATS_UNLOCK();
                itemstats[id].evicted_unfetched++;
            }
            STATS_LOCK();
            stats.evictions++;
            STATS_UNLOCK();
            it = search;
            slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);
			/* 把这个 item 从 LRU 队列和哈希表中移除 */
            do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
            /* Initialize the item block: */
            it->slabs_clsid = 0;
        } else {
            refcount_decr(&search->refcount);
        }
	/* LRU 队列是空的, 或者锁住了, 那就只能从 slabclass 分配内存 */
    } else {
        /* If the LRU is empty or locked, attempt to allocate memory */
        it = slabs_alloc(ntotal, id);
        if (search != NULL)
            refcount_decr(&search->refcount);
    }

    if (it == NULL) {
        itemstats[id].outofmemory++;
        /* Last ditch effort. There was a very rare bug which caused
         * refcount leaks. We leave this just in case they ever happen again.
         * We can reasonably assume no item can stay locked for more than
         * three hours, so if we find one in the tail which is that old,
         * free it anyway.
         */
        if (search != NULL &&
            search->refcount != 2 &&
            search->time + TAIL_REPAIR_TIME < current_time) {
            itemstats[id].tailrepairs++;
            search->refcount = 1;
            do_item_unlink_nolock(search, hash(ITEM_key(search), search->nkey, 0));
        }
        pthread_mutex_unlock(&cache_lock);
        return NULL;
    }

    assert(it->slabs_clsid == 0);
    assert(it != heads[id]);

	/* 顺便对 item 做一些初始化 */
    /* Item initialization can happen outside of the lock; the item's already
     * been removed from the slab LRU.
     */
    it->refcount = 1;     /* the caller will have a reference */
    pthread_mutex_unlock(&cache_lock);
    it->next = it->prev = it->h_next = 0;
    it->slabs_clsid = id;

    DEBUG_REFCNT(it, '*');
    it->it_flags = settings.use_cas ? ITEM_CAS : 0;
    it->nkey = nkey;
    it->nbytes = nbytes;
    memcpy(ITEM_key(it), key, nkey);
    it->exptime = exptime;
    memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
    it->nsuffix = nsuffix;
    return it;
}
 

 

4.5 do_item_link

形成了一个完成的 item 后, 就要把它放入两个数据结构中, 一是 memcached 的哈希表,

memcached 运行过程中只有一个哈希表, 二是 item 所在的 slabclass 的 LRU 队列.

如 图6 所示, 每个 slabclass 都有一个 LRU 队列

 

int do_item_link(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    mutex_lock(&cache_lock);
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;

    STATS_LOCK();
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();

    /* Allocate a new CAS ID on link. */
    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
	/* 把 item 放入哈希表 */
    assoc_insert(it, hv);
	/* 把 item 放入 LRU 队列*/
    item_link_q(it);
    refcount_incr(&it->refcount);
    pthread_mutex_unlock(&cache_lock);

    return 1;
}
 

4.6 do_item_unlink

do_item_unlink 与 do_item_unlink 做的工作相反, 把 item 从哈希表和 LRU 队列中删除,

而且还释放掉 item 所占的内存 (其实只是把 item 放到空闲链表中).

 

void do_item_unlink(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
		/* 从哈希表中删除 item */
        assoc_delete(ITEM_key(it), it->nkey, hv);
		/* 从 LRU 队列中删除 item */
        item_unlink_q(it);
		/* 释放 item 所占的内存 */
        do_item_remove(it);
    }
    pthread_mutex_unlock(&cache_lock);
}
 

4.7 item_get

根据 key 找对应的 item, 为了加快查找速度, memcached 使用一个哈希表对 key 和 item 所在的内存

地址做映射. item_get 直接从哈希表中查找就可以了, 当然找到了还要检查 item 是否超时. 超时了的 item

将从哈希表和 LRU 队列中删除掉

 

/** wrapper around assoc_find which does the lazy expiration logic */
item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {
    mutex_lock(&cache_lock);
	/* 从哈希表中找 item */
    item *it = assoc_find(key, nkey, hv);
    if (it != NULL) {
        refcount_incr(&it->refcount);
        /* Optimization for slab reassignment. prevents popular items from
         * jamming in busy wait. Can only do this here to satisfy lock order
         * of item_lock, cache_lock, slabs_lock. */
        if (slab_rebalance_signal &&
            ((void *)it >= slab_rebal.slab_start && (void *)it < slab_rebal.slab_end)) {
            do_item_unlink_nolock(it, hv);
            do_item_remove(it);
            it = NULL;
        }
    }
    pthread_mutex_unlock(&cache_lock);
    int was_found = 0;

    if (settings.verbose > 2) {
        if (it == NULL) {
            fprintf(stderr, "> NOT FOUND %s", key);
        } else {
            fprintf(stderr, "> FOUND KEY %s", ITEM_key(it));
            was_found++;
        }
    }

	/* 找到了, 然后检查是否超时 */
    if (it != NULL) {
        if (settings.oldest_live != 0 && settings.oldest_live <= current_time &&
            it->time <= settings.oldest_live) {
            do_item_unlink(it, hv);
            do_item_remove(it);
            it = NULL;
            if (was_found) {
                fprintf(stderr, " -nuked by flush");
            }
        } else if (it->exptime != 0 && it->exptime <= current_time) {
            do_item_unlink(it, hv);
            do_item_remove(it);
            it = NULL;
            if (was_found) {
                fprintf(stderr, " -nuked by expire");
            }
        } else {
            it->it_flags |= ITEM_FETCHED;
            DEBUG_REFCNT(it, '+');
        }
    }

    if (settings.verbose > 2)
        fprintf(stderr, "\n");

    return it;
}
  • 大小: 18.9 KB
  • 大小: 15.4 KB
  • 大小: 33.9 KB
  • 大小: 15.4 KB
  • 大小: 9.2 KB
  • 大小: 46.3 KB
分享到:
评论
2 楼 kyogt 2013-06-17  
膜拜中~以为在理解struct item 的 时候 无法理解48B的由来 看了这篇博客 理解加深了
1 楼 mfkdzhou 2012-12-07  
学习了。

相关推荐

    找到一个memcached分析文档

    描述中提到的“博文链接:https://1998a.iteye.com/blog/257813”是一个ITeye博客上的文章链接,虽然具体内容没有提供,但我们可以假设这篇文章可能是作者分享的关于Memcached的使用经验、优化技巧或者源码分析。...

    memcached for win32

    这个描述暗示了可能包含一篇博客文章,指导用户如何在 Windows 环境中配置和启动 Memcached 服务,以便它可以作为后台进程运行,并且可以被应用程序用来存储和检索数据。 **标签解析:** - "源码":这表明可能包含...

    memcached完全剖析ehcache memcached redis 缓存技术总结

    8. **源码分析** - 针对开发人员,可能有对这些缓存系统关键组件的源码解析,帮助理解其内部运作机制。 9. **安全性与管理** - 讨论如何确保缓存的安全性,以及监控和管理缓存的方法。 10. **最佳实践** - 提供...

    Memcached资料

    2. **内存管理**:采用slab分配器策略,将内存划分为不同的slabs,每个slab用于存储特定大小的数据,避免了小块内存的碎片问题。 3. **命令解析**:Memcached支持一系列命令,如`get`、`set`、`delete`等,源码中...

    memcached相关知识

    5. **源码分析**:了解memcached的源码有助于深入理解其内部机制,如内存管理、网络通信、数据序列化等,有助于进行性能优化和二次开发。 6. **性能优化**:可以通过调整内存大小、设置合理的超时时间、优化缓存...

    memcached全面剖析资料

    这篇资料可能包含memcached的基本概念、工作原理、安装配置、优化技巧以及常见问题的解决方法。 描述中提到的“博文链接:https://sizhefang.iteye.com/blog/231627”提供了一个博客文章的地址,暗示了该资源可能...

    redis 1.0源代码注释.zip

    这也是Redis之所以流行起来的一个重要原因,当然Redis能够流行起来的原因,显然不只这个一个,如支持高并发的读写、数据的持久化、高效的内存管理及淘汰机制……3.Redis社区集内部实现原理分析(二)本篇紧随上篇src...

    CentOS下Nginx0.8.52-PHP5.3.3-MySQL5.5.6-Memcached1.4.5安装配置优化

    Nginx的安装通常可以采用从源码编译安装或者直接使用CentOS提供的包管理工具yum安装预编译的rpm包。 1. 使用yum安装Nginx ```bash yum install epel-release yum install nginx ``` 安装完成后,启动Nginx服务: ``...

    简易内存数据库(一)总述

    此外,对于源码和工具部分,文章可能还涵盖了开源内存数据库如Redis、Memcached等的简要分析,或者介绍了如何构建一个简单的内存数据库系统,包括数据结构的选择(如哈希表、B树等)和API设计等。 在本科班课程项目...

    动网bbs---源码

    5. 缓存技术:通过缓存机制提高页面加载速度,减轻服务器压力,如使用文件缓存或内存缓存(如memcached)。 四、安全特性 1. SQL注入防护:源码中包含了防止SQL注入的措施,对用户输入进行过滤和转义,防止恶意攻击...

    http://www.terracotta.org/

    4. **源码分析**:学习如何阅读和理解开源项目的源代码,这包括代码结构、模块划分以及关键算法的理解。 5. **集成和扩展**:讨论如何将Ehcache或Memcached集成到现有的应用程序中,以及如何根据需求对其进行扩展和...

    oracle coherence

    这篇博客文章可能没有提供直接的描述,但根据标签 "源码" 和 "工具",我们可以推测这篇博客可能讨论了 Oracle Coherence 的源代码分析或与开发和调试相关的工具。`magro-memcached-session-manager-99be57d` 这个...

    memadmin 安装,memcache监控工具,详细文档

    `memadmin` 是一款专为 `memcached` 设计的管理工具,它可以帮助我们监控、管理和调整 `memcached` 服务器的状态,确保系统的高效运行。本篇文章将详细介绍 `memadmin` 的安装过程,以及如何利用它进行 `memcache` ...

    memcacheq-0.2.0.tar.gz

    作为一个轻量级的内存队列,Memcacheq旨在解决Memcached缺乏持久化和队列管理的问题,适用于需要高效处理大量数据流的Web应用。 二、Memcacheq的核心功能 1. 队列操作:Memcacheq支持入队(enqueue)和出队...

    memcache服务器监控

    1. **源码分析**:可能深入到Memcache的源代码级别,理解其内部工作原理,帮助优化配置或自定义监控脚本。 2. **工具推荐**:介绍一些用于监控Memcache的开源工具,如`memcached-top`,一个实时显示Memcache统计信息...

    Session

    标签"源码"提示我们,这篇博客可能还涉及到了Session实现的内部代码或开源库的源码分析,这可能包括对Servlet容器(如Tomcat)中Session管理的源码研究,或者是第三方库(如Spring Session)的实现原理。 至于"工具...

    Redis_Deep_Read:redis 1.0原始注解

    Redis中支持的数据结构比Memcached要多的多啦,如基本的字符串,哈希表,列表,集合,可排序集,在这些基本数据结构上也提供了针对该数据结构的各种操作,这也是Redis之所以流行起来的一个重要原因,当然Redis能够...

    Java学习的中间件

    在IT行业中,Java作为一种广泛应用的编程语言,其学习过程中涉及的中间件技术是开发者们必须掌握的关键领域。...在实际工作中,不断学习和实践,结合源码分析和工具应用,将使你在这个领域更加得心应手。

    tomcat并发资料

    综上,掌握Tomcat并发处理的知识,结合源码分析和工具应用,能有效地提升服务器的并发处理能力,应对高流量网站的需求。这份"Tomcat并发资料.txt"文件可能包含上述部分或全部知识点的详细解释,是学习和优化Tomcat...

    缓存技术浅谈

    7. **源码分析**:文章可能深入到具体缓存库的源码层面,如分析Guava Cache的工作原理,或者Redis的数据结构和操作命令实现。 8. **工具应用**:可能介绍了一些用于缓存管理、监控的工具,例如JMX监控缓存命中率,...

Global site tag (gtag.js) - Google Analytics