memcached 内存初始化与key-value存储
/**首先介绍一下一个跟内存相关的非常重要的概念,内存块类型数据结构:*/
typedef struct {
unsigned int size; /* chunk的大小 sizes of items */
unsigned int perslab; /* 整个slab有chunk的数量 how many items per slab */ void *slots; /* item串起来的链表 list of item ptrs */
unsigned int sl_curr; /* 当前slabclass 有多少可用的 chunk total free items in list */ unsigned int slabs; /* 之前已经分配这种类型slab的数量 how many slabs were allocated for this class */ void **slab_list; /* 同类型的slab构成一个数组 array of slab pointers */
unsigned int list_size; /* 已经分配空间的slab_list的数组长度,实际使用数量为slabs 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; static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; /** 全局变量存储了系统所有的slab块类型*/ static size_t mem_limit = 0;
static size_t mem_malloced = 0;
static int power_largest; /** 记录有多少种slab块类型*/ static void *mem_base = NULL; /** 指向最初向系统申请的那大片空间,默认64M*/
static void *mem_current = NULL; /** 指向当前系统申请大片空间的首地址*/
static size_t mem_avail = 0; /** 统计系统申请的大片空间还有多少字节可以用*/ /*
* Figures out which slab class (chunk size) is required to store an item of
* a given size.
*
* Given object size, return id to use when allocating/freeing memory for object
* 0 means error: can't store such a large object
*/ /**
* 计算出哪一个slab class 适合存储一个size长度的item, 返回0 表示无法存储这个item
*/
unsigned int slabs_clsid(const size_t size) {
int res = POWER_SMALLEST; if (size == 0)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest) /* won't fit in the biggest slab */
return 0;
return res;
} /**
* Determines the chunk sizes and initializes the slab class descriptors
* accordingly.
*/
/**
* 确定chunk的大小并初始化slab的类型
* @limit 总的申请的内存块的大小 默认为 64M
* @相邻slab 之间的chunck大小进阶因子,比如第一种slab_class1的chunk为48 bytes,第二种slab_class2的chunk大小就为48*factor,第三种48*factor^2...
*/
/**
* slabs_init 主要逻辑,初始化内存块类型
* 首先理解一下memcached内存的划分逻辑,memcached在启动时会一次性向系统申请一大块内存,根据启动参数决定申请的大小,
* 默认为64M,之后便不再会向系统申请内存,而是在已申请的大块内存数组里面划分,少了系统调用,所以速度提高了。
* 获得一大块内存之后,memcached开始基于这块内存进行使用以及管理,首先是定义了内存块的概念,每一块默认大小为1M,
* 之后再对这一块内存进行划分为每一个chunk,每一个1M块里面的chunks都是一样大小的,不同的1M块包含的chunks的大小会有不同,
* 因此便有了块的类型,memcached 用一个数组来存储所有的类型slabclass, 默认类型不超过200种。那么memcached其实是用来存储
* key-value结构的,所以作为内存粒度最小的chunk自然就是用来存key-value了,将key-value进行包装一下用一个结构来表示,名称
* 为item,item就包含了一对key-value的信息,因此item所占的空间即长度就决定了需要用哪一种chunk来存储,不同的key-value所对应
* 的item长度自然是不一样,因而就需要不同大小的chunk来存储,也就是不同类型的slabclass.因而memcached引入了一个factor参数,
* 这个factor参数就是用来生成不同的slabclass,比如slabcalss[0]的chunk的大小为48bytes,则slabclass[1]的chunk大小则为48*factor bytes,
* factoe默认为1.25,所以相同大小的两块1M内存,slabclass[1]划分出来的chunk数量就比slabclass[0]要少,但是每个chunk比较大些,大点的
* item也能存得下,但其实一切对内存进行划分的行为都会造成一定量的内存浪费,但是若便于管理内存,利大于弊也是能接受一点浪费的。
*/
void slabs_init(const size_t limit, const double factor, const bool prealloc) {
int i = POWER_SMALLEST - 1;
unsigned int size = sizeof(item) + settings.chunk_size; /** setting.chunk_size为默认的大小*/ mem_limit = limit; if (prealloc) {
/* Allocate everything in a big chunk with malloc */
/** 一次性向系统申请一大块的内存*/
mem_base = malloc(mem_limit);
if (mem_base != NULL) {
mem_current = mem_base;
mem_avail = mem_limit;
} else {
fprintf(stderr, "Warning: Failed to allocate requested memory in"
" one large chunk.\nWill allocate in smaller chunks\n");
}
}
/** 初始化slabclass数组*/
memset(slabclass, 0, sizeof(slabclass)); /**
* 初始化内存块的类型,默认最多200种
* setting.items_size_max就表示了一块内存默认大小为1M
*/
while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
/* 确定内存对齐? Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); /** slabclass.size 表示适合存储的item的大小, 这里似乎忽略了chunk的概念,其实chunk就是用来存item的*/
slabclass[i].size = size;
/** slabclass.perslab 表示这块1M内存会有多少个item*/
slabclass[i].perslab = settings.item_size_max / slabclass[i].size; /** 下一种slabclass chunk的大小*/
size *= factor;
...
}
} /** 记录最终有多少种slabclass*/
power_largest = i; /** 最后一种item的大小就是块的大小了,也就是item可能就是1M大小,一个1M内存块就存了1个item*/
slabclass[power_largest].size = settings.item_size_max;
slabclass[power_largest].perslab = 1;
... /* for the test suite: faking of how much we've already malloc'd */
{
char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
if (t_initial_malloc) {
mem_malloced = (size_t)atol(t_initial_malloc);
}
} /** 是否预分配*/
if (prealloc) {
slabs_preallocate(power_largest);
}
} /**
* 系统启动时预分配内存块的主要逻辑
* @spec maxslabs 表示有多少种slabclass
*
* 预分配的效果就是为每一种slabclass初始化一个slab块,并将slab块划分为对应大小的chunks
* 预分配不是系统启动默认的,因为有可能分配的slab始终不会用到,浪费了存储空间,但是被用
* 到时由于已经分配好,直接可以使用,系统效率会高点,具体看存入的item实际大小的情况了
*/ static void slabs_preallocate (const unsigned int maxslabs) {
int i;
unsigned int prealloc = 0; /* pre-allocate a 1MB slab in every size class so people don't get
confused by non-intuitive "SERVER_ERROR out of memory"
messages. this is the most common question on the mailing
list. if you really don't want this, you can rebuild without
these three lines. */
/**
* 为每一种slabclass预分配1个1M的块,并将其划分为对应大小的的chunks
*/
for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
if (++prealloc > maxslabs)
return;
if (do_slabs_newslab(i) == 0) { /** 具体看do_slabs_newslab()注释*/
fprintf(stderr, "Error while preallocating slab memory!\n"
"If using -L or other prealloc options, max memory must be "
"at least %d megabytes.\n", power_largest);
exit(1);
}
} }
/** 登记到某一种类型的slab_list数组中*/
static int grow_slab_list (const unsigned int id) {
slabclass_t *p = &slabclass[id];
/** 如果数组没有空间再放多一个slab,就需要扩容数组*/
if (p->slabs == p->list_size) {
size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16;
void *new_list = realloc(p->slab_list, new_size * sizeof(void *));
if (new_list == 0) return 0;
p->list_size = new_size;
p->slab_list = new_list;
}
return 1;
} /**
* 函数名描述函数功能:将一个slab page 拆分(成小块)添加到可用链表-_-
* 将(申请到的)内存首地址作为参数,根据id找到所属的slabclass类型,
* 找到对应的chunk大小,将每一个chunk添加到slabclass的slot链表,具体由do_slabs_free()操作
*/
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {
slabclass_t *p = &slabclass[id];
int x;
for (x = 0; x < p->perslab; x++) {
do_slabs_free(ptr, 0, id); /** */
ptr += p->size; /** 指针移动size的长度*/
}
} /** 申请一个slab, id 表示所属的class*/
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; /** 先判断可申请长度是否满足,之后判断是否需要扩容slabclass中记录申请的slab的数组,最后申请内存*/
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);
split_slab_page_into_freelist(ptr, id); /** 将这一页的内存块瓜分成各个chunk小块,添加到slabclass可用slots链表*/ /**在slabclass中的数组记录多分配一个slab*/
p->slab_list[p->slabs++] = ptr;
mem_malloced += len;
MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id); return 1;
} /*@null@*/
/** 分配一个slab块 size 表示item的大小,id 表示slabclass id*/
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); /* 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 */
/* 先确保slabclass有咩有可用的slot, 再去申请新的slab, 申请也有可能失败*/
if (! (p->sl_curr != 0 || do_slabs_newslab(id) != 0)) {
/* We don't have more memory available */
ret = NULL;
} else if (p->sl_curr != 0) { /** 在slot有可用空间的时候申请失败则直接从slot那里取一个*/
/* 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;
}
if (ret) {
p->requested += size; /** 统计已经被使用的字节数量*/
MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
} else {
MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
}
return ret;
} /** 按照函数名理解,释放一个slab块
* ptr 表示首地址,size表示item已使用字节大小,id是所属的slabclass
*/
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]; it = (item *)ptr; /** 转化(初始化)成item类型指针*/
it->it_flags |= ITEM_SLABBED;
it->prev = 0;
it->next = p->slots; /** 串在 slabcalss的slots链表头部*/
if (it->next) it->next->prev = it; /*下一个节点不为空时,将下一个节点的prev指向it*/
p->slots = it; /** 更新slots */ p->sl_curr++; /**更新当前可用的chunk数量, slot代表了链表的名称, sl_curr表示可用的数量*/
p->requested -= size; /** 更新已经被请求使用了的字节数,多了一个相当于requested 减掉一个*/
return;
} /** 从已经申请的内存块中申请slab内存块*/
static void *memory_allocate(size_t size) {
void *ret;
if (mem_base == NULL) { /**全局变量指向系统申请时的内存块*/
/* We are not using a preallocated large memory chunk */
ret = malloc(size); /** 表示尚未向系统申请过,这里就直接调用系统调用申请*/
} else {
ret = mem_current; /** mem_current同样是一个全局变量,指向mem_base申请之后剩余的可用内存*/ if (size > mem_avail) { /** 判断申请是不是过大*/
return NULL;
}
/* mem_current pointer _must_ be aligned!!! */
if (size % CHUNK_ALIGN_BYTES) { /** 官方注释这里必须内存对齐*/
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
}
mem_current = ((char*)mem_current) + size; /** mem_current向前移动*/
if (size < mem_avail) {
mem_avail -= size; /* 更新全局统计量*/
} else {
mem_avail = 0;
}
}
return ret;
} /*分配slab块的对外接口,主要是加了锁,基本上do_xxx()函数都是不加锁的*/
void *slabs_alloc(size_t size, unsigned int id) {
void *ret;
pthread_mutex_lock(&slabs_lock);
ret = do_slabs_alloc(size, id);
pthread_mutex_unlock(&slabs_lock);
return ret;
}
... /**更新已经slabclass使用的字节数*/
void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)
{
pthread_mutex_lock(&slabs_lock);
slabclass_t *p;
if (id < POWER_SMALLEST || id > power_largest) {
fprintf(stderr, "Internal error! Invalid slab class\n");
abort();
}
p = &slabclass[id];
p->requested = p->requested - old + ntotal;
pthread_mutex_unlock(&slabs_lock);
}
以上注释仅代表个人见解,若有误导,请见谅!
memcached 内存初始化与key-value存储的更多相关文章
- memcached内存管理及key value长度限制
1)什么是内存碎片?内存是大小有限的资源.例如把内存比作一张小床,来了一个小伙伴,可以睡下,再来一个小伙伴也能睡下.现在两个人了,他们点了差不多的大小的位置(资源),位置还有剩下.然后再来一个小胖子, ...
- memcached内存管理机制分析
memached是高性能分布式内存对象系统,通过在内存中存储数据对象来减少对磁盘的数据读取次数,提高服务速度. 从业务需求出发.我们通过一条命令(如set)将一条键值对(key,value)插入mem ...
- Memcached内存管理模型分析
Memcached 是一个高性能的分布式内存对象缓存系统,它通过在内存中缓存数据和对象来减少读取数据库的次数,从而减轻RDBMS的负担,提高服务的速度.提升可扩展性.本文将基于memcached1.4 ...
- memcached内存管理机制[未整理]
memcached默认采用的是Slab Allocator的机制分配管理内存的,在此之前,内存的分配是通过对所有的记录简单地进行malloc和free来进行的,但这种方式容易造成很多内存碎片,加重操作 ...
- Memcached内存缓存技术
Memcached是什么,有什么作用? Memcached是一个开源的.高性能的内存缓存软件,从名称上看Mem就是内存的意思,而Cache就是缓存的意思. Memcached通过在事先规划好的内存空间 ...
- 分布式缓存系统 Memcached 内存管理机制
在前面slab数据存储部分分析了Memecached中记录数据的具体存储机制,从中可以看到所采用的内存管理机制——slab内存管理,这也正是linux所采用的内存高效管理机制,对于Memchached ...
- Memcached内存分配优化及使用问题
前几天做了个Memcached的思考,并测试了一些数据,是关于如何提高Memcached内存使用率的问题.在启动memcached的时候可以加-f参数和-n参数.-f指定各slab里面chunk大小的 ...
- Smart210学习记录-------内存初始化
买了Smart210的板子,开始学习中,,,,, 今天看了重定位DRAM ,然而内存需要初始化,早上信心满满的我到现在崩溃的我....也不知遭受了什么样的蹂躏 ,,还是记下一点学到的知识吧.. 数据手 ...
- ok6410内存初始化
•DRAM:它的基本原件是小电容,电容可以在两个极板上保留电荷,但是需要定期的充电(刷新),否则数据会丢失.缺点:由于要定期刷新存储介质,存取速度较慢. •SRAM:它是一种具有静止存取功能的内存,不 ...
随机推荐
- 干货:Java并发编程系列之synchronized(一)
1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...
- OpenCV实现SfM(三):多目三维重建
http://lib.csdn.net/article/opencv/24548 注意:本文中的代码必须使用OpenCV3.0或以上版本进行编译,因为很多函数是3.0以后才加入的. 目录: 问题简化 ...
- 分享三个USB抓包软件---Bus Hound,USBlyzer 和-USBTrace(转)
源:分享三个USB抓包软件---Bus Hound,USBlyzer 和-USBTrace Bus Hound官方下载地址:http://perisoft.net/bushound/Bus Hound ...
- ES6学习--箭头函数
1. 箭头函数基本形式 let func = (num) => num; let func = () => num; let sum = (num1,num2) => num1 + ...
- https的设置
现有如下的web架构(简化之后的),需要把原来的http访问修改到https访问! haproxy的认证有两种方式: 第一种:haproxy提供ssl证书,后面的nginx访问使用正常的http. 第 ...
- rabbitmq架构简介(包括集群)
总的来说,rabbitmq使用erlang语言编写,其架构类似于servlet容器运行servlet应用,底层是erlang VM.然后是erlang节点,上面是应用.如下所示: 每个MQ中运行的应用 ...
- Android JSON 解析关键代码
Android Json 解析其实还是蛮重要的知识点,为什么这么说呢,因为安卓通信大部分的协议都是使用 json 的方式传输,我知道以前大部分是使用的 xml ,但是时代在发展社会在进步,json 成 ...
- 02: SocketServer服务
网络编程其他篇 目录: 1.1 SocketServer四种基本流及 异步处理理论部分 1.2 创建socketserver实现: 多客户端并发 1.3 SocketServer实现多并发FTP 部分 ...
- centos 安装最新稳定版本docker
直接yum安装的docker版本是 : docker --versionDocker version 1.12.6, build 85d7426/1.12.6 一些新特性需要安装最新的稳定版本 国内可 ...
- UVa 11082 Matrix Decompressing - 网络流
开始眨眼一看怎么也不像是网络流的一道题,再怎么看也觉得像是搜索.不过虽然这道题数据范围很小,但也不至于搜索也是可以随随便便就可以过的.(不过这道题应该是special judge,因为一题可以多解而且 ...