forked from zouyanjian/techbooks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmemcached.html
1067 lines (830 loc) · 35.9 KB
/
memcached.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
lang="en" xml:lang="en">
<head>
<title>Memcached 技术分析报告</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<meta name="generator" content="Org-mode"/>
<meta name="generated" content="2011-04-02 19:25:20 CST"/>
<meta name="author" content="Eguo Wang"/>
<meta name="description" content=""/>
<meta name="keywords" content=""/>
<style type="text/css">
<!--/*--><![CDATA[/*><!--*/
html { font-family: Times, serif; font-size: 12pt; }
.title { text-align: center; }
.todo { color: red; }
.done { color: green; }
.tag { background-color: #add8e6; font-weight:normal }
.target { }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
p.verse { margin-left: 3% }
pre {
border: 1pt solid #AEBDCC;
background-color: #F3F5F7;
padding: 5pt;
font-family: courier, monospace;
font-size: 90%;
overflow:auto;
}
table { border-collapse: collapse; }
td, th { vertical-align: top; }
dt { font-weight: bold; }
div.figure { padding: 0.5em; }
div.figure p { text-align: center; }
.linenr { font-size:smaller }
.code-highlighted {background-color:#ffff00;}
.org-info-js_info-navigation { border-style:none; }
#org-info-js_console-label { font-size:10px; font-weight:bold;
white-space:nowrap; }
.org-info-js_search-highlight {background-color:#ffff00; color:#000000;
font-weight:bold; }
/*]]>*/-->
</style>
<script type="text/javascript">
<!--/*--><![CDATA[/*><!--*/
function CodeHighlightOn(elem, id)
{
var target = document.getElementById(id);
if(null != target) {
elem.cacheClassElem = elem.className;
elem.cacheClassTarget = target.className;
target.className = "code-highlighted";
elem.className = "code-highlighted";
}
}
function CodeHighlightOff(elem, id)
{
var target = document.getElementById(id);
if(elem.cacheClassElem)
elem.className = elem.cacheClassElem;
if(elem.cacheClassTarget)
target.className = elem.cacheClassTarget;
}
/*]]>*///-->
</script>
</head>
<body>
<div id="content">
<h1 class="title">Memcached 技术分析报告</h1>
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#sec-1">1 关于Memcached </a>
<ul>
<li><a href="#sec-1.1">1.1 简介 </a></li>
<li><a href="#sec-1.2">1.2 设计理念 </a>
<ul>
<li><a href="#sec-1.2.1">1.2.1 简单Key/Value存储 </a></li>
<li><a href="#sec-1.2.2">1.2.2 取巧的设计: 一半在客户端,另一半在服务端 </a></li>
<li><a href="#sec-1.2.3">1.2.3 服务器之间不通信 </a></li>
<li><a href="#sec-1.2.4">1.2.4 忘记数据是一个特性 </a></li>
</ul>
</li>
<li><a href="#sec-1.3">1.3 架构 </a></li>
<li><a href="#sec-1.4">1.4 安全 </a></li>
<li><a href="#sec-1.5">1.5 示例 </a></li>
<li><a href="#sec-1.6">1.6 协议 </a>
<ul>
<li><a href="#sec-1.6.1">1.6.1 关键字 key </a></li>
<li><a href="#sec-1.6.2">1.6.2 指令集 Commands </a></li>
<li><a href="#sec-1.6.3">1.6.3 超时时间 Expiration time </a></li>
<li><a href="#sec-1.6.4">1.6.4 错误信息 Error strings </a></li>
<li><a href="#sec-1.6.5">1.6.5 存储命令 Storage commands </a></li>
<li><a href="#sec-1.6.6">1.6.6 读取命令 Retrieval command </a></li>
<li><a href="#sec-1.6.7">1.6.7 删除 Deletion </a></li>
<li><a href="#sec-1.6.8">1.6.8 增加/减少 Increment/Decrement </a></li>
<li><a href="#sec-1.6.9">1.6.9 统计 Statistics </a></li>
<li><a href="#sec-1.6.10">1.6.10 多用途统计 General-purpose statistics </a></li>
<li><a href="#sec-1.6.11">1.6.11 其他命令 Other commands </a></li>
<li><a href="#sec-1.6.12">1.6.12 UDP协议 UDP protocol </a></li>
</ul>
</li>
<li><a href="#sec-1.7">1.7 主要数据结构及算法 </a></li>
<li><a href="#sec-1.8">1.8 配置选项 </a></li>
</ul>
</li>
<li><a href="#sec-2">2 典型案例 </a>
<ul>
<li><a href="#sec-2.1">2.1 Facebook </a></li>
<li><a href="#sec-2.2">2.2 Twitter </a></li>
</ul>
</li>
<li><a href="#sec-3">3 基于memcached的扩展和演化 </a>
<ul>
<li><a href="#sec-3.1">3.1 redis </a></li>
<li><a href="#sec-3.2">3.2 MemcacheDB </a></li>
<li><a href="#sec-3.3">3.3 MemcacheQ </a></li>
<li><a href="#sec-3.4">3.4 membase </a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-1" class="outline-2">
<h2 id="sec-1"><span class="section-number-2">1</span> 关于Memcached </h2>
<div class="outline-text-2" id="text-1">
</div>
<div id="outline-container-1.1" class="outline-3">
<h3 id="sec-1.1"><span class="section-number-3">1.1</span> 简介 </h3>
<div class="outline-text-3" id="text-1.1">
<p>Memcached是一个通用的分布式内存缓存系统软件,由LiveJoural的技术团队Danga
开发维护,于2003由Brad Fitzpatrick编写发布第一版。常用在以数据库为
数据驱动的网站,从官网提供的数据来看,已知的知名用户包括 LiveJournal,
Wikipedia, Flickr, Bebo, Twitter, Typepad, Yellowbot, YouTube,
Digg, Wordpress.com, Craigslist, Mixi等,其实大家都知道,它几乎是标
配服务器软件了。
</p>
<p>
Memcached通过Hash表将数据平均分布到多台机器上,当数据满时,它通过
least recently used (LRU)算法淘汰出不经常使用的数据,一般情况下它的
分布式是通过客户端的API的实现的。
它是有以下几个部分构成的:
</p><ul>
<li>
客户端软件,它会提供和维护一个可用的memcached服务器列表
</li>
<li>
基于客户端的hash算法,它会根据key来选择连接哪台服务器
</li>
<li>
服务器软件,将key关联的value存储进内部的hash表中。
</li>
<li>
服务端算法,决定何时排出旧的数据(如果内存满时),或内存再利用
</li>
</ul>
<p>Memcached主要由C语言编写,使用libevent提供网络服务,目前稳定
版是1.4.5 (2010-4-3)。可以运行于Unix, Windows和MacOS等多个平台,以
BSD许可证发布。官方网站:<a href="http://www.memcached.org">http://www.memcached.org</a>.
memcached的主代码版本库托管在github上,"master"分支中永远是可运行,
高质量的代码。可以直接通过浏览器访问来查看代码:
</p>
<p>
<a href="http://github.com/memcached/memcachd">http://github.com/memcached/memcachd</a>
</p>
<p>
git clone git://github.com/memcached/memcached
</p>
<p>
主开发者的代码库有以下几个:
</p><ul>
<li>
dormando: git://github.com/dormando/memcached.git
</li>
<li>
dustin: git://github.com/dustin/memcached.git
</li>
<li>
trond: git://github.com/trondn/memcached.git
</li>
</ul>
<p>同时,它也在google code上有个官方项目主页:
<a href="http://code.google.com/p/memcached/">http://code.google.com/p/memcached/</a>
</p>
</div>
</div>
<div id="outline-container-1.2" class="outline-3">
<h3 id="sec-1.2"><span class="section-number-3">1.2</span> 设计理念 </h3>
<div class="outline-text-3" id="text-1.2">
</div>
<div id="outline-container-1.2.1" class="outline-4">
<h4 id="sec-1.2.1"><span class="section-number-4">1.2.1</span> 简单Key/Value存储 </h4>
<div class="outline-text-4" id="text-1.2.1">
<p>服务端并不关心你的数据是怎样的,数据单元由key, 过期时间,可选标志
和原始数据构成,它并不理解你的数据结构,存储前需要你自己做序列
化。有些命令(incr/decr)可能会操作隐含的数据,但实现并不难。
</p>
</div>
</div>
<div id="outline-container-1.2.2" class="outline-4">
<h4 id="sec-1.2.2"><span class="section-number-4">1.2.2</span> 取巧的设计: 一半在客户端,另一半在服务端 </h4>
<div class="outline-text-4" id="text-1.2.2">
<p>部分"memcached实现"是在客户端,部分在服务端。客户端知道如何将
数据发送给特定的服务器,以及在不能连接到服务器时该怎么做,和怎样从
服务器上获取key。而服务器端知道怎样接收存储数据,和让他们过期。
</p>
</div>
</div>
<div id="outline-container-1.2.3" class="outline-4">
<h4 id="sec-1.2.3"><span class="section-number-4">1.2.3</span> 服务器之间不通信 </h4>
<div class="outline-text-4" id="text-1.2.3">
<p>Memcached服务器通常是不与其它服务器通信的,也不会有同步操作,和广
播。
</p></div>
</div>
<div id="outline-container-1.2.4" class="outline-4">
<h4 id="sec-1.2.4"><span class="section-number-4">1.2.4</span> 忘记数据是一个特性 </h4>
<div class="outline-text-4" id="text-1.2.4">
<p>Memcached,默认的是一个最近使用的数据缓存服务软件,它会通过LRU算法
将旧的不再使用的数据从中移除,保证在一定量的内存中始终有存储空间可
用,也是为了更好的使用内存。
</p></div>
</div>
</div>
<div id="outline-container-1.3" class="outline-3">
<h3 id="sec-1.3"><span class="section-number-3">1.3</span> 架构 </h3>
<div class="outline-text-3" id="text-1.3">
<p>此系统使用C/S架构模式,服务器端维护一个Key-Value的关联数组,客户端
填充这个数组并检索它。key最大长度长限是250byte,value最多1M大小。
客户程序通过使用客户端程序库联接远程服务器,默认端口是11211。只有客
户端知道所有联接到的服务器,而服务器并不直接与其它服务器通信。如果
客户端想存储或通过key读取value时,客户端程序库首先根据key计算出一个
相应的服务器hash,然后直接连接到那台服务器上,服务器将计算出它自己
使用的key的hash来决定存储位置或读取对应的value。
</p>
<p>
所有的数据都存储在内存中,如果内存空间不足时,它会排除旧的不再使用
的value,在那之前,客户端并不能认定当需要时数据还是存在的。
</p>
<p>
一般情况下,会以一些服务器和多个客户端的方式来部署,当然,也是可以
继续以CS模式部署在单台服务器上的。它支持TCP,UDP,UNIX domain socket
等几种可选网络通信方式。
</p>
</div>
</div>
<div id="outline-container-1.4" class="outline-3">
<h3 id="sec-1.4"><span class="section-number-3">1.4</span> 安全 </h3>
<div class="outline-text-3" id="text-1.4">
<p>大多数情况下,memcached被部署在可信的网络环境中,客户端可以自由无阻
的连接任一服务器。当然它也是可以被部署在不可信的网络环境中,或开放网
络,或是管理员就想控制客户端到服务端的环境中的,此时应将可选的SASL
认证支持一起编译到memcached中,需要注意的是,SASL认证支持只能使用二
进制通信协议。更多细节请参考memcached的sasl编译说明。
</p></div>
</div>
<div id="outline-container-1.5" class="outline-3">
<h3 id="sec-1.5"><span class="section-number-3">1.5</span> 示例 </h3>
<div class="outline-text-3" id="text-1.5">
<p>读取数据,如果不存在,则从数据库中取并加入memcached中
</p>
<pre class="example">function get_foo(int userid) {
/* first try the cache */
data = memcached_fetch("userrow:" + userid);
if (!data) {
/* not found : request database */
data = db_select("select * from users where userid=?", userid);
/* then store in cache util next get */
memcached_add("userrow:" + userid, data);
}
return data;
}
</pre>
<p>
更新数据
</p>
<pre class="example">function update_foo(int userid, string dbUpdateString) {
/* first update database */
result = db_execute(dbUpdateString);
if (result) {
/* database update successful:fetch data to be stored in cache */
data = db_select("select * from users where userid=?", userid);
memcached_set("userrow:" + userid, data);
}
}
</pre>
</div>
</div>
<div id="outline-container-1.6" class="outline-3">
<h3 id="sec-1.6"><span class="section-number-3">1.6</span> 协议 </h3>
<div class="outline-text-3" id="text-1.6">
<p>memcached的客户端通过TCP连接与服务器通信(UDP协议的接口也可以使用,
详细说明请参考”UDP 协议”部分)。一个给定的运行中的memcached服务器在
某个(可配置的)端口上监听连接;客户端连接该端口,发送命令给服务器,
读取反馈,最后关闭连接。
</p>
<p>
没有必要发送一个专门的命令去结束会话。客户端可以在不需要该连接的
时候就关闭它。注意:我们鼓励客户端缓存它们与服务器的连接,而不是
每次要存储或读取数据的时候再次重新建立与服务器的连接。memcache同
时打开很多连接不会对性能造成到大的影响,这是因为memcache在设计之
处,就被设计成即使打开了很多连接(数百或者需要时上千个连接)也可
以高效的运行。缓存连接可以节省与服务器建立TCP连接的时间开销(于
此相比,在服务器段为建立一个新的连接所做准备的开销可以忽略不计)。
</p>
<p>
memcache通信协议有两种类型的数据:文本行和非结构化数据。文本行用
来发送从客户端到服务器的命令以及从服务器回送的反馈信息。非结构化
的数据用在客户端希望存储或者读取数据时。服务器会以字符流的形式严
格准确的返回相应数据在存储时存储的数据。服务器不关注字节序,它也
不知道字节序的存在。memcahce对非结构化数据中的字符没有任何限制,
可以是任意的字符,读取数据时,客户端可以在前次返回的文本行中确切
的知道接下来的数据块的长度。
</p>
<p>
文本行通常以"\r\n"结束。非结构化数据通常也是以"\r\n"结束,尽管
\r、\n或者其他任何8位字符可以出现在数据块中。所以当客户端从服务
器读取数据时,必须使用前面提供的数据块的长度,来确定数据流的结
束,二不是依据跟随在字符流尾部的"\r\n"来确定数据流的结束,尽管实
际上数据流格式如此。
</p>
</div>
<div id="outline-container-1.6.1" class="outline-4">
<h4 id="sec-1.6.1"><span class="section-number-4">1.6.1</span> 关键字 key </h4>
<div class="outline-text-4" id="text-1.6.1">
<p>memcached使用关键字来区分存储不同的数据。关键字是一个字符串,可以唯
一标识一条数据。当前关键字的长度限制是250个字符(当然目前客户端似乎
没有需求用这么长的关键字);关键字一定不能包含控制字符和空格。
</p>
</div>
</div>
<div id="outline-container-1.6.2" class="outline-4">
<h4 id="sec-1.6.2"><span class="section-number-4">1.6.2</span> 指令集 Commands </h4>
<div class="outline-text-4" id="text-1.6.2">
<p>memcahe有3种类型的命令:
</p><ul>
<li id="sec-1.6.2.1">存储命令(6个命令:"set"、"add"、"replace"、"append"、"prepend"和"cas") <br/>
<p>
要求服务器安装关键字存储数据。客户端发送一个命令行,然后一个数据块;命
令执行后客户端等待一行反馈,用来表示命令执行成功与否。
</p>
</li>
<li id="sec-1.6.2.2">读取命令("get"和"gets") <br/>
<p>
要求服务器根据一组关键字读取数据(在一个请求总可以包含一个或多个关
键字)。客户端发送一个包含请求关键字的命令行;命令传递到服务器后,
服务器就查找每一个关键字下的数据,然户将数据以“一个关键字数据,一个
反馈信息行跟着一个数据块”的格式回送数据,直到服务器发送“END”的反馈
行。
</p>
</li>
<li id="sec-1.6.2.3">其他命令,如flush all,version等 <br/>
<p>
这些命令不使用非结构化的数据。对于这些命令,客户端发送一个文本的命
令行,根据命令的特性等待一行数据或者在最后一行以“END“结尾的几行反馈
信息。
</p>
<p>
所有的命令行总是以命令的名字开始,紧接着是以空格分割的参数。命令名
称都是小写,并且是大小写敏感的。
</p>
</li>
</ul>
</div>
</div>
<div id="outline-container-1.6.3" class="outline-4">
<h4 id="sec-1.6.3"><span class="section-number-4">1.6.3</span> 超时时间 Expiration time </h4>
<div class="outline-text-4" id="text-1.6.3">
<p>一些发送到服务器的命令包含超时时间(该超时时间对应于:数据项保存时
间;客户端操作限时)。在这些例子中,被发送的真实时间要么是UNIX时间
戳(自1970年1月1日零时起的秒数数值),或者从当前时间开始算起的秒数。
对于后一种情况,秒数的数值不能超过60*60*24*30(30天的秒数);如果秒
数的数值大于了这个数值,服务器会认为该数值是UNIX时间戳,而不是自当
前时间开始的秒数偏移值。
</p>
</div>
</div>
<div id="outline-container-1.6.4" class="outline-4">
<h4 id="sec-1.6.4"><span class="section-number-4">1.6.4</span> 错误信息 Error strings </h4>
<div class="outline-text-4" id="text-1.6.4">
<p>每个命令都有可能被反馈以一个错误消息。这些错误消息有以下三个类型:
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>"ERROR\r\n"</td><td>意味着客户端发送了一个在协议中不存在的命令。</td></tr>
<tr><td>"CLIENT_ERROR <error>\r\n"</td><td>表示客户端输入的命令行上存在某种错误,输入不符合协议规定。<error>是一个人工可读(human-readable)的错误注释。</td></tr>
<tr><td>"SERVER_ERROR <error>\r\n"</td><td>表示服务器在执行命令时发生了某些错误,致使服务器无法执行下去。<error>也是一个人工可读(human-readable)的错误注释。在一些情况下,错误导致服务器不能再为客户端服务(这样的情况很少发生),服务器就会在发生错误消息后主动关闭连接。这也是服务器主动关闭到客户端连接的唯一情况。</td></tr>
</tbody>
</table>
<p>
后续秒数各种命令的时候,我们不再赘述错误消息的情况,当我们要清楚错误是存在的,不可忽略。
</p>
</div>
</div>
<div id="outline-container-1.6.5" class="outline-4">
<h4 id="sec-1.6.5"><span class="section-number-4">1.6.5</span> 存储命令 Storage commands </h4>
<div class="outline-text-4" id="text-1.6.5">
<p>首先,客户端发生如下这样的命令:
</p>
<pre class="example"><command name> <key> <flags> <exptime> <bytes>\r\n
<data block>\r\n
</pre>
<p>
其中:
</p>
<ul>
<li>
<command name> 是 set、add或者replace。set表示存储该数据;add表
示如果服务器没有保存该关键字的情况下,存储该数据;replace表示在
服务器已经拥有该关键字的情况下,替换原有内容。
</li>
<li>
<key> 是客户端要求服务器存储数据的关键字。
</li>
<li>
<flags> 是一个16位的无符号整数,服务器将它和数据一起存储并且当该
数据被检索时一起返回。客户端可能使用该数值作为一个位图来存储特殊
数据信息;这个字段对服务器不是透明的。
</li>
<li>
<exptime> 是超时时间。如果值为0表示该数据项永远不超时(但有时候该
数据项可能被删除以为其他数据腾出空间);如果值不为0,可能是绝对
的UNIX时间,也可能是自现在开始的偏移值,它保证客户段在这个超时时
间到达后,客户端将取不到该数据项。
</li>
<li>
<bytes> 是随后数据的字节数,不包括终结符"\r\n"。<bytes>有可能是
0,它后面将是一个空的数据块。
</li>
<li>
<data block> 是真正要存储数据流。
</li>
</ul>
<p>发送命令行和数据后,客户端等待反馈,可以是如下几种情况:
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>"STORED\r\n</td><td>表示存储数据成功。</td></tr>
<tr><td>"NOT_STORED\r\n</td><td>表示发送的数据没有存储,但这不因为错误,而是发生在add或者replace命令不能满足条件时,或者数据项正处于要删除的队列中。</td></tr>
<tr><td>错误消息</td><td></td></tr>
</tbody>
</table>
</div>
</div>
<div id="outline-container-1.6.6" class="outline-4">
<h4 id="sec-1.6.6"><span class="section-number-4">1.6.6</span> 读取命令 Retrieval command </h4>
<div class="outline-text-4" id="text-1.6.6">
<p>读取命令如下所示:
</p>
<pre class="example">get <key>*\r\n
</pre>
<ul>
<li>
<key>* 表示一个或多个使用空格分割的关键字字符串。
</li>
</ul>
<p>发送命令后,客户端等待返回一个或多个数据项,每个数据项的格式是一个
文本行,后跟着一个数据块。当所有的数据项发送完毕后,服务器发送字符
串”END\r\n”表示服务器反馈数据的结束。
</p>
<p>
返回数据项的格式如下:
</p>
<pre class="example">VALUE <key> <flags> <bytes>\r\n
<data block>\r\n
</pre>
<ul>
<li>
<key> 是发生数据项的关键字。
</li>
<li>
<flags> 是存储该数据项时,客户端命令中的标志字段。
</li>
<li>
<bytes> 是紧跟文本行后数据块的长度,不包括终结符"\r\n"。
</li>
<li>
<data block>是数据项的数据部分。
</li>
</ul>
<p>如果请求命令行中的有些关键字对应的数据项没有被返回,这意味着服务器
没有该关键字标示下的数据项(有可能是从来没有被存储过,或者存储过但
被删除掉以腾出内存空间,或者数据项超时了,再或者它被某个客户端删除
了)。
</p>
</div>
</div>
<div id="outline-container-1.6.7" class="outline-4">
<h4 id="sec-1.6.7"><span class="section-number-4">1.6.7</span> 删除 Deletion </h4>
<div class="outline-text-4" id="text-1.6.7">
<p>删除命令允许直接删除数据项,命令格式如下:
</p>
<pre class="example">delete <key> <time>\r\n
</pre>
<ul>
<li>
<key> 是客户端希望服务器删除数据项的关键字
</li>
<li>
<time> 是客户端希望服务器阻止add和replace命令使用该关键字数据项的
秒数,可以是相对时间也可以是UNIX的绝对时间。在这段时间内,数据项
被放入一个删除队列,它不能被get命令读取,在其上使用add和replace
也会失败,但使用set命令可以成功。当这个时间过去后,数据项从服务
器的内存中真正的删除。该参数是可选参数,如果不存在默认为0,这意
味着立即从服务器上删除。
</li>
</ul>
<p>服务器返回信息:
</p><table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>"DELETED\r\n"</td><td>表示数据项删除成功</td></tr>
<tr><td>"NOT_FOUND\r\n</td><td>表示该关键字指定的数据项在服务器上没有找到</td></tr>
<tr><td>其他错误消息</td><td></td></tr>
</tbody>
</table>
下面的flush_all命令使得所有存在的数据项立即失效。
</div>
</div>
<div id="outline-container-1.6.8" class="outline-4">
<h4 id="sec-1.6.8"><span class="section-number-4">1.6.8</span> 增加/减少 Increment/Decrement </h4>
<div class="outline-text-4" id="text-1.6.8">
<p>“incr”和”decr”命令用来修改以及存在的数据项的内容,增加或者减少它。
该数据被当作32位无符号整数处理。如果当前数据非此类数据,则经将该内
容当作0来处理。另外在其上施加incr/decr命令的数据项必须是业已存在
的;对于不存在的数据项不会将它作为0对待,而是以错误结束。
</p>
<p>
客户端发送命令行如下格式:
</p>
<pre class="example">incr <key> <value>\r\n
</pre>
<p>
或者
</p>
<pre class="example">decr <key> <value>\r\n
</pre>
<ul>
<li>
<key> 是客户端要修改数据项的关键字
</li>
<li>
<value> 是对该数据行进行增加或者减少的操作数。它是一个32位的无符号整数。
</li>
</ul>
<p>反馈信息有如下几种:
</p><table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>"NOT_FOUND\r\n"</td><td>表明在服务器上没有找到该数据项。</td></tr>
<tr><td>"<value>\r\n"</td><td>value是执行完增加/减少命令后,该数据项新的数值。</td></tr>
<tr><td>错误信息。</td><td></td></tr>
</tbody>
</table>
<p>
注意到“decr”命令的下溢问题,如果客户端尝试减少的数量小于0,其结果是
0。“incr”命令的溢出问题没有检查。另外减少一个数据而使它减少了长度,
但不保证减少它返回时的长度。该数字可能是附加空格的数字,但这只是实
现的优化,所以你不能相信它。
</p>
</div>
</div>
<div id="outline-container-1.6.9" class="outline-4">
<h4 id="sec-1.6.9"><span class="section-number-4">1.6.9</span> 统计 Statistics </h4>
<div class="outline-text-4" id="text-1.6.9">
<p>“stats”命令用来查询服务器的运行情况和其他内部数据。它有两种情况,以有无参数来区分:
</p>
<pre class="example">stats\r\n
</pre>
<p>
或者
</p>
<pre class="example">stats <args>\r\n
</pre>
<ul>
<li>
第一种情况它导致服务器输出一般统计信息以及设置信息和文档化内容。
</li>
<li>
第二种情况根据<args>具体的参数,服务器发送各种内部数据。这部分没
有在协议中文档化,因为与memcache的开发者有关其可能是随时变化的。
</li>
</ul>
</div>
</div>
<div id="outline-container-1.6.10" class="outline-4">
<h4 id="sec-1.6.10"><span class="section-number-4">1.6.10</span> 多用途统计 General-purpose statistics </h4>
<div class="outline-text-4" id="text-1.6.10">
<p>当接收到没有带参数的“stats”命令后,服务器发送许多类似与如下格式的文
本行:
</p>
<pre class="example">STAT <name> <value>\r\n
</pre>
<p>
当类似的文本行全部发送完毕后,服务器发送如下的文本行结束反馈信息:
</p>
<pre class="example">END\r\n
</pre>
<p>
在所有STAT文本行中,<name>是该统计项目的名称,<value>是其数据。下面
是一份stats命令反馈的所有统计项目的列表,后面跟着其值的数据类型。在
数据类型列中,”32u”表示一个32位无符号整数,”64u”表示一个64位无符号
整数,”32u:32u”表示是两个用冒号分割的32位无符号整数。
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption>stats命令反馈</caption>
<colgroup><col align="left" /><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>名称</td><td>值类型</td><td>含义</td></tr>
<tr><td>pid</td><td>32u</td><td>服务器进程的进程号</td></tr>
<tr><td>uptime</td><td>32u</td><td>服务器自运行以来的秒数</td></tr>
<tr><td>time</td><td>32u</td><td>当前服务器上的UNIX时间</td></tr>
<tr><td>version</td><td>string</td><td>服务器的版本字符串</td></tr>
<tr><td>rusage_user</td><td>32u:32u</td><td>服务器进程积累的用户时间(秒:微妙)</td></tr>
<tr><td>rusage_system</td><td>32u:32u</td><td>服务器进程积累的系统时间(秒:微妙)</td></tr>
<tr><td>curr_items</td><td>32u</td><td>当前在服务器上存储的数据项的个数</td></tr>
<tr><td>total_items</td><td>32u</td><td>在服务器上曾经保存过的数据项的个数</td></tr>
<tr><td>bytes</td><td>64u</td><td>当前服务器上保存数据的字节数</td></tr>
<tr><td>curr_connections</td><td>32u</td><td>处于打开状态的连接数目</td></tr>
<tr><td>total_connections</td><td>32u</td><td>曾经打开过的所有连接的数目</td></tr>
<tr><td>connection_structures</td><td>32u</td><td>服务器分配的连接结构体的个数</td></tr>
<tr><td>cmd_get</td><td>64u</td><td>get命令请求的次数</td></tr>
<tr><td>cmd_set</td><td>64u</td><td>存储命令请求的次数</td></tr>
<tr><td>get_hits</td><td>64u</td><td>关键字获取命中的次数</td></tr>
<tr><td>get_misses</td><td>64u</td><td>关键字获取没有命中的次数</td></tr>
<tr><td>evictions</td><td>64u</td><td>所有因超时而被替换出内存的数据项的个数</td></tr>
<tr><td>bytes_read</td><td>64u</td><td>服务器从网络上读取到的字节数</td></tr>
<tr><td>bytes_write</td><td>64u</td><td>服务器向网络上写的字节数</td></tr>
<tr><td>limit_maxbytes</td><td>64u</td><td>服务器允许存储数据的最大值</td></tr>
</tbody>
</table>
</div>
</div>
<div id="outline-container-1.6.11" class="outline-4">
<h4 id="sec-1.6.11"><span class="section-number-4">1.6.11</span> 其他命令 Other commands </h4>
<div class="outline-text-4" id="text-1.6.11">
<p>"flush_all"是一个带有可选数字参数的命令,它的执行总是成功的,服务器
总是响应以"OK\r\n"字符串。它的作用是使得所有的数据项立即(默认)或
者经过一个指定的超时时间后全部失效。在置数据项失效后,对于读取命令
将不会返回任何内容,除非在失效后这些数据再次被存储。flush_all并没有
真正的释放这些存在过的数据项占用的内存空间;数据空间真实被占用的情
况发生在使用新的数据项覆盖老的数据项时。该命令作用最准确的定义是:
它导致所有更新时间早于该命令设定的时间点的数据项,在被检索时被忽
略,其表现就像已被删除了一样。
</p>
<p>
使用带有延时flush_all命令的目的是,当你有个memcached服务器池,需要
刷新所有的内容时,但不能在同一时间刷洗所有的服务器,这样就可能因为
所有的服务器突然都要重新建立数据内容,而导致数据库压力的颠簸。延时
选项允许你设置他们隔10秒失效(设置第一个延时为0,第二个10秒,第三个
20秒等等)。
</p>
<p>
"version"是一个没有参数的命令,命令格式如下:
</p>
<pre class="example">version\r\n
</pre>
<p>
服务器发回的反馈信息如下:
</p><table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>"VERSION <version>\r\n"</td><td><version> 是从服务器返回的版本字符串。</td></tr>
<tr><td>错误消息。</td><td></td></tr>
</tbody>
</table>
<p>
"verbosity"是一个带有数字参数的命令。它的执行总是成功的,服务器反馈
以"OK\r\n"表示执行完成。它用来设置日志输出的详细等级。
</p>
<p>
"quit"是一个没有参数的命令。其格式如下:
</p>
<pre class="example">quit\r\n
</pre>
<p>
当服务器接受到此命令后,就关闭与该客户的连接。不管怎样,客户端可以
在任意不需要该连接的时刻关闭它,而不需要发送该命令。
</p>
</div>
</div>
<div id="outline-container-1.6.12" class="outline-4">
<h4 id="sec-1.6.12"><span class="section-number-4">1.6.12</span> UDP协议 UDP protocol </h4>
<div class="outline-text-4" id="text-1.6.12">
<p>
当基于TCP协议的连接数超过TCP连接的上限时,我们可以使用UDP协议来替代。
但是UDP协议接口不提供可靠的传输,所以多用在不严格要求成功的操作上;
典型的get请求会因为缓存的问题,引起丢失或者不完整的传输。
</p>
<p>
每个UDP数据包包含一个简单的帧头,接着就是如TCP协议描述的数据格式的
数据流。在当前的实现中,请求必须包含在一个单独的UDP数据包中,但返回
可能分散在多个数据包中。(唯一的可以拆分请求数据包的是大的多关键字
get请求和set请求,鉴于可靠性相比而言他们更适合用TCP传输。)
</p>
<p>
帧头有8字节长,如下是其格式(所有的数字都是16位网络字节序整形,高位
在前):
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col align="left" /><col align="left" />
</colgroup>
<tbody>
<tr><td>0 - 1</td><td>请求ID</td></tr>
<tr><td>2 - 3</td><td>序列号</td></tr>
<tr><td>4 - 5</td><td>在当前的消息中含有的数据包的个数</td></tr>
<tr><td>6 - 7</td><td>保留以后使用,当前必须为0</td></tr>
</tbody>
</table>
<p>
请求ID由客户端提供。它的典型值是一个从随机种子开始递增值,实际上客
户端可以使用任意的请求ID。服务器的反馈信息中包含了和请求命令中一样
的请求ID。客户端凭借这个请求ID区分来自于同一服务器的反馈。每一个包
含未知请求ID的数据包,可能是由于延时反馈造成,这些数据包都应该抛弃
不用。
</p>
<p>
序列号从0到n-1,n是消息中总的数据包的个数。客户端按照序列号排序重组
数据包;结果序列中包含了一个完整的如TCP协议一样格式的反馈信息(包含
了"\r\n"总结字符串)。
</p>
</div>
</div>
</div>
<div id="outline-container-1.7" class="outline-3">
<h3 id="sec-1.7"><span class="section-number-3">1.7</span> 主要数据结构及算法 </h3>
<div class="outline-text-3" id="text-1.7">
</div>
</div>
<div id="outline-container-1.8" class="outline-3">
<h3 id="sec-1.8"><span class="section-number-3">1.8</span> 配置选项 </h3>
<div class="outline-text-3" id="text-1.8">
</div>
</div>
</div>
<div id="outline-container-2" class="outline-2">
<h2 id="sec-2"><span class="section-number-2">2</span> 典型案例 </h2>
<div class="outline-text-2" id="text-2">
</div>
<div id="outline-container-2.1" class="outline-3">
<h3 id="sec-2.1"><span class="section-number-3">2.1</span> Facebook </h3>
<div class="outline-text-3" id="text-2.1">
</div>
</div>
<div id="outline-container-2.2" class="outline-3">
<h3 id="sec-2.2"><span class="section-number-3">2.2</span> Twitter </h3>
<div class="outline-text-3" id="text-2.2">
</div>
</div>
</div>
<div id="outline-container-3" class="outline-2">
<h2 id="sec-3"><span class="section-number-2">3</span> 基于memcached的扩展和演化 </h2>
<div class="outline-text-2" id="text-3">
</div>
<div id="outline-container-3.1" class="outline-3">
<h3 id="sec-3.1"><span class="section-number-3">3.1</span> redis </h3>
<div class="outline-text-3" id="text-3.1">