forked from huangzworks/redis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol.html
652 lines (530 loc) · 33.9 KB
/
protocol.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
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>通信协议(protocol) — Redis 命令参考</title>
<script type="text/javascript" src="../_static/js/modernizr.min.js"></script>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'../',
VERSION:'2019',
LANGUAGE:'None',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="复制(Replication)" href="replication.html" />
<link rel="prev" title="键空间通知(keyspace notification)" href="notification.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search">
<a href="../index.html" class="icon icon-home"> Redis 命令参考
</a>
<div class="version">
2019
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../string/index.html">字符串</a></li>
<li class="toctree-l1"><a class="reference internal" href="../hash/index.html">哈希表</a></li>
<li class="toctree-l1"><a class="reference internal" href="../list/index.html">列表</a></li>
<li class="toctree-l1"><a class="reference internal" href="../set/index.html">集合</a></li>
<li class="toctree-l1"><a class="reference internal" href="../sorted_set/index.html">有序集合</a></li>
<li class="toctree-l1"><a class="reference internal" href="../hyperloglog/index.html">HyperLogLog</a></li>
<li class="toctree-l1"><a class="reference internal" href="../geo/index.html">地理位置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../bitmap/index.html">位图</a></li>
<li class="toctree-l1"><a class="reference internal" href="../database/index.html">数据库</a></li>
<li class="toctree-l1"><a class="reference internal" href="../expire/index.html">自动过期</a></li>
<li class="toctree-l1"><a class="reference internal" href="../transaction/index.html">事务</a></li>
<li class="toctree-l1"><a class="reference internal" href="../script/index.html">Lua 脚本</a></li>
<li class="toctree-l1"><a class="reference internal" href="../persistence/index.html">持久化</a></li>
<li class="toctree-l1"><a class="reference internal" href="../pubsub/index.html">发布与订阅</a></li>
<li class="toctree-l1"><a class="reference internal" href="../replication/index.html">复制</a></li>
<li class="toctree-l1"><a class="reference internal" href="../client_and_server/index.html">客户端与服务器</a></li>
<li class="toctree-l1"><a class="reference internal" href="../configure/index.html">配置选项</a></li>
<li class="toctree-l1"><a class="reference internal" href="../debug/index.html">调试</a></li>
<li class="toctree-l1"><a class="reference internal" href="../internal/index.html">内部命令</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">功能文档</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="cluster-spec.html">Redis 集群规范</a></li>
<li class="toctree-l2"><a class="reference internal" href="persistence.html">持久化(persistence)</a></li>
<li class="toctree-l2"><a class="reference internal" href="pubsub.html">发布与订阅(pub/sub)</a></li>
<li class="toctree-l2"><a class="reference internal" href="sentinel.html">Sentinel</a></li>
<li class="toctree-l2"><a class="reference internal" href="cluster-tutorial.html">集群教程</a></li>
<li class="toctree-l2"><a class="reference internal" href="notification.html">键空间通知(keyspace notification)</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">通信协议(protocol)</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#id1">网络层</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id2">请求</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id3">新版统一请求协议</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id4">回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id5">状态回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id6">错误回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id7">整数回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id8">批量回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id9">多条批量回复</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id10">多条批量回复中的空元素</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id11">多命令和流水线</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id12">内联命令</a></li>
<li class="toctree-l3"><a class="reference internal" href="#redis">高性能 Redis 协议分析器</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="replication.html">复制(Replication)</a></li>
<li class="toctree-l2"><a class="reference internal" href="transaction.html">事务(transaction)</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">Redis 命令参考</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html">Docs</a> »</li>
<li><a href="index.html">功能文档</a> »</li>
<li>通信协议(protocol)</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/topic/protocol.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="protocol">
<h1>通信协议(protocol)<a class="headerlink" href="#protocol" title="Permalink to this headline">¶</a></h1>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">本文档翻译自: <a class="reference external" href="http://redis.io/topics/protocol">http://redis.io/topics/protocol</a> 。</p>
</div>
<p>Redis 协议在以下三个目标之间进行折中:</p>
<ul class="simple">
<li>易于实现</li>
<li>可以高效地被计算机分析(parse)</li>
<li>可以很容易地被人类读懂</li>
</ul>
<div class="section" id="id1">
<h2>网络层<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2>
<p>客户端和服务器通过 TCP 连接来进行数据交互,
服务器默认的端口号为 6379 。</p>
<p>客户端和服务器发送的命令或数据一律以 <code class="docutils literal"><span class="pre">\r\n</span></code> (CRLF)结尾。</p>
</div>
<div class="section" id="id2">
<h2>请求<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h2>
<p>Redis 服务器接受命令以及命令的参数。</p>
<p>服务器会在接到命令之后,对命令进行处理,并将命令的回复传送回客户端。</p>
</div>
<div class="section" id="id3">
<h2>新版统一请求协议<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h2>
<p>新版统一请求协议在 Redis 1.2 版本中引入,
并最终在 Redis 2.0 版本成为 Redis 服务器通信的标准方式。</p>
<p>你的 Redis 客户端应该按照这个新版协议来进行实现。</p>
<p>在这个协议中,
所有发送至 Redis 服务器的参数都是二进制安全(binary safe)的。</p>
<p>以下是这个协议的一般形式:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF
</pre></div>
</div>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">译注:命令本身也作为协议的其中一个参数来发送。</p>
</div>
<p>举个例子,
以下是一个命令协议的打印版本:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>*3
$3
SET
$5
mykey
$7
myvalue
</pre></div>
</div>
<p>这个命令的实际协议值如下:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="s2">"*3</span><span class="se">\r\n</span><span class="s2">$3</span><span class="se">\r\n</span><span class="s2">SET</span><span class="se">\r\n</span><span class="s2">$5</span><span class="se">\r\n</span><span class="s2">mykey</span><span class="se">\r\n</span><span class="s2">$7</span><span class="se">\r\n</span><span class="s2">myvalue</span><span class="se">\r\n</span><span class="s2">"</span>
</pre></div>
</div>
<p>稍后我们会看到,
这种格式除了用作命令请求协议之外,
也用在命令的回复协议中:
这种只有一个参数的回复格式被称为<strong>批量回复(Bulk Reply)</strong>。</p>
<p>统一协议请求原本是用在回复协议中,
用于将列表的多个项返回给客户端的,
这种回复格式被称为<strong>多条批量回复(Multi Bulk Reply)</strong>。</p>
<p>一个多条批量回复以 <code class="docutils literal"><span class="pre">*<argc>\r\n</span></code> 为前缀,
后跟多条不同的批量回复,
其中 <code class="docutils literal"><span class="pre">argc</span></code> 为这些批量回复的数量。</p>
</div>
<div class="section" id="id4">
<h2>回复<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h2>
<p>Redis 命令会返回多种不同类型的回复。</p>
<p>通过检查服务器发回数据的第一个字节,
可以确定这个回复是什么类型:</p>
<ul class="simple">
<li>状态回复(status reply)的第一个字节是 <code class="docutils literal"><span class="pre">"+"</span></code></li>
<li>错误回复(error reply)的第一个字节是 <code class="docutils literal"><span class="pre">"-"</span></code></li>
<li>整数回复(integer reply)的第一个字节是 <code class="docutils literal"><span class="pre">":"</span></code></li>
<li>批量回复(bulk reply)的第一个字节是 <code class="docutils literal"><span class="pre">"$"</span></code></li>
<li>多条批量回复(multi bulk reply)的第一个字节是 <code class="docutils literal"><span class="pre">"*"</span></code></li>
</ul>
</div>
<div class="section" id="id5">
<h2>状态回复<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h2>
<p>一个状态回复(或者单行回复,single line reply)是一段以 <code class="docutils literal"><span class="pre">"+"</span></code> 开始、 <code class="docutils literal"><span class="pre">"\r\n"</span></code> 结尾的单行字符串。</p>
<p>以下是一个状态回复的例子:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="o">+</span><span class="n">OK</span>
</pre></div>
</div>
<p>客户端库应该返回 <code class="docutils literal"><span class="pre">"+"</span></code> 号之后的所有内容。
比如在在上面的这个例子中,
客户端就应该返回字符串 <code class="docutils literal"><span class="pre">"OK"</span></code> 。</p>
<p>状态回复通常由那些不需要返回数据的命令返回,这种回复不是二进制安全的,它也不能包含新行。</p>
<p>状态回复的额外开销非常少,只需要三个字节(开头的 <code class="docutils literal"><span class="pre">"+"</span></code> 和结尾的 CRLF)。</p>
</div>
<div class="section" id="id6">
<h2>错误回复<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h2>
<p>错误回复和状态回复非常相似,
它们之间的唯一区别是,
错误回复的第一个字节是 <code class="docutils literal"><span class="pre">"-"</span></code> ,
而状态回复的第一个字节是 <code class="docutils literal"><span class="pre">"+"</span></code> 。</p>
<p>错误回复只在某些地方出现问题时发送:
比如说,
当用户对不正确的数据类型执行命令,
或者执行一个不存在的命令,
等等。</p>
<p>一个客户端库应该在收到错误回复时产生一个异常。</p>
<p>以下是两个错误回复的例子:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="o">-</span><span class="n">ERR</span> <span class="n">unknown</span> <span class="n">command</span> <span class="s1">'foobar'</span>
<span class="o">-</span><span class="n">WRONGTYPE</span> <span class="n">Operation</span> <span class="n">against</span> <span class="n">a</span> <span class="n">key</span> <span class="n">holding</span> <span class="n">the</span> <span class="n">wrong</span> <span class="n">kind</span> <span class="n">of</span> <span class="n">value</span>
</pre></div>
</div>
<p>在 <code class="docutils literal"><span class="pre">"-"</span></code> 之后,直到遇到第一个空格或新行为止,这中间的内容表示所返回错误的类型。</p>
<p><code class="docutils literal"><span class="pre">ERR</span></code> 是一个通用错误,而 <code class="docutils literal"><span class="pre">WRONGTYPE</span></code> 则是一个更特定的错误。
一个客户端实现可以为不同类型的错误产生不同类型的异常,
或者提供一种通用的方式,
让调用者可以通过提供字符串形式的错误名来捕捉(trap)不同的错误。</p>
<p>不过这些特性用得并不多,
所以并不是特别重要,
一个受限的(limited)客户端可以通过简单地返回一个逻辑假(false)来表示一个通用的错误条件。</p>
</div>
<div class="section" id="id7">
<h2>整数回复<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h2>
<p>整数回复就是一个以 <code class="docutils literal"><span class="pre">":"</span></code> 开头, CRLF 结尾的字符串表示的整数。</p>
<p>比如说,
<code class="docutils literal"><span class="pre">":0\r\n"</span></code> 和 <code class="docutils literal"><span class="pre">":1000\r\n"</span></code> 都是整数回复。</p>
<p>返回整数回复的其中两个命令是 <a class="reference internal" href="../string/incr.html#incr"><span class="std std-ref">INCR key</span></a> 和 <a class="reference internal" href="../persistence/lastsave.html#lastsave"><span class="std std-ref">LASTSAVE</span></a> 。
被返回的整数没有什么特殊的含义,
<a class="reference internal" href="../string/incr.html#incr"><span class="std std-ref">INCR key</span></a> 返回键的一个自增后的整数值,
而 <a class="reference internal" href="../persistence/lastsave.html#lastsave"><span class="std std-ref">LASTSAVE</span></a> 则返回一个 UNIX 时间戳,
返回值的唯一限制是这些数必须能够用 64 位有符号整数表示。</p>
<p>整数回复也被广泛地用于表示逻辑真和逻辑假:
比如 <a class="reference internal" href="../database/exists.html#exists"><span class="std std-ref">EXISTS key</span></a> 和 <a class="reference internal" href="../set/sismember.html#sismember"><span class="std std-ref">SISMEMBER key member</span></a> 都用返回值 <code class="docutils literal"><span class="pre">1</span></code> 表示真,
<code class="docutils literal"><span class="pre">0</span></code> 表示假。</p>
<p>其他一些命令,
比如 <a class="reference internal" href="../set/sadd.html#sadd"><span class="std std-ref">SADD key member [member …]</span></a> 、 <a class="reference internal" href="../set/srem.html#srem"><span class="std std-ref">SREM key member [member …]</span></a> 和 <a class="reference internal" href="../string/setnx.html#setnx"><span class="std std-ref">SETNX key value</span></a> ,
只在操作真正被执行了的时候,
才返回 <code class="docutils literal"><span class="pre">1</span></code> ,
否则返回 <code class="docutils literal"><span class="pre">0</span></code> 。</p>
<p>以下命令都返回整数回复:
<a class="reference internal" href="../string/setnx.html#setnx"><span class="std std-ref">SETNX key value</span></a> 、
<a class="reference internal" href="../database/del.html#del"><span class="std std-ref">DEL key [key …]</span></a> 、
<a class="reference internal" href="../database/exists.html#exists"><span class="std std-ref">EXISTS key</span></a> 、
<a class="reference internal" href="../string/incr.html#incr"><span class="std std-ref">INCR key</span></a> 、
<a class="reference internal" href="../string/incrby.html#incrby"><span class="std std-ref">INCRBY key increment</span></a> 、
<a class="reference internal" href="../string/decr.html#decr"><span class="std std-ref">DECR key</span></a> 、
<a class="reference internal" href="../string/decrby.html#decrby"><span class="std std-ref">DECRBY key decrement</span></a> 、
<a class="reference internal" href="../database/dbsize.html#dbsize"><span class="std std-ref">DBSIZE</span></a> 、
<a class="reference internal" href="../persistence/lastsave.html#lastsave"><span class="std std-ref">LASTSAVE</span></a> 、
<a class="reference internal" href="../database/renamenx.html#renamenx"><span class="std std-ref">RENAMENX key newkey</span></a> 、
<a class="reference internal" href="../database/move.html#move"><span class="std std-ref">MOVE key db</span></a> 、
<a class="reference internal" href="../list/llen.html#llen"><span class="std std-ref">LLEN key</span></a> 、
<a class="reference internal" href="../set/sadd.html#sadd"><span class="std std-ref">SADD key member [member …]</span></a> 、
<a class="reference internal" href="../set/srem.html#srem"><span class="std std-ref">SREM key member [member …]</span></a> 、
<a class="reference internal" href="../set/sismember.html#sismember"><span class="std std-ref">SISMEMBER key member</span></a> 、
<a class="reference internal" href="../set/scard.html#scard"><span class="std std-ref">SCARD key</span></a> 。</p>
</div>
<div class="section" id="id8">
<h2>批量回复<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h2>
<p>服务器使用批量回复来返回二进制安全的字符串,字符串的最大长度为 512 MB 。</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端:GET mykey
服务器:foobar
</pre></div>
</div>
<p>服务器发送的内容中:</p>
<ul class="simple">
<li>第一字节为 <code class="docutils literal"><span class="pre">"$"</span></code> 符号</li>
<li>接下来跟着的是表示实际回复长度的数字值</li>
<li>之后跟着一个 CRLF</li>
<li>再后面跟着的是实际回复数据</li>
<li>最末尾是另一个 CRLF</li>
</ul>
<p>对于前面的 <a class="reference internal" href="../string/get.html#get"><span class="std std-ref">GET key</span></a> 命令,服务器实际发送的内容为:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="s2">"$6</span><span class="se">\r\n</span><span class="s2">foobar</span><span class="se">\r\n</span><span class="s2">"</span>
</pre></div>
</div>
<p>如果被请求的值不存在,
那么批量回复会将特殊值 <code class="docutils literal"><span class="pre">-1</span></code> 用作回复的长度值,
就像这样:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端:GET non-existing-key
服务器:$-1
</pre></div>
</div>
<p>这种回复称为空批量回复(NULL Bulk Reply)。</p>
<p>当请求对象不存在时,客户端应该返回空对象,而不是空字符串:
比如 Ruby 库应该返回 <code class="docutils literal"><span class="pre">nil</span></code> ,
而 C 库应该返回 <code class="docutils literal"><span class="pre">NULL</span></code> (或者在回复对象中设置一个特殊标志),
诸如此类。</p>
</div>
<div class="section" id="id9">
<h2>多条批量回复<a class="headerlink" href="#id9" title="Permalink to this headline">¶</a></h2>
<p>像 <a class="reference internal" href="../list/lrange.html#lrange"><span class="std std-ref">LRANGE key start stop</span></a> 这样的命令需要返回多个值,
这一目标可以通过多条批量回复来完成。</p>
<p>多条批量回复是由多个回复组成的数组,
数组中的每个元素都可以是任意类型的回复,
包括多条批量回复本身。</p>
<p>多条批量回复的第一个字节为 <code class="docutils literal"><span class="pre">"*"</span></code> ,
后跟一个字符串表示的整数值,
这个值记录了多条批量回复所包含的回复数量,
再后面是一个 CRLF 。</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端: LRANGE mylist 0 3
服务器: *4
服务器: $3
服务器: foo
服务器: $3
服务器: bar
服务器: $5
服务器: Hello
服务器: $5
服务器: World
</pre></div>
</div>
<p>在上面的示例中,服务器发送的所有字符串都由 CRLF 结尾。</p>
<p>正如你所见到的那样,
多条批量回复所使用的格式,
和客户端发送命令时使用的统一请求协议的格式一模一样。
它们之间的唯一区别是:</p>
<ul class="simple">
<li>统一请求协议只发送批量回复。</li>
<li>而服务器应答命令时所发送的多条批量回复,则可以包含任意类型的回复。</li>
</ul>
<p>以下例子展示了一个多条批量回复,
回复中包含四个整数值,
以及一个二进制安全字符串:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>*5\r\n
:1\r\n
:2\r\n
:3\r\n
:4\r\n
$6\r\n
foobar\r\n
</pre></div>
</div>
<p>在回复的第一行,
服务器发送 <code class="docutils literal"><span class="pre">*5\r\n</span></code> ,
表示这个多条批量回复包含 5 条回复,
再后面跟着的则是 5 条回复的正文。</p>
<p>多条批量回复也可以是空白的(empty),
就像这样:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端: LRANGE nokey 0 1
服务器: *0\r\n
</pre></div>
</div>
<p>无内容的多条批量回复(null multi bulk reply)也是存在的,
比如当 <a class="reference internal" href="../list/blpop.html#blpop"><span class="std std-ref">BLPOP key [key …] timeout</span></a> 命令的阻塞时间超过最大时限时,
它就返回一个无内容的多条批量回复,
这个回复的计数值为 <code class="docutils literal"><span class="pre">-1</span></code> :</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端: BLPOP key 1
服务器: *-1\r\n
</pre></div>
</div>
<p>客户端库应该区别对待空白多条回复和无内容多条回复:
当 Redis 返回一个无内容多条回复时,
客户端库应该返回一个 null 对象,
而不是一个空数组。</p>
</div>
<div class="section" id="id10">
<h2>多条批量回复中的空元素<a class="headerlink" href="#id10" title="Permalink to this headline">¶</a></h2>
<p>多条批量回复中的元素可以将自身的长度设置为 <code class="docutils literal"><span class="pre">-1</span></code> ,
从而表示该元素不存在,
并且也不是一个空白字符串(empty string)。</p>
<p>当 <a class="reference internal" href="../database/sort.html#sort"><span class="std std-ref">SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination]</span></a> 命令使用 <code class="docutils literal"><span class="pre">GET</span> <span class="pre">pattern</span></code> 选项对一个不存在的键进行操作时,
就会发生多条批量回复中带有空白元素的情况。</p>
<p>以下例子展示了一个包含空元素的多重批量回复:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>服务器: *3
服务器: $3
服务器: foo
服务器: $-1
服务器: $3
服务器: bar
</pre></div>
</div>
<p>其中,
回复中的第二个元素为空。</p>
<p>对于这个回复,
客户端库应该返回类似于这样的回复:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="p">[</span><span class="s2">"foo"</span><span class="p">,</span> <span class="n">nil</span><span class="p">,</span> <span class="s2">"bar"</span><span class="p">]</span>
</pre></div>
</div>
</div>
<div class="section" id="id11">
<h2>多命令和流水线<a class="headerlink" href="#id11" title="Permalink to this headline">¶</a></h2>
<p>客户端可以通过流水线,
在一次写入操作中发送多个命令:</p>
<ul class="simple">
<li>在发送新命令之前, 无须阅读前一个命令的回复。</li>
<li>多个命令的回复会在最后一并返回。</li>
</ul>
</div>
<div class="section" id="id12">
<h2>内联命令<a class="headerlink" href="#id12" title="Permalink to this headline">¶</a></h2>
<p>当你需要和 Redis 服务器进行沟通,
但又找不到 <code class="docutils literal"><span class="pre">redis-cli</span></code> ,
而手上只有 <code class="docutils literal"><span class="pre">telnet</span></code> 的时候,
你可以通过 Redis 特别为这种情形而设的内联命令格式来发送命令。</p>
<p>以下是一个客户端和服务器使用内联命令来进行交互的例子:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端: PING
服务器: +PONG
</pre></div>
</div>
<p>以下另一个返回整数值的内联命令的例子:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span>客户端: EXISTS somekey
服务器: :0
</pre></div>
</div>
<p>因为没有了统一请求协议中的 <code class="docutils literal"><span class="pre">"*"</span></code> 项来声明参数的数量,
所以在 <code class="docutils literal"><span class="pre">telnet</span></code> 会话输入命令的时候,
必须使用空格来分割各个参数,
服务器在接收到数据之后,
会按空格对用户的输入进行分析(parse),
并获取其中的命令参数。</p>
</div>
<div class="section" id="redis">
<h2>高性能 Redis 协议分析器<a class="headerlink" href="#redis" title="Permalink to this headline">¶</a></h2>
<p>尽管 Redis 的协议非常利于人类阅读,
定义也很简单,
但这个协议的实现性能仍然可以和二进制协议一样快。</p>
<p>因为 Redis 协议将数据的长度放在数据正文之前,
所以程序无须像 JSON 那样,
为了寻找某个特殊字符而扫描整个 payload ,
也无须对发送至服务器的 payload 进行转义(quote)。</p>
<p>程序可以在对协议文本中的各个字符进行处理的同时,
查找 CR 字符,
并计算出批量回复或多条批量回复的长度,
就像这样:</p>
<div class="highlight-c"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="s">"$123</span><span class="se">\r\n</span><span class="s">"</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">p</span><span class="o">++</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="o">*</span><span class="n">p</span> <span class="o">!=</span> <span class="sc">'\r'</span><span class="p">)</span> <span class="p">{</span>
<span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">len</span><span class="o">*</span><span class="mi">10</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="o">*</span><span class="n">p</span> <span class="o">-</span> <span class="sc">'0'</span><span class="p">);</span>
<span class="n">p</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Now p points at '\r', and the len is in bulk_len. */</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>得到了批量回复或多条批量回复的长度之后,
程序只需调用一次 <code class="docutils literal"><span class="pre">read</span></code> 函数,
就可以将回复的正文数据全部读入到内存中,
而无须对这些数据做任何的处理。</p>
<p>在回复最末尾的 CR 和 LF 不作处理,丢弃它们。</p>
<p>Redis 协议的实现性能可以和二进制协议的实现性能相媲美,
并且由于 Redis 协议的简单性,
大部分高级语言都可以轻易地实现这个协议,
这使得客户端软件的 bug 数量大大减少。</p>
</div>
</div>
<div class="section" id="discuss">
<h2>
讨论
<a class="headerlink" href="#discuss" title="永久链接至标题">¶</a>
</h2>
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'redis-command-cn'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
</div>
<!--
<div id="sponser">
<h2>赞助商</h2>
<p>我们正在寻找赞助商,有意对这个网站进行赞助的朋友请联系 [email protected] 。</p>
</div>
-->
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="replication.html" class="btn btn-neutral float-right" title="复制(Replication)" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="notification.html" class="btn btn-neutral float-left" title="键空间通知(keyspace notification)" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
© Copyright 2019, Redis
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-53959484-7', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>