存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在 memcached中,被多个应用共享。
101. 需要注意的是,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会丢失,所以 memcached不能用来持久保存数据。很多人的错误理解,memcached的性能非常好,好到了内存和硬盘的对比程度,其实memcached使用 内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接 的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了,memcached进程本身并不占用多少CPU资源的情况。
102. ◎Memcached的工作方式
103. 以下的部分中,读者最好能准备一份memcached的源代码。 104. Memcached是传统的网络服务程序,如果启动的时候使用了-d参数,它会以守护进程的方式执行。创建守护进程由daemon.c完成,这个程序只有一个daemon函数,这个函数很简单(如无特殊说明,代码以1.2.1为准):
105. #include
109. int daemon(nochdir, noclose) 110. { int nochdir, noclose; 111.
112. int fd; 113.
114. switch (fork()) { 115. case -1:
116. return (-1); 117. case 0: 118. break; 119. default:
120. _exit(0); 121. } 122.
123. if (setsid() == -1) 124. return (-1); 125.
126. if (!nochdir)
127. (void)chdir(\128.
129. if (!noclose && (fd = open(\{
130. (void)dup2(fd, STDIN_FILENO);
131. (void)dup2(fd, STDOUT_FILENO); 132. (void)dup2(fd, STDERR_FILENO); 133. if (fd > STDERR_FILENO) 134. (void)close(fd); 135. }
136. return (0); 137. }
138. 这个函数 fork 了整个进程之后,父进程就退出,接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备, daemon 就建立成功了。 139. Memcached 本身的启动过程,在 memcached.c 的 main 函数中顺序如下:
140. 1 、调用 settings_init() 设定初始化参数 2 、从启动命令中读取参数来设置 setting 值 3 、设定 LIMIT 参数
4 、开始网络 socket 监听(如果非 socketpath 存在)( 1.2 之后支持 UDP 方式)
5 、检查用户身份( Memcached 不允许 root 身份启动)
6 、如果有 socketpath 存在,开启 UNIX 本地连接(Sock 管道) 7 、如果以 -d 方式启动,创建守护进程(如上调用 daemon 函数) 8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab 9 、如设置中 managed 生效,创建 bucket 数组 10 、检查是否需要锁定内存页 11 、初始化信号、连接、删除队列 12 、如果 daemon 方式,处理进程 ID
13 、event 开始,启动过程结束, main 函数进入循环。
141. 在 daemon 方式中,因为 stderr 已经被定向到黑洞,所以不会反馈执行中的可见错误信息。
142. memcached.c 的主循环函数是 drive_machine ,传入参数是指向当前的连接的结构指针,根据 state 成员的状态来决定动作。
143. Memcached 使用一套自定义的协议完成数据交换,它的 protocol 文档可以参考:
http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
144. 在API中,换行符号统一为\\r\\n 145. ◎Memcached的内存管理方式
146. Memcached有一个很有特色的内存管理方式,为了提高效率,它使用预申请和分组的方式管理内存空间,而并不是每次需要写入数据的时候去malloc,删除数据的时候free一个指针。Memcached使用slab->chunk的组织方式管理内存。
147. 1.1和1.2的slabs.c中的slab空间划分算法有一些不同,后面会分别介绍。
148. Slab可以理解为一个内存块,一个slab是memcached一次申请内存的最小单位,在memcached中,一个slab的大小默 认为1048576字节(1MB),所以memcached都是整MB的使用内存。每一个slab被划分为
若干个chunk,每个chunk里保存一个 item,每个item同时包含了item结构体、key和value(注意在memcached中的value是只有字符串的)。slab按照自己的 id分别组成链表,这些链表又按id挂在一个slabclass数组上,整个结构看起来有点像二维数组。slabclass的长度在1.1中是21,在 1.2中是200。
149. slab有一个初始chunk大小,1.1中是1字节,1.2中是80字节,1.2中有一个factor值,默认为1.25
150. 在1.1中,chunk大小表示为初始大小*2^n,n为classid,即:id为0的slab,每chunk大小1字节,id为1的 slab,每chunk大小2字节,id为2的slab,每chunk大小4字节??id为20的slab,每chunk大小为1MB,就是说id为20 的slab里只有一个chunk: 151. void slabs_init(size_t limit) { 152. int i;
153. int size=1; 154.
155. mem_limit = limit;
156. for(i=0; i<=POWER_LARGEST; i++, size*=2) { 157. slabclass[i].size = size;
158. slabclass[i].perslab = POWER_BLOCK / size; 159. slabclass[i].slots = 0;
160. slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
161. slabclass[i].end_page_ptr = 0; 162. slabclass[i].end_page_free = 0; 163. slabclass[i].slab_list = 0; 164. slabclass[i].list_size = 0; 165. slabclass[i].killing = 0; 166. } 167.
168. /* for the test suite: faking of how much we've already malloc'd */ 169. {
170. char *t_initial_malloc = getenv(\171. if (t_initial_malloc) { 172. mem_malloced =
atol(getenv(\173. } 174. } 175.
176. /* pre-allocate slabs by default, unless the environment variable
177. for testing is set to something non-zero */ 178. {
179. char *pre_alloc = getenv(\180. if (!pre_alloc || atoi(pre_alloc)) {
181. slabs_preallocate(limit / POWER_BLOCK); 182. } 183. } 184. }
185. 在1.2中,chunk大小表示为初始大小*f^n,f为factor,在
memcached.c中定义,n为classid,同时,201个头不 是全部都要初始化的,因为factor可变,初始化只循环到计算出的大小达到slab大小的一半为止,而且它是从id1开始的,即:id为1的slab, 每chunk大小80字节,id为2的slab,每chunk大小80*f,id为3的slab,每chunk大小80*f^2,初始化大小有一个修正值 CHUNK_ALIGN_BYTES,用来保证n-byte排列 (保证结果是CHUNK_ALIGN_BYTES的整倍数)。这样,在标准情况下,memcached1.2会初始化到id40,这个slab中每个 chunk大小为504692,每个slab中有两个chunk。最后,slab_init函数会在最后补足一个id41,它是整块的,也就是这个 slab中只有一个1MB大的chunk: 186. 187.
188. void slabs_init(size_t limit, double factor) { 189. int i = POWER_SMALLEST - 1;
190. unsigned int size = sizeof(item) + settings.chunk_size; 191.
192. /* Factor of 2.0 means use the default memcached behavior */ 193. if (factor == 2.0 && size < 128) 194. size = 128; 195.
196. mem_limit = limit;
197. memset(slabclass, 0, sizeof(slabclass)); 198.
199. while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) { 200. /* Make sure items are always n-byte aligned */ 201. if (size % CHUNK_ALIGN_BYTES)
202. size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); 203.
204. slabclass[i].size = size;
205. slabclass[i].perslab = POWER_BLOCK / slabclass[i].size; 206. size *= factor;
207. if (settings.verbose > 1) {
208. fprintf(stderr, \perslab ]\\n\
209. i, slabclass[i].size, slabclass[i].perslab);
210. } 211. } 212.
213. power_largest = i;
214. slabclass[power_largest].size = POWER_BLOCK; 215. slabclass[power_largest].perslab = 1; 216.
217. /* for the test suite: faking of how much we've already malloc'd */ 218. {
219. char *t_initial_malloc = getenv(\220. if (t_initial_malloc) { 221. mem_malloced =
atol(getenv(\222. } 223.
224. } 225.
226. #ifndef DONT_PREALLOC_SLABS 227. {
228. char *pre_alloc = getenv(\229. if (!pre_alloc || atoi(pre_alloc)) {
230. slabs_preallocate(limit / POWER_BLOCK); 231. } 232. } 233. #endif 234. }
235. 由上可以看出,memcached的内存分配是有冗余的,当一个slab不能被它所拥有的chunk大小整除时,slab尾部剩余的空间就被丢弃了,如id40中,两个chunk占用了1009384字节,这个slab一共有1MB,那么就有39192字节被浪费了。
236. Memcached使用这种方式来分配内存,是为了可以快速的通过item长度定位出slab的classid,有一点类似hash,因为 item的长度是可以计算的,比如一个item的长度是300字节,在1.2中就可以得到它应该保存在id7的slab中,因为按照上面的计算方 法,id6的chunk大小是252字节,id7的chunk大小是316字节,id8的chunk大小是396字节,表示所有252到316字节的 item都应该保存在id7中。同理,在1.1中,也可以计算得到它出于256和512之间,应该放在chunk_size为512的id9中(32位系 统)。
237. Memcached初始化的时候,会初始化slab(前面可以看到,在main函数中调用了slabs_init())。它会在 slabs_init()中检查一个常量DONT_PREALLOC_SLABS,如果这个没有被定义,说明使用预分配内存方式初始化slab,这样在所 有已经定义过的slabclass中,每一个id创建
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库memcached使用文档(4)在线全文阅读。
相关推荐: