-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
682 lines (328 loc) · 595 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Netty源码总结(一)</title>
<link href="2021/08/23/NIO/Netty%E6%BA%90%E7%A0%81/"/>
<url>2021/08/23/NIO/Netty%E6%BA%90%E7%A0%81/</url>
<content type="html"><![CDATA[<h1 id="Netty源码之EventLoop"><a href="#Netty源码之EventLoop" class="headerlink" title="Netty源码之EventLoop"></a>Netty源码之EventLoop</h1><h3 id="Reactor回顾"><a href="#Reactor回顾" class="headerlink" title="Reactor回顾"></a>Reactor回顾</h3><p><img src="https://i.loli.net/2021/08/23/Lx7itHT5ISOEpaK.png" alt="Reactor模型"></p><p>channel和selector属于 java.nio 包中的类,分别为网络通信中的通道(连接)和选择器。</p><p>Reactor和 handler 属于Reactor模型高性能编程中的应用程序角色,分别为反应器和处理器。</p><p><img src="https://i.loli.net/2021/08/23/uHQGgqv79sE6VyX.png" alt="Reactor流程"></p><p><strong>第一步:注册</strong></p><p>将channel 通道的就绪事件,注册到选择器Selector。这块注册的代码,放在Reactor的构造函数中完成。一般来说,一个Reactor 对应一个选择器Selector,一个Reactor拥有一个Selector成员属性。</p><p><strong>第二步:轮询</strong></p><p>轮询的代码,是Reactor重要的一个组成部分,或者说核心的部分。轮询选择器是否有就绪事件。</p><p><strong>第三步:分发</strong></p><p>将就绪事件,分发到事件附件的处理器handler中,由handler完成实际的处理。</p><h3 id="Netty中Reactor模式的应用"><a href="#Netty中Reactor模式的应用" class="headerlink" title="Netty中Reactor模式的应用"></a>Netty中Reactor模式的应用</h3><p><img src="https://i.loli.net/2021/08/23/1nEcLfgPoMy8Zp5.png" alt="Netty模型"></p><p>Netty中的<strong>Channel</strong>系列类型,对应于经典Reactor模型中的client, 封装了用户的通讯连接。</p><p>Netty中的<strong>EventLoop</strong>系列类型,对应于经典Reactor模型中的Reactor,完成Channel的注册、轮询、分发。</p><p>Netty中的<strong>Handler</strong>系列类型,对应于经典Reactor模型中的Handler,不过Netty中的Handler设计得更加的高级和巧妙,使用了Pipeline模式。</p><h3 id="EventLoop"><a href="#EventLoop" class="headerlink" title="EventLoop"></a>EventLoop</h3><p>EventLoop 不是Netty中的一个类,而是一系列的类,或者说一组类。这一组类的作用,对应于Reactor模式的Reactor 角色。</p><p><strong>注册:</strong>Netty的Channel通过javaChannel()方法返回了一个 Java NIO SocketChannel,然后将这个SocketChannel 注册到与 eventLoop 关联的 selector 上。</p><p><strong>轮询:</strong>在Channel注册完成之后,EventLoop 就会开启轮询模式。整个轮询的过程,和经典的Reactor模式的流程大致相同。</p><p><img src="https://i.loli.net/2021/08/23/gt3P4WXk5AsaRef.png" alt="轮询流程"></p><p>Netty中,一个 NioEventLoop 本质上是和一个特定的线程绑定, 这个线程保存在EvnentLoop的父类属性中。</p><p>事件的轮询,在NioEventLoop.run() 方法</p><p><strong>分发:</strong>在AbstractNioByteChannel 中,可以找到 unsafe.read( ) 调用的实现代码。 unsafe.read( )负责的是 Channel 的底层数据的 IO 读取,并且将读取的结果,dispatch(分派)给最终的Handler。</p>]]></content>
<categories>
<category> Netty </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 框架 </tag>
</tags>
</entry>
<entry>
<title>《Scalable IO in Java》中的Reactor模式</title>
<link href="2021/08/23/NIO/%E3%80%8AScalable%20IO%20in%20Java%E3%80%8B%E4%B8%AD%E7%9A%84Reactor%E6%A8%A1%E5%BC%8F/"/>
<url>2021/08/23/NIO/%E3%80%8AScalable%20IO%20in%20Java%E3%80%8B%E4%B8%AD%E7%9A%84Reactor%E6%A8%A1%E5%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="Reactor模式"><a href="#Reactor模式" class="headerlink" title="Reactor模式"></a>Reactor模式</h1><h3 id="简介:"><a href="#简介:" class="headerlink" title="简介:"></a>简介:</h3><p>Reactor模式也叫反应器模式,大多数IO相关组件如Netty、Redis在使用的IO模式,用来解决高性能并发问题。</p><h3 id="传统IO的缺陷:"><a href="#传统IO的缺陷:" class="headerlink" title="传统IO的缺陷:"></a>传统IO的缺陷:</h3><p>最原始的网络编程思路就是服务器用一个while循环,不断监听端口是否有新的套接字连接,如果有,那么就调用一个处理函数处理,类似:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">{</span>socket <span class="token operator">=</span> <span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">handle</span><span class="token punctuation">(</span>socket<span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种方法的最大问题是无法并发,效率太低,如果当前的请求没有处理完,那么后面的请求只能被<strong>阻塞</strong>,服务器的吞吐量太低。</p><p>之后,想到了使用多线程,也就是很经典的connection per thread,每一个连接用一个线程处理,类似:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">ServerSocket</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">Socket</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">BasicModel</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token class-name">ServerSocket</span> ss <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ServerSocket</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SOCKET_SERVER_PORT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">interrupted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span>ss<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//创建新线程来handle</span> <span class="token comment">// or, single-threaded, or a thread pool</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Handler</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token class-name">Socket</span> socket<span class="token punctuation">;</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span><span class="token class-name">Socket</span> s<span class="token punctuation">)</span> <span class="token punctuation">{</span> socket <span class="token operator">=</span> s<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> input <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>INPUT_SIZE<span class="token punctuation">]</span><span class="token punctuation">;</span> socket<span class="token punctuation">.</span><span class="token function">getInputStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> output <span class="token operator">=</span> <span class="token function">process</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> socket<span class="token punctuation">.</span><span class="token function">getOutputStream</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>output<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">process</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> input<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> output<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span> output<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对于每一个请求都分发给一个线程,每个线程中都独自处理上面的流程。</p><h4 id="多线程并发模式的优点:"><a href="#多线程并发模式的优点:" class="headerlink" title="多线程并发模式的优点:"></a>多线程并发模式的优点:</h4><p>一定程度上极大地提高了服务器的<strong>吞吐量</strong>,因为之前的请求在read阻塞以后,不会影响到后续的请求,因为他们在不同的线程中。这也是为什么通常会讲“一个线程只能对应一个socket”的原因。另外有个问题,如果一个线程中对应多个socket连接不行吗?语法上可以,但是实际上没有用,每一个socket都是阻塞的,所以在一个线程里只能处理一个socket,就算accept了多个也没用,前一个socket被阻塞了,后面的是无法被执行到的。</p><h4 id="多线程并发模式的缺点:"><a href="#多线程并发模式的缺点:" class="headerlink" title="多线程并发模式的缺点:"></a>多线程并发模式的缺点:</h4><p>缺点在于资源要求太高,系统中创建线程是需要比较高的系统资源的,如果连接数太高,系统无法承受,而且,线程的反复创建-销毁也需要代价。</p><h4 id="改进方法是:"><a href="#改进方法是:" class="headerlink" title="改进方法是:"></a>改进方法是:</h4><p>采用基于事件驱动的设计,当有事件触发时,才会调用处理器进行数据处理。使用Reactor模式,对线程的数量进行控制,一个线程处理大量的事件。</p><h3 id="单线程Reactor模式:"><a href="#单线程Reactor模式:" class="headerlink" title="单线程Reactor模式:"></a>单线程Reactor模式:</h3><p><strong>Reactor模型的朴素原型</strong></p><p>Java的NIO模式的Selector网络通讯,其实就是一个简单的Reactor模型。可以说是Reactor模型的朴素原型。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Server</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">testServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">// 1、获取Selector选择器</span> <span class="token class-name">Selector</span> selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2、获取通道</span> <span class="token class-name">ServerSocketChannel</span> serverSocketChannel <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3.设置为非阻塞</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4、绑定连接</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SOCKET_SERVER_PORT<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 5、将通道注册到选择器上,并注册的操作为:“接收”操作</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_ACCEPT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 6、采用轮询的方式,查询获取“准备就绪”的注册过的操作</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>selector<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 7、获取当前选择器中所有注册的选择键(“已经准备就绪的操作”)</span> <span class="token class-name">Iterator</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">SelectionKey</span><span class="token punctuation">></span></span> selectedKeys <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>selectedKeys<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 8、获取“准备就绪”的时间</span> <span class="token class-name">SelectionKey</span> selectedKey <span class="token operator">=</span> selectedKeys<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 9、判断key是具体的什么事件</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedKey<span class="token punctuation">.</span><span class="token function">isAcceptable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 10、若接受的事件是“接收就绪” 操作,就获取客户端连接</span> <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 11、切换为非阻塞模式</span> socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 12、将该通道注册到selector选择器上</span> socketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_READ<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedKey<span class="token punctuation">.</span><span class="token function">isReadable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 13、获取该选择器上的“读就绪”状态的通道</span> <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span>selectedKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 14、读取数据</span> <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> length <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>length <span class="token operator">=</span> socketChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">.</span><span class="token function">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span>length<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> byteBuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> socketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 15、移除选择键</span> selectedKeys<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 7、关闭连接</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token function">testServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>实际上的Reactor模式,是基于Java NIO的,在他的基础上,抽象出来两个组件——<strong>Reactor</strong>和<strong>Handler</strong>两个组件:</p><p>(1)Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理;新的事件包含连接建立就绪、读就绪、写就绪等。</p><p>(2)Handler:将自身(handler)与事件绑定,负责事件的处理,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel。</p><p>具体模型如图所示:</p><p><img src="https://i.loli.net/2021/08/23/5MeqSDK86lTgVAw.png" alt="screenShot.png"></p><p>《Scalable IO in Java》是这样描绘的,图中的accepter,看做是一种特殊的handler。</p><p><img src="https://i.loli.net/2021/08/23/rEI5lpGsaQFjcOL.png" alt="screenShot.png"></p><p><strong>参考代码:</strong></p><p><strong>Reactor:</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">InetSocketAddress</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span></span><span class="token class-name">ByteBuffer</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SelectionKey</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">Selector</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">ServerSocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Iterator</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Set</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Reactor</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span><span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token class-name">Selector</span> selector<span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">ServerSocketChannel</span> serverSocket<span class="token punctuation">;</span> <span class="token class-name">Reactor</span><span class="token punctuation">(</span><span class="token keyword">int</span> port<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">//Reactor初始化</span> selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serverSocket <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serverSocket<span class="token punctuation">.</span><span class="token function">socket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//非阻塞</span> serverSocket<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//分步处理,第一步,接收accept事件</span> <span class="token class-name">SelectionKey</span> sk <span class="token operator">=</span> serverSocket<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_ACCEPT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//attach callback object, Acceptor</span> sk<span class="token punctuation">.</span><span class="token function">attach</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Acceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">interrupted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> selector<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Set</span> selected <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Iterator</span> it <span class="token operator">=</span> selected<span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//Reactor负责dispatch收到的事件</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> selected<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span> k<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Runnable</span> r <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Runnable</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>k<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//调用之前注册的callback对象</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>r <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> r<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// inner class</span> <span class="token keyword">class</span> <span class="token class-name">Acceptor</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token class-name">SocketChannel</span> channel <span class="token operator">=</span> serverSocket<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>channel <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">new</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> channel<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>Handler:</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">com<span class="token punctuation">.</span>crazymakercircle<span class="token punctuation">.</span>config<span class="token punctuation">.</span></span><span class="token class-name">SystemConfig</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span></span><span class="token class-name">ByteBuffer</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SelectionKey</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">Selector</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SocketChannel</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Handler</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span><span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token class-name">SocketChannel</span> channel<span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">SelectionKey</span> sk<span class="token punctuation">;</span> <span class="token class-name">ByteBuffer</span> input <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>INPUT_SIZE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ByteBuffer</span> output <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SEND_SIZE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> READING <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> SENDING <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> state <span class="token operator">=</span> READING<span class="token punctuation">;</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span><span class="token class-name">Selector</span> selector<span class="token punctuation">,</span> <span class="token class-name">SocketChannel</span> c<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> channel <span class="token operator">=</span> c<span class="token punctuation">;</span> c<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Optionally try first read now</span> sk <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//将Handler作为callback对象</span> sk<span class="token punctuation">.</span><span class="token function">attach</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//第二步,注册Read就绪事件</span> sk<span class="token punctuation">.</span><span class="token function">interestOps</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_READ<span class="token punctuation">)</span><span class="token punctuation">;</span> selector<span class="token punctuation">.</span><span class="token function">wakeup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> <span class="token function">inputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> <span class="token function">outputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">process</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">==</span> READING<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">==</span> SENDING<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> channel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">inputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">process</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> state <span class="token operator">=</span> SENDING<span class="token punctuation">;</span> <span class="token comment">// Normally also do first write now</span> <span class="token comment">//第三步,接收write就绪事件</span> sk<span class="token punctuation">.</span><span class="token function">interestOps</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_WRITE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> channel<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>output<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//write完就结束了, 关闭select key</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">outputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> sk<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>单线程模式的缺点:</strong></p><p>1、 当其中某个 handler 阻塞时, 会导致其他所有的 client 的 handler 都得不到执行, 并且更严重的是, handler 的阻塞也会导致整个服务不能接收新的 client 请求(因为 acceptor 也被阻塞了)。 因为有这么多的缺陷, 因此单线程Reactor 模型用的比较少。这种单线程模型不能充分利用多核资源,所以实际使用的不多。</p><p>2、因此,单线程模型仅仅适用于handler 中业务处理组件能快速完成的场景。</p><h3 id="多线程的Reactor"><a href="#多线程的Reactor" class="headerlink" title="多线程的Reactor:"></a>多线程的Reactor:</h3><p><strong>在线程Reactor模式基础上,做如下改进:</strong></p><p>(1)将Handler处理器的执行放入线程池,多线程进行业务处理。</p><p>(2)而对于Reactor而言,可以仍为单个线程。如果服务器为多核的CPU,为充分利用系统资源,可以将Reactor拆分为两个线程。</p><p><strong>一个简单的图如下:</strong></p><p><img src="https://i.loli.net/2021/08/23/VxU37TKGtmlZ9vR.png" alt="screenShot.png"></p><p> <strong>改进后的完整示意图</strong></p><p>《Scalable IO in Java》中Reactor是一条独立的线程,Hander 处于线程池中执行。</p><p><img src="https://i.loli.net/2021/08/23/1bdTWGwotuChP9O.png" alt="screenShot.png"></p><p>它是基于单线程做一个线程池的改进,改进 的<strong>Handler</strong>代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">com<span class="token punctuation">.</span>crazymakercircle<span class="token punctuation">.</span>config<span class="token punctuation">.</span></span><span class="token class-name">SystemConfig</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span></span><span class="token class-name">ByteBuffer</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SelectionKey</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">Selector</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">ExecutorService</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">Executors</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">MthreadHandler</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span><span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token class-name">SocketChannel</span> channel<span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">SelectionKey</span> selectionKey<span class="token punctuation">;</span> <span class="token class-name">ByteBuffer</span> input <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>INPUT_SIZE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ByteBuffer</span> output <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SEND_SIZE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> READING <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> SENDING <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> state <span class="token operator">=</span> READING<span class="token punctuation">;</span> <span class="token class-name">ExecutorService</span> pool <span class="token operator">=</span> <span class="token class-name">Executors</span><span class="token punctuation">.</span><span class="token function">newFixedThreadPool</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> PROCESSING <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token class-name">MthreadHandler</span><span class="token punctuation">(</span><span class="token class-name">Selector</span> selector<span class="token punctuation">,</span> <span class="token class-name">SocketChannel</span> c<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> channel <span class="token operator">=</span> c<span class="token punctuation">;</span> c<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Optionally try first read now</span> selectionKey <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//将Handler作为callback对象</span> selectionKey<span class="token punctuation">.</span><span class="token function">attach</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//第二步,注册Read就绪事件</span> selectionKey<span class="token punctuation">.</span><span class="token function">interestOps</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_READ<span class="token punctuation">)</span><span class="token punctuation">;</span> selector<span class="token punctuation">.</span><span class="token function">wakeup</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> <span class="token function">inputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> <span class="token function">outputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">process</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">==</span> READING<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">==</span> SENDING<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> channel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">inputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> PROCESSING<span class="token punctuation">;</span> <span class="token comment">//使用线程pool异步执行</span> pool<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Processer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> channel<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>output<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//write完就结束了, 关闭select key</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">outputIsComplete</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> selectionKey<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">processAndHandOff</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">process</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> state <span class="token operator">=</span> SENDING<span class="token punctuation">;</span> <span class="token comment">// or rebind attachment</span> <span class="token comment">//process完,开始等待write就绪</span> selectionKey<span class="token punctuation">.</span><span class="token function">interestOps</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_WRITE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Processer</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">processAndHandOff</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="Reactor的改进:"><a href="#Reactor的改进:" class="headerlink" title="Reactor的改进:"></a>Reactor的改进:</h3><p>对于多个CPU的机器,为充分利用系统资源,将Reactor拆分为两部分。代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">InetSocketAddress</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">Socket</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SelectionKey</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">Selector</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">ServerSocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Iterator</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Set</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">MthreadReactor</span> <span class="token keyword">implements</span> <span class="token class-name">Runnable</span><span class="token punctuation">{</span> <span class="token comment">//subReactors集合, 一个selector代表一个subReactor</span> <span class="token class-name">Selector</span><span class="token punctuation">[</span><span class="token punctuation">]</span> selectors<span class="token operator">=</span><span class="token keyword">new</span> <span class="token class-name">Selector</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">int</span> next <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">ServerSocketChannel</span> serverSocket<span class="token punctuation">;</span> <span class="token class-name">MthreadReactor</span><span class="token punctuation">(</span><span class="token keyword">int</span> port<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">//Reactor初始化</span> selectors<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> selectors<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serverSocket <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> serverSocket<span class="token punctuation">.</span><span class="token function">socket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span>port<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//非阻塞</span> serverSocket<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//分步处理,第一步,接收accept事件</span> <span class="token class-name">SelectionKey</span> sk <span class="token operator">=</span> serverSocket<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span> selectors<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_ACCEPT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//attach callback object, Acceptor</span> sk<span class="token punctuation">.</span><span class="token function">attach</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Acceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">interrupted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span><span class="token number">2</span> <span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> selectors<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Set</span> selected <span class="token operator">=</span> selectors<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Iterator</span> it <span class="token operator">=</span> selected<span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//Reactor负责dispatch收到的事件</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>it<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> selected<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span> k<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Runnable</span> r <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Runnable</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>k<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//调用之前注册的callback对象</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>r <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> r<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Acceptor</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token keyword">public</span> <span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token class-name">SocketChannel</span> connection <span class="token operator">=</span> serverSocket<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//主selector负责accept</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>connection <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span>selectors<span class="token punctuation">[</span>next<span class="token punctuation">]</span><span class="token punctuation">,</span> connection<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//选个subReactor去负责接收到的connection</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">++</span>next <span class="token operator">==</span> selectors<span class="token punctuation">.</span>length<span class="token punctuation">)</span> next <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="Reactor的优缺点:"><a href="#Reactor的优缺点:" class="headerlink" title="Reactor的优缺点:"></a>Reactor的优缺点:</h3><p><strong>优点</strong></p><p>1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;</p><p>2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;</p><p>3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;</p><p>4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;</p><p> <strong>缺点</strong></p><p>1)相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。</p><p>2)Reactor模式需要底层的Synchronous Event Demultiplexer支持,比如Java中的Selector支持,操作系统的select系统调用支持,如果要自己实现Synchronous Event Demultiplexer可能不会有那么高效。</p><p>3) Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用改进版的Reactor模式如Proactor模式。</p>]]></content>
<categories>
<category> NIO </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 框架 </tag>
</tags>
</entry>
<entry>
<title>NIO基础三大件</title>
<link href="2021/08/22/NIO/NIO%E5%9F%BA%E7%A1%80/"/>
<url>2021/08/22/NIO/NIO%E5%9F%BA%E7%A1%80/</url>
<content type="html"><![CDATA[<h1 id="NIO基础三大件"><a href="#NIO基础三大件" class="headerlink" title="NIO基础三大件"></a>NIO基础三大件</h1><h4 id="Channel"><a href="#Channel" class="headerlink" title="Channel"></a><strong>Channel</strong></h4><p>OIO中,通常来说,一个连接使用有两个流,一个输入流一个输出流。通过两个流不断的进行输入和输出。</p><p>与之相对应,NIO中,通常来说,一个连接就是一个通道Channel表示,所有的 NIO 的 I/O 操作都是从 Channel 开始的。 一个 channel 类似于OIO中的两个 stream的结合体。</p><p>Java NIO中最重要的集中Channel的实现:</p><p>(1)FileChannel</p><p>(2)DatagramChannel</p><p>(3)SocketChannel</p><p>(4)ServerSocketChannel</p><p>四种通道的说明如下:</p><p>FileChannel用于文件的数据读写。</p><p>DatagramChannel用于UDP的数据读写。</p><p>SocketChannel用于TCP的数据读写。</p><p>ServerSocketChannel允许我们监听TCP连接请求,每个请求会创建会一个SocketChannel。</p><h4 id="selector"><a href="#selector" class="headerlink" title="selector"></a><strong>selector</strong></h4><p>通道Channel要进行多路复用,基础就是选择器selector。</p><p>这是一个IO事件的查询器,通过 Selector,一个线程可以查询多个 Channel 的 IO 事件的就绪状态。</p><p>我们要做的工作,就是将要进行状态查询的Channel(相当于流)注册到选择器Seletor中。当我们向一个 Selector 中注册了 Channel 后,Selector 内部的机制就可以自动地为我们不断地查询(select) 这些注册的 Channel 是否有已就绪的 IO 事件(例如可读,可写,网络连接完成等)。</p><p>通过这样的 Selector 机制,我们就可以很简单地使用一个线程高效地管理多个 Channel 了。</p><p>判断一个Channel 能被Selector 复用,有一个前提:判断他是否继承了一个抽象类SelectableChannel。如果继承了SelectableChannel,则可以被复用,否则不能。比如FileChannel。</p><h4 id="Buffer"><a href="#Buffer" class="headerlink" title="Buffer"></a><strong>Buffer</strong></h4><p>当我们需要与 NIO Channel 进行交互时,我们就需要使用到 NIO Buffer,即数据从 Buffer读取到 Channel 中,并且从 Channel 中写入到 Buffer 中。</p><p>Buffer的使用,也是NIO非阻塞的重要的前提和基础之一。</p><p>使用步骤:</p><p>一:将数据写入到 Buffer 中;</p><p>二:调用 Buffer.flip()方法,将 NIO Buffer 转换为读模式;</p><p>三:从 Buffer 中读取数据;</p><p>四:调用 Buffer.clear() 或 Buffer.compact()方法,将 Buffer 转换为写模式。</p><p>当我们将数据写入到 Buffer 中时,Buffer 会记录我们已经写了多少的数据;当我们需要从 Buffer 中读取数据时,必须调用 Buffer.flip()将 Buffer 切换为读模式。</p><h4 id="简单的例子"><a href="#简单的例子" class="headerlink" title="简单的例子"></a>简单的例子</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>crazymakercircle<span class="token punctuation">.</span>iodemo<span class="token punctuation">.</span>base</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">com<span class="token punctuation">.</span>crazymakercircle<span class="token punctuation">.</span>config<span class="token punctuation">.</span></span><span class="token class-name">SystemConfig</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>net<span class="token punctuation">.</span></span><span class="token class-name">InetSocketAddress</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span></span><span class="token class-name">ByteBuffer</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SelectionKey</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">Selector</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">ServerSocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>nio<span class="token punctuation">.</span>channels<span class="token punctuation">.</span></span><span class="token class-name">SocketChannel</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Iterator</span><span class="token punctuation">;</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SelectorDemo</span><span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">{</span> <span class="token comment">/** * 客户端 */</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">testClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token class-name">InetSocketAddress</span> address<span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SOCKET_SERVER_IP<span class="token punctuation">,</span> <span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SOCKET_SERVER_PORT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 1、获取通道(channel)</span> <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token class-name">SocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2、切换成非阻塞模式</span> socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3、分配指定大小的缓冲区</span> <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span> byteBuffer<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"hello world "</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> socketChannel<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span> socketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token function">testClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Server</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">testServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">// 1、获取Selector选择器</span> <span class="token class-name">Selector</span> selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2、获取通道</span> <span class="token class-name">ServerSocketChannel</span> serverSocketChannel <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3.设置为非阻塞</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4、绑定连接</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token class-name">SystemConfig</span><span class="token punctuation">.</span>SOCKET_SERVER_PORT<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 5、将通道注册到选择器上,并注册的操作为:“接收”操作</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_ACCEPT<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 6、采用轮询的方式,查询获取“准备就绪”的注册过的操作</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>selector<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 7、获取当前选择器中所有注册的选择键(“已经准备就绪的操作”)</span> <span class="token class-name">Iterator</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">SelectionKey</span><span class="token punctuation">></span></span> selectedKeys <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>selectedKeys<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 8、获取“准备就绪”的时间</span> <span class="token class-name">SelectionKey</span> selectedKey <span class="token operator">=</span> selectedKeys<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 9、判断key是具体的什么事件</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedKey<span class="token punctuation">.</span><span class="token function">isAcceptable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 10、若接受的事件是“接收就绪” 操作,就获取客户端连接</span> <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 11、切换为非阻塞模式</span> socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 12、将该通道注册到selector选择器上</span> socketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span>OP_READ<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectedKey<span class="token punctuation">.</span><span class="token function">isReadable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 13、获取该选择器上的“读就绪”状态的通道</span> <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> selectedKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 14、读取数据</span> <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> length <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>length <span class="token operator">=</span> socketChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">.</span><span class="token function">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> length<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> byteBuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> socketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 15、移除选择键</span> selectedKeys<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 7、关闭连接</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token function">testServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="NIO总结"><a href="#NIO总结" class="headerlink" title="NIO总结"></a>NIO总结</h4><p>(1)客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像之前的客户端那样被同步阻塞。</p><p>(2)SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样I/O通信线程就可以处理其他的链路,不需要同步等待这个链路可用。</p><p>(3)线程模型的优化:由于JDK的Selector在Linux等主流操作系统上通过epoll实现,它没有连接句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降。因此,它非常适合做高性能、高负载的网络服务器。</p>]]></content>
<categories>
<category> NIO </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 框架 </tag>
</tags>
</entry>
<entry>
<title>分布式ID之雪花算法</title>
<link href="2021/08/15/%E6%95%B0%E6%8D%AE%E5%BA%93/%E5%88%86%E5%B8%83%E5%BC%8FID%E4%B9%8B%E9%9B%AA%E8%8A%B1%E7%AE%97%E6%B3%95/"/>
<url>2021/08/15/%E6%95%B0%E6%8D%AE%E5%BA%93/%E5%88%86%E5%B8%83%E5%BC%8FID%E4%B9%8B%E9%9B%AA%E8%8A%B1%E7%AE%97%E6%B3%95/</url>
<content type="html"><![CDATA[<h1 id="雪花算法的概要"><a href="#雪花算法的概要" class="headerlink" title="雪花算法的概要"></a>雪花算法的概要</h1><h2 id="雪花算法的概要-1"><a href="#雪花算法的概要-1" class="headerlink" title="雪花算法的概要"></a>雪花算法的概要</h2><p><img src="https://i.loli.net/2021/08/15/R7FtkXYe8oG6h5l.png" alt="screenShot.png"></p><h3 id="组成部分(64bit)"><a href="#组成部分(64bit)" class="headerlink" title="组成部分(64bit)"></a>组成部分(64bit)</h3><p><strong>1.第一位</strong> 占用1bit,其值始终是0,没有实际作用。 </p><p><strong>2.时间戳</strong> 占用41bit,精确到毫秒,总共可以容纳约69年的时间。</p><p> <strong>3.工作机器id</strong> 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。 </p><p><strong>4.序列号</strong> 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。</p><p>SnowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢:: <strong>同一毫秒的ID数量 = 1024 X 4096 = 4194304</strong></p><h2 id="雪花算法的实现"><a href="#雪花算法的实现" class="headerlink" title="雪花算法的实现"></a>雪花算法的实现</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SnowflakeIdWorker</span> <span class="token punctuation">{</span> <span class="token comment">/** * 开始时间截 (2015-01-01) */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> twepoch <span class="token operator">=</span> <span class="token number">1420041600000L</span><span class="token punctuation">;</span> <span class="token comment">/** * 机器id所占的位数 */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> workerIdBits <span class="token operator">=</span> <span class="token number">5L</span><span class="token punctuation">;</span> <span class="token comment">/** * 数据标识id所占的位数 */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> datacenterIdBits <span class="token operator">=</span> <span class="token number">5L</span><span class="token punctuation">;</span> <span class="token comment">/** * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> maxWorkerId <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1L</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1L</span> <span class="token operator"><<</span> workerIdBits<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/** * 支持的最大数据标识id,结果是31 */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> maxDatacenterId <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1L</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1L</span> <span class="token operator"><<</span> datacenterIdBits<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/** * 序列在id中占的位数 */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> sequenceBits <span class="token operator">=</span> <span class="token number">12L</span><span class="token punctuation">;</span> <span class="token comment">/** * 机器ID向左移12位 */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> workerIdShift <span class="token operator">=</span> sequenceBits<span class="token punctuation">;</span> <span class="token comment">/** * 数据标识id向左移17位(12+5) */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> datacenterIdShift <span class="token operator">=</span> sequenceBits <span class="token operator">+</span> workerIdBits<span class="token punctuation">;</span> <span class="token comment">/** * 时间截向左移22位(5+5+12) */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> timestampLeftShift <span class="token operator">=</span> sequenceBits <span class="token operator">+</span> workerIdBits <span class="token operator">+</span> datacenterIdBits<span class="token punctuation">;</span> <span class="token comment">/** * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">long</span> sequenceMask <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1L</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1L</span> <span class="token operator"><<</span> sequenceBits<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/** * 工作机器ID(0~31) */</span> <span class="token keyword">private</span> <span class="token keyword">long</span> workerId<span class="token punctuation">;</span> <span class="token comment">/** * 数据中心ID(0~31) */</span> <span class="token keyword">private</span> <span class="token keyword">long</span> datacenterId<span class="token punctuation">;</span> <span class="token comment">/** * 毫秒内序列(0~4095) */</span> <span class="token keyword">private</span> <span class="token keyword">long</span> sequence <span class="token operator">=</span> <span class="token number">0L</span><span class="token punctuation">;</span> <span class="token comment">/** * 上次生成ID的时间截 */</span> <span class="token keyword">private</span> <span class="token keyword">long</span> lastTimestamp <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1L</span><span class="token punctuation">;</span> <span class="token comment">/** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */</span> <span class="token keyword">public</span> <span class="token class-name">SnowflakeIdWorker</span><span class="token punctuation">(</span><span class="token keyword">long</span> workerId<span class="token punctuation">,</span> <span class="token keyword">long</span> datacenterId<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>workerId <span class="token operator">></span> maxWorkerId <span class="token operator">||</span> workerId <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">"worker Id can't be greater than %d or less than 0"</span><span class="token punctuation">,</span> maxWorkerId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>datacenterId <span class="token operator">></span> maxDatacenterId <span class="token operator">||</span> datacenterId <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">"datacenter Id can't be greater than %d or less than 0"</span><span class="token punctuation">,</span> maxDatacenterId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>workerId <span class="token operator">=</span> workerId<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>datacenterId <span class="token operator">=</span> datacenterId<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * 获得下一个ID (该方法是线程安全的) * @return SnowflakeId */</span> <span class="token keyword">public</span> <span class="token keyword">synchronized</span> <span class="token keyword">long</span> <span class="token function">nextId</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> timestamp <span class="token operator">=</span> <span class="token function">timeGen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>timestamp <span class="token operator"><</span> lastTimestamp<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span> <span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string">"Clock moved backwards. Refusing to generate id for %d milliseconds"</span><span class="token punctuation">,</span> lastTimestamp <span class="token operator">-</span> timestamp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 如果是同一时间生成的,则进行毫秒内序列</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>lastTimestamp <span class="token operator">==</span> timestamp<span class="token punctuation">)</span> <span class="token punctuation">{</span> sequence <span class="token operator">=</span> <span class="token punctuation">(</span>sequence <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">&</span> sequenceMask<span class="token punctuation">;</span> <span class="token comment">// 毫秒内序列溢出</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>sequence <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//阻塞到下一个毫秒,获得新的时间戳</span> timestamp <span class="token operator">=</span> <span class="token function">tilNextMillis</span><span class="token punctuation">(</span>lastTimestamp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 时间戳改变,毫秒内序列重置</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> sequence <span class="token operator">=</span> <span class="token number">0L</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 上次生成ID的时间截</span> lastTimestamp <span class="token operator">=</span> timestamp<span class="token punctuation">;</span> <span class="token comment">// 移位并通过或运算拼到一起组成64位的ID</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>timestamp <span class="token operator">-</span> twepoch<span class="token punctuation">)</span> <span class="token operator"><<</span> timestampLeftShift<span class="token punctuation">)</span> <span class="token comment">//</span> <span class="token operator">|</span> <span class="token punctuation">(</span>datacenterId <span class="token operator"><<</span> datacenterIdShift<span class="token punctuation">)</span> <span class="token comment">//</span> <span class="token operator">|</span> <span class="token punctuation">(</span>workerId <span class="token operator"><<</span> workerIdShift<span class="token punctuation">)</span> <span class="token comment">//</span> <span class="token operator">|</span> sequence<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */</span> <span class="token keyword">protected</span> <span class="token keyword">long</span> <span class="token function">tilNextMillis</span><span class="token punctuation">(</span><span class="token keyword">long</span> lastTimestamp<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> timestamp <span class="token operator">=</span> <span class="token function">timeGen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>timestamp <span class="token operator"><=</span> lastTimestamp<span class="token punctuation">)</span> <span class="token punctuation">{</span> timestamp <span class="token operator">=</span> <span class="token function">timeGen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> timestamp<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */</span> <span class="token keyword">protected</span> <span class="token keyword">long</span> <span class="token function">timeGen</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">SnowflakeIdWorker</span> idWorker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SnowflakeIdWorker</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> id <span class="token operator">=</span> idWorker<span class="token punctuation">.</span><span class="token function">nextId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> MySQL数据库学习 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> MySQL </tag>
</tags>
</entry>
<entry>
<title>计算机网络知识点梳理</title>
<link href="2021/08/14/%E8%AE%A1%E7%BD%91+%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/"/>
<url>2021/08/14/%E8%AE%A1%E7%BD%91+%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/</url>
<content type="html"><![CDATA[<h1 id="计算机网络知识点梳理"><a href="#计算机网络知识点梳理" class="headerlink" title="计算机网络知识点梳理"></a>计算机网络知识点梳理</h1><h2 id="★★★-各层协议的作用,以及-TCP-IP-协议的特点"><a href="#★★★-各层协议的作用,以及-TCP-IP-协议的特点" class="headerlink" title="★★★ 各层协议的作用,以及 TCP/IP 协议的特点"></a>★★★ 各层协议的作用,以及 TCP/IP 协议的特点</h2><p><strong>应用层</strong>:任务是通过应⽤进程间的交互来完成特定⽹络应⽤</p><p><strong>传输层</strong>:任务是负责向两台主机进程之间的通信提供通⽤的数据传输服务</p><p><strong>网络层</strong>:任务是选择合适的⽹间路由和交换结点, 确保数据及时传送</p><p><strong>链路层</strong>:两台主机之间的数据传输,总是在⼀段⼀段的链路上传输送的,这就需要使⽤专⻔的链路层的协议</p><p><strong>TCP/IP 协议的特点:</strong></p><p>(1)协议标准是完全开放的,并且独立于特定的计算机硬件与操作系统。</p><p>(2)独立于网络硬件系统,可以运行在广域网,更适合于互联网。</p><p>(3)网络地址统一分配,网络中每一设备和终端都具有一个唯一地址。</p><p>(4)高层协议标准化,可以提供多种多样可靠网络服务。</p><h2 id="★★☆-以太网的特点,以及帧结构"><a href="#★★☆-以太网的特点,以及帧结构" class="headerlink" title="★★☆ 以太网的特点,以及帧结构"></a>★★☆ 以太网的特点,以及帧结构</h2><p><strong>特点:</strong>最初的以太网是将许多计算机都连接到 <strong>一根总线</strong> 上。高速有效,不用划分信道,但是不安全</p><p>在以太网链路上的数据包称作<strong>以太帧</strong></p><p>以太帧起始部分由前同步码和帧开始定界符组成,后面紧跟着一个以太网报头,以 MAC 地址说明目的地址和源地址。以太帧的中部是该帧负载的包含其他协议报头的数据包,如 IP 协议。</p><p>以太帧由一个 32 位冗余校验码结尾,用于检验数据传输是否出现损坏。以太帧结构如图所示</p><p><img src="https://i.loli.net/2021/08/15/aL8KwpQ1e5432cU.png" alt="screenShot.png"></p><h2 id="★★☆-集线器、交换机、路由器的作用,以及所属的网络层"><a href="#★★☆-集线器、交换机、路由器的作用,以及所属的网络层" class="headerlink" title="★★☆ 集线器、交换机、路由器的作用,以及所属的网络层"></a>★★☆ 集线器、交换机、路由器的作用,以及所属的网络层</h2><p><strong>集线器</strong>(Hub) —— 工作在物理层</p><p>信号在线路中传播会进行衰减,集线器的作用就是对信号进行再生放大,从而扩大了网络的传输距离</p><p><strong>交换机</strong>(Switch) —— 工作在数据链路层(<strong>交换机转发的消息叫做帧</strong>)</p><p>集线器的信道利用率太低了,所以就出现了交换机。交换机很很多个端口,每个端口都能够连接一台计算机,当计算机A向计算机B发送信息时,会在内部建立起一条临时性的数据传输通道,如果有多台计算机同时通信,那么就会维护多条通道。</p><p><strong>路由器</strong>(Router) —— 工作在网络层(<strong>路由器转发的消息叫做IP数据报</strong>)</p><p>交换机是工作在数据链路层的,也即交换机只能转发局域网内的帧。如果网络A的主机想要发消息给网络B的主机就需要路由器了。</p><p>如果网络A的主机1想要向网络B的主机2发送消息,那么主机1必须在IP数据报的首部加上源IP地址和目标IP地址,那么路由器A会根据目标IP地址将IP数据报转发到路由器B。<strong>路由器B接收到该IP数据报后,会将该IP数据报封装成帧,然后在帧的首部加上主机B的MAC地址作为目的地址,然后把该消息转发给主机B,或者转发给交换机,由交换机转发给主机B。</strong></p><p><strong>那么路由器B怎么知道某个IP地址对应了哪个主机,且该主机的MAC地址是什么呢?</strong></p><p>路由器内部维护了一张arp表,即地址解析协议表,它知道哪个IP地址对应了哪个MAC地址</p><h2 id="★★☆-TCP和IP报文头部"><a href="#★★☆-TCP和IP报文头部" class="headerlink" title="★★☆ TCP和IP报文头部"></a>★★☆ TCP和IP报文头部</h2><p><strong>IP协议</strong>是网络层的主要协议,为上层传输层提供无连接、无状态、不可靠的服务。优点是简单高效。无状态是指各个IP报文是独立传送的,不同步传输状态的信息,所以容易发生重复和乱序的情况。不可靠是指IP协议不能保证数据报一定能被送达,可靠性主要是通过传输层的TCP协议来保证的。</p><p><img src="https://i.loli.net/2021/08/15/SA7IxUeCv3P5coD.png" alt="screenShot.png"></p><p>IP头部通常有20字节,加上选项的话,最多不超过60字节。</p><p>4位版本:指IP协议版本号,是IPV4还是IPV6</p><p>4位首部长度:指IP头部的长度,以4字节为单位,最大值为(1111)b × 4 = 60字节</p><p>8位服务类型:包含一个4位优先权字段:最小延时,最大吞吐量,最高可靠性和最小费用。(暂时不清楚具体通途,待补充)</p><p>16位总长度:表示整个IP数据报的长度,最大为65535,但是由于有MTU的限制,一般达不到这个数值</p><p>16位标识:数据报的标识,系统采用边发送边赋值的方式</p><p>3位标志:分别为保留区,禁止分片标志,和更多分片标志。DF位设置时表示禁止分区,如果数据报文过大,可能会发送失败。</p><p>MF位表示后面还有报文段,如果报文分片的话,除了最后一个分片,其他的报文片都置1</p><p>13位片偏移:分片相对于原始报文片开始的偏移</p><p>8位生存时间:表示报文到达目的之前,允许经过的路由跳数,没经过1跳,TTL值减一,值减到0的时候,丢弃</p><p>8位协议:用来区分上层协议(例如TCP,UDP等)</p><p>16位首部校验和:以CRC算法校验数据在传输过程中头部是否损坏</p><p>32位源IP地址:发送端的IP地址</p><p>32位目的IP地址:目的IP地址</p><p> <strong>TCP协议</strong>是面向连接的协议,是工作在传输层的协议。TCP协议通过三次握手、四次挥手、流量控制、拥塞控制、超时重传、确认报文等机制来保证可靠性。</p><p><img src="https://i.loli.net/2021/08/15/8ZoApJksKXH57bw.png" alt="screenShot.png"></p><p>同IP头部类似,TCP头部通常也为20字节,带上选项部分,最大不超过60字节</p><p>16位源端口号:指发送端的端口号</p><p>16位目的端口号:指目的端的端口号</p><p>4位头部长度:同IP头部,表示TCP头部的大小,以4字节为单位。</p><p>32位序列号:TCP通信过程中,通过序列号来保证传输过程中数据的有序性</p><p>32位确认号:用以对接受到的报文进行确认</p><p>保留6位</p><p>URG:表示紧急指针</p><p>ACK:表示确认号</p><p>PSH:通知对端立即从缓冲区取走数据</p><p>SYN:表示请求建立连接</p><p>FIN:标志要通知对端本端的数据发送要关闭</p><p>16位窗口大小:TCP流量控制的手段,告诉发送方本端的接收端缓冲区还能接受多少数据</p><p>16位校验和:由发送方填充,接收端用CRC校验算法,用以检查TCP报文在传输过程中是否有损坏</p><h2 id="★☆☆-ARP-协议的作用,以及维护-ARP-缓存的过程"><a href="#★☆☆-ARP-协议的作用,以及维护-ARP-缓存的过程" class="headerlink" title="★☆☆ ARP 协议的作用,以及维护 ARP 缓存的过程"></a>★☆☆ ARP 协议的作用,以及维护 ARP 缓存的过程</h2><p>在 Linux 系统中,我们可以使⽤ arp -a 命令来查看 ARP 缓存的内容。</p><p>通过目的IP地址而获取目的MAC地址的过程是由ARP协议来实现的。</p><p>APR缓存用来存放IP地址和MAC地址的关联信息。在发送数据前,设备会先查找ARP缓存表。如果缓存表中存在对方设备的MAC地址,则直接采用该MAC地址来封装帧,然后在发出去。如果缓存表中不存在相应信息,则通过发送ARP request报文来获得它。学习到的IP地址和MAC地址的映射关系会被放入ARP缓存表中存放一段时间。在有效期内,设备可以直接从这个表中查找目的MAC地址来进行数据封装,而无需进行ARP查询。过了这段有效期,ARP就会被自动删除。</p><h2 id="★★★-UDP-与-TCP-比较,分析上层协议应该使用-UDP-还是-TCP"><a href="#★★★-UDP-与-TCP-比较,分析上层协议应该使用-UDP-还是-TCP" class="headerlink" title="★★★ UDP 与 TCP 比较,分析上层协议应该使用 UDP 还是 TCP"></a>★★★ UDP 与 TCP 比较,分析上层协议应该使用 UDP 还是 TCP</h2><h2 id="★★★-理解三次握手以及四次挥手具体过程,状态转移。三次握手的原因、四次挥手原因、TIME-WAIT、CLOSE-WAIT-的意义"><a href="#★★★-理解三次握手以及四次挥手具体过程,状态转移。三次握手的原因、四次挥手原因、TIME-WAIT、CLOSE-WAIT-的意义" class="headerlink" title="★★★ 理解三次握手以及四次挥手具体过程,状态转移。三次握手的原因、四次挥手原因、TIME_WAIT、CLOSE_WAIT 的意义"></a>★★★ 理解三次握手以及四次挥手具体过程,状态转移。三次握手的原因、四次挥手原因、TIME_WAIT、CLOSE_WAIT 的意义</h2><p><strong>三次握手的过程:</strong></p><p><img src="https://i.loli.net/2021/08/15/zdageNLAUiQ9JsF.png" alt="clip_image002.png"></p><p>第一次握手:客户端发起建立连接请求,并发送SYN和一个序列号i</p><p>第二次握手:服务器确认与客户端建立连接,发送SY N和一个自己的序列号j,同时发送ACK以及确认值i+1</p><p>第三次握手:客户端向服务器发送ACK以及确认值j+1</p><p><strong>原因:</strong>通过三次握⼿能防⽌历史连接的建⽴,能减少双⽅不必要的资源开销,能帮助双⽅同步初始化序列号</p><p>在网络拥堵的情况下⼀个「旧 SYN 报⽂」⽐「最新的 SYN 」 报⽂早到达了服务端; 那么此时服务端就会回⼀个 SYN + ACK 报⽂给客户端; 客户端收到后可以根据⾃身的上下⽂,判断这是⼀个历史连接(序列号过期或超时),那么第三次握手客户端就会发送 RST 报⽂给服务端,表示中⽌这⼀次连接</p><p><strong>四次挥手的过程:</strong></p><p><img src="https://i.loli.net/2021/08/15/ENRmxj6qFzgGVTQ.png" alt="clip_image004.png"></p><p>第一次挥手:主动断开方要求关闭连接,并且发送FIN</p><p>第二次挥手:被动断开方确认关闭连接发送ACK和确认值i+2</p><p>第三次挥手:被动断开方紧接着发送FIN</p><p>第四次挥手:主动断开方发送ACK和确认值J+2表示确认</p><p><strong>原因:</strong>服务端需要继续发送数据,所以服务端的 ACK 和 FIN ⼀般都会分开发送,从⽽⽐三次握⼿导致多了⼀次</p><p><strong>状态转移过程:</strong></p><p>第三次握⼿是可以携带数据的,前两次握⼿是不可以携带数据的</p><p><img src="https://i.loli.net/2021/08/15/wLVj14RhUCKGbJx.png" alt="clip_image006.png"></p><p> <strong>TIME_WAIT</strong>:</p><p>主动关闭方在收到被动关闭方的<code>FIN</code>包后并返回<code>ACK</code>后,会进入<code>TIME_WAIT</code>状态,<code>TIME_WAIT</code>状态又称<code>2MSL</code>状态</p><p><strong>为什么是维持2MSL</strong></p><ul><li>一个<code>MSL</code>是确保主动关闭方最后的<code>ACK</code>能够到达对端。</li><li>一个<code>MSL</code>是确保被动关闭方重发的<code>FIN</code>能够被主动关闭方收到。</li></ul><p><strong>存在的意义:</strong></p><p>(1)保证可靠的终止TCP连接(2)保证迟来的数据能被识别并丢弃</p><p><strong>优化:</strong>调整系统内核参数 调整短链接为长链接</p><p><strong>CLOSE_WAIT:</strong></p><p>在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。</p><p> 出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。解决方法基本的思想就是要检测出对方已经关闭的socket。</p><h2 id="★★★-可靠传输原理,并设计可靠-UDP-协议"><a href="#★★★-可靠传输原理,并设计可靠-UDP-协议" class="headerlink" title="★★★ 可靠传输原理,并设计可靠 UDP 协议"></a>★★★ 可靠传输原理,并设计可靠 UDP 协议</h2><p><strong>TCP可靠传输:</strong>确认机制、重传机制、滑动窗口。</p><p><strong>可靠性</strong></p><p> 1.应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据长度将保持不变。由TCP传递给IP的信息单位称为报文段或段(segment)。</p><p>2.当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。当TCP收到发自TCP连接另一端的数据,它将发送一个确认。TCP有延迟确认的功能,在此功能没有打开,则是立即确认。功能打开,则由定时器触发确认时间点。</p><p>3.TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。</p><p>4.既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。</p><p>5.既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。</p><p>6.TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。</p><p><strong>重传策略</strong></p><p>TCP协议用于控制数据段是否需要重传的依据是设立重发定时器。在发送一个数据段的同时启动一个重传,如果在重传超时前收到确认(Acknowlegement)就关闭该重传,如果重传超时前没有收到确认,则重传该数据段。在选择重发时间的过程中,TCP必须具有自适应性。它需要根据互联网当时的通信情况,给出合适的重发时间。</p><p>这种重传策略的关键是对定时器初值的设定。采用较多的算法是Jacobson于1988年提出的一种不断调整超时时间间隔的动态算法。其工作原理是:对每条连接TCP都保持一个变量RTT(Round Trip Time),用于存放当前到目的端往返所需要时间最接近的估计值。当发送一个数据段时,同时启动连接的定时器,如果在定时器超时前确认到达,则记录所需要的时间(M),并修正[2] RTT的值,如果定时器超时前没有收到确认,则将RTT的值增加1倍。通过测量一系列的RTT(往返时间)值,TCP协议可以估算数据包重发前需要等待的时间。在估计该连接所需的当前延迟时通常利用一些统计学的原理和算法(如Karn算法),从而得到TCP重发之前需要等待的时间值。</p><p><strong>窗口确认</strong></p><p>TCP的一项功能就是确保每个数据段都能到达目的地。位于目的主机的TCP服务对接受到的数据进行确认,并向源应用程序发送确认信息。使用数据报头序列号以及确认号来确认已收到包含在数据段的相关的数据字节。</p><p>TCP在发回源设备的数据段中使用确认号,指示接收设备期待接收的下一字节。这个过程称为期待确认。</p><p>源主机在收到确认消息之前可以传输的数据的大小称为窗口大小。用于管理丢失数据和流量控制。</p><p><strong>UDP可靠传输:</strong></p><p>传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。</p><p>最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。</p><ul><li><p>1、添加seq/ack机制,确保数据发送到对端</p></li><li><p>2、添加发送和接收缓冲区,主要是用户超时重传。</p></li><li><p>3、添加超时重传机制。</p><p><strong>详细说明:</strong>发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。</p></li></ul><h2 id="★★☆-TCP-滑动窗口、拥塞控制的作用,理解具体原理"><a href="#★★☆-TCP-滑动窗口、拥塞控制的作用,理解具体原理" class="headerlink" title="★★☆ TCP 滑动窗口、拥塞控制的作用,理解具体原理"></a>★★☆ TCP 滑动窗口、拥塞控制的作用,理解具体原理</h2><p><strong>滑动窗口:</strong></p><p>首先当然是可靠性,其次是传输效率,最后是稳定性</p><p><img src="https://i.loli.net/2021/08/15/ZW1gd4i2Qp9slP3.png" alt="943267-20190304193946550-910449223.png"></p><ol><li>窗口大小是指无需等待确认就可以继续发送数据的最大值,上图的窗口大小是4000字节(4段)</li><li>发送前4段时,无需ACK,直接发送</li><li>收到第一个ACK后,滑动窗口向后移动,继续发送第五段的数据</li><li>操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有应答的数据才会从缓冲区中删除</li><li>窗口越大,则网络的吞吐率就越高</li></ol><p><strong>拥塞控制:</strong></p><p>前⾯的流量控制是避免「发送⽅」的数据填满「接收⽅」的缓存,但是并不知道⽹络的中发⽣了什么。</p><p>在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是 ⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环被不断地放 ⼤。所以,TCP 不能忽略⽹络上发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低 发送的数据量</p><p>当网络出现拥塞就会发生数据包重传,分为两种:超时重传和快速重传</p><p><img src="https://i.loli.net/2021/08/15/9VCtB4ei3gJXzIn.png" alt="K8PQT@D5E_0_DRIPYMHS5X6.png"></p><p><strong>快速重传:</strong></p><p><img src="https://i.loli.net/2021/08/15/q3pDml65YMhorzX.png" alt="screenShot.png"></p><h2 id="★★☆-DNS-的端口号;TCP-还是-UDP;作为缓存、负载均衡"><a href="#★★☆-DNS-的端口号;TCP-还是-UDP;作为缓存、负载均衡" class="headerlink" title="★★☆ DNS 的端口号;TCP 还是 UDP;作为缓存、负载均衡"></a>★★☆ DNS 的端口号;TCP 还是 UDP;作为缓存、负载均衡</h2><p>DNS端口号:53</p><p>DNS在进行区域传输的时候使用TCP协议,其它时候则使用UDP协议;</p><p><strong>DNS缓存</strong>:有dns的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对DNS结果做一定程度的缓存。</p><p>DNS查询过程如下:</p><ol><li>首先搜索浏览器自身的DNS缓存,如果存在,则域名解析到此完成。</li><li>如果浏览器自身的缓存里面没有找到对应的条目,那么会尝试读取操作系统的hosts文件看是否存在对应的映射关系,如果存在,则域名解析到此完成。</li><li>如果本地hosts文件不存在映射关系,则查找本地DNS服务器(ISP服务器,或者自己手动设置的DNS服务器),如果存在,域名到此解析完成。</li><li>如果本地DNS服务器还没找到的话,它就会向根服务器发出请求,进行递归查询。</li></ol><p> <strong>DNS负载均衡</strong>采用的是简单的轮询算法,不能区分服务器的差异,不能反映服务器的当前运行状态,不能做到为性能较好的服务器多分配请求,甚至会出现客户请求集中在某一台服务器上的情况。</p><h2 id="★★★-GET-与-POST-比较:作用、参数、安全性、幂等性、可缓存"><a href="#★★★-GET-与-POST-比较:作用、参数、安全性、幂等性、可缓存" class="headerlink" title="★★★ GET 与 POST 比较:作用、参数、安全性、幂等性、可缓存"></a>★★★ GET 与 POST 比较:作用、参数、安全性、幂等性、可缓存</h2><p><strong>Get ⽅法</strong>的作用是请求从服务器获取资源,不会「破坏」服务器上的资源是安全的,因为它是「只读」操作,⽆论操作多少次,服务器上的数据都是安全 的,且每次的结果都是相同的,可以缓存</p><p> <strong>POST ⽅法</strong>作用是向 URI 指定的资源提交数据,数据就放在报⽂的 body ⾥,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多 个资源,所以不是幂等的,不可以缓存</p><h2 id="★★☆HTTP-状态码"><a href="#★★☆HTTP-状态码" class="headerlink" title="★★☆HTTP 状态码"></a>★★☆HTTP 状态码</h2><p><strong>1xx 类</strong>状态码属于提示信息,是协议处理中的⼀种中间状态,实际⽤到的⽐较少</p><p><strong>2xx 类</strong>状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态</p><pre class="line-numbers language-none"><code class="language-none">「200 OK」是最常⻅的成功状态码,表示⼀切正常。如果是⾮ HEAD 请求,服务器返回的响应头都会有 body 数据「204 No Content」也是常⻅的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据「206 Partial Content」是应⽤于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,⽽ 是其中的⼀部分,也是服务器处理成功的状态<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>3xx 类</strong>状态码表示客户端请求的资源发送了变动,需要客户端⽤新的 URL 重新发送请求获取资源,也就是重定向</p><pre class="line-numbers language-none"><code class="language-none">「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改⽤新的 URL 再次访问「302 Found」表示临时重定向,说明请求的资源还在,但暂时需要⽤另⼀个 URL 来访问301 和 302 都会在响应头⾥使⽤字段 Location ,指明后续要跳转的 URL,浏览器会⾃动重定向新的 URL。 「304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲⽂件,也称缓存重定向,⽤于缓 存控制<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>4xx 类</strong>状态码表示客户端发送的报⽂有误,服务器⽆法处理,也就是错误码的含义</p><pre class="line-numbers language-none"><code class="language-none">「400 Bad Request」表示客户端请求的报⽂有错误,但只是个笼统的错误「403 Forbidden」表示服务器禁⽌访问资源,并不是客户端的请求出错「404 Not Found」表示请求的资源在服务器上不存在或未找到,所以⽆法提供给客户端<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>5xx 类状态码表示客户端请求报⽂正确,但是服务器处理时内部发⽣了错误,属于服务器端的错误码</p><pre class="line-numbers language-none"><code class="language-none">「500 Internal Server Error」与 400 类型,是个笼统通⽤的错误码,服务器发⽣了什么错误,我们并不知道「501 Not Implemented」表示客户端请求的功能还不⽀持,类似“即将开业,敬请期待”的意思「502 Bad Gateway」通常是服务器作为⽹关或代理时返回的错误码,表示服务器⾃身⼯作正常,访问后端服务器 发⽣了错误「503 Service Unavailable」表示服务器当前很忙,暂时⽆法响应服务器,类似“⽹络服务正忙,请稍后重试”的意 思<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="★★★-Cookie-作用、安全性问题、和-Session-的比较"><a href="#★★★-Cookie-作用、安全性问题、和-Session-的比较" class="headerlink" title="★★★ Cookie 作用、安全性问题、和 Session 的比较"></a>★★★ Cookie 作用、安全性问题、和 Session 的比较</h2><p><strong>是什么</strong></p><p>Cookie是服务的发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后的请求中发送给服务器,用户告知两个请求来自同一浏览器。</p><p><strong>作用</strong></p><p>HTTP请求是无状态的,使用Cookie可以用来保存状态信息。<br>例如可以用来保存Session ID</p><p>Cookie曾用作客户端数据的存储,但现在新的浏览器已经支持各种方式的本地存储,如storage API或Indexed DB,Cookie已经被淘汰了。</p><p><strong>创建过程</strong></p><p>服务器发送的相应报文包含Set-Cookie首部字段,客户端得到相应报文后把Cookie内容保存到浏览器中。</p><p><strong>生命周期</strong></p><p>可对Cookie设置过期时间,若不设置则默认为浏览器会话期间,关闭浏览器窗口,cookie就消失。</p><p><strong>安全性</strong></p><p>设置Secure=true的Cookie只能通过Https协议发送。</p><p><strong>Cookie和Session的比较</strong></p><p>与Cookie不同Session是保存在服务器上的。通过名为JSESSIONID的Cookie保存session id。</p><p>1.存取方式不同</p><p>Cookie只能存取ASCII字符串<br>Session能够存取任何类型的数据,如Java对象等</p><p>2.隐私策略不同</p><p>Cookie存储在浏览器中,对浏览器是可见的,客户端的一些程序可以查看、复制甚至修改Cookie中的内容。</p><p>而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露风险</p><p>假如使用Cookie存储账号密码等信息,最好进行加密后再存储,提交到服务器后再进行解密。</p><p>3有效期不同</p><p>Cookie可设置过期时间。默认是浏览器关闭。</p><p>Session依赖于名为JSESSIONID的cookie,而该Cookie的过期时间为-1,表示只要关了浏览器就过期,而该服务器上的对应session也就失效了。假如设置session的超时时间过程,服务器累计的Session就会越多,越容易招致内存溢出</p><p>4.服务器压力不同</p><p>Session保存在服务器上,每个用户都会产生一个session,假如并发用户十分多,会产生十分多的session,消耗大量的内存。</p><p>Cookie保存在客户端,不占用服务器资源。</p><p>5.浏览器支持不同</p><p>Cookie是需要浏览器支持的。假如浏览器禁用了cookie,则不能使用cookie</p><p>6.跨域支持上的不同</p><p>Cookie支持跨域访问。Session不支持</p><h2 id="★★☆-缓存-的-Cache-Control-字段,特别是-Expires-和-max-age-的区别。ETag-验证原理"><a href="#★★☆-缓存-的-Cache-Control-字段,特别是-Expires-和-max-age-的区别。ETag-验证原理" class="headerlink" title="★★☆ 缓存 的 Cache-Control 字段,特别是 Expires 和 max-age 的区别。ETag 验证原理"></a>★★☆ 缓存 的 Cache-Control 字段,特别是 Expires 和 max-age 的区别。ETag 验证原理</h2><p>Http协议的cache-control的常见取值及其组合释义:<br>no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器.<br>no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源).<br>private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.<br>public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等.<br>max-age: 相对过期时间, 即以秒为单位的缓存时间.<br>no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器.</p><ul><li> private, 正数的max-age: 后退时候不会访问服务器.</li><li> no-cache, 正数的max-age: 后退时会访问服务器.</li></ul><p><strong>Expires</strong></p><p>给出的日期/时间后,被响应认为是过时。如Expires:Thu, 02 Apr 2009 05:14:08 GMT</p><p>需和Last-Modified结合使用。用于控制请求文件的有效时间,当请求数据在有效期内时客户端浏览器从缓存请求数据而不是服务器端.当缓存中数据失效或过期,才决定从服务器更新数据。</p><p>Expires指定的时间可以是相对文件的最后访问时间(Atime)或者修改时间(MTime),而max-age相对的是文档的请求时间(Atime)</p><p><strong>Etag工作原理</strong></p><p>HTTP协议规格说明定义ETag为“被请求变量的实体标记”(参见14.19)。简单点即服务器响应时给请求URL标记,并在HTTP响应头中将其传送到客户端,类似服务器端返回的格式:</p><p>Etag:“5d8c72a5edda8d6a:3239″</p><p>客户端的查询更新格式是这样的:</p><p>If-None-Match:“5d8c72a5edda8d6a:3239″</p><p>如果ETag没改变,则返回状态304。</p><p>即:在客户端发出请求后,HttpReponse Header中包含Etag:“5d8c72a5edda8d6a:3239″</p><p>标识,等于告诉Client端,你拿到的这个的资源有表示ID:5d8c72a5edda8d6a:3239。当下次需要发Request索要同一个URI的时候,浏览器同时发出一个If-None-Match报头(Http RequestHeader)此时包头中信息包含上次访问得到的Etag:“5d8c72a5edda8d6a:3239″标识。</p><p>If-None-Match:“5d8c72a5edda8d6a:3239“,这样,Client端等于Cache了两份,服务器端就会比对2者的etag。如果If-None-Match为False,不返回200,返回304(Not Modified) Response。</p><h2 id="★★★-长连接与短连接原理以及使用场景,流水线(管道)"><a href="#★★★-长连接与短连接原理以及使用场景,流水线(管道)" class="headerlink" title="★★★ 长连接与短连接原理以及使用场景,流水线(管道)"></a>★★★ 长连接与短连接原理以及使用场景,流水线(管道)</h2><p><strong>短连接</strong></p><pre class="line-numbers language-none"><code class="language-none">连接->传输数据->关闭连接比如HTTP是无状态的的短链接,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。因为连接后接收了数据就断开了,所以每次数据接受处理不会有联系。 这也是HTTP协议无状态的原因之一。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p><strong>长连接</strong></p><pre class="line-numbers language-none"><code class="language-none">连接->传输数据->保持连接 -> 传输数据-> ...........->直到一方关闭连接,多是客户端关闭连接。长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>流水线(PipeLine)是在同一条长连接上发出连续的请求,而不用等待应答返回。这样可以避免连接延迟。</strong></p><h2 id="★★★-HTTP-1-1-的特性"><a href="#★★★-HTTP-1-1-的特性" class="headerlink" title="★★★ HTTP/1.1 的特性"></a>★★★ HTTP/1.1 的特性</h2><ol><li>使⽤ TCP ⻓连接的⽅式改善了 HTTP/1.0 短连接造成的性能开销。 </li><li>⽀持管道(pipeline)⽹络传输,只要第⼀个请求发出去了,不必等其回来,就可以发第⼆个请求出去,可以 减少整体的响应时间</li></ol><h2 id="★★☆-HTTP-1-x-的缺陷,以及-HTTP-2-,HTTP-3的特点"><a href="#★★☆-HTTP-1-x-的缺陷,以及-HTTP-2-,HTTP-3的特点" class="headerlink" title="★★☆ HTTP/1.x 的缺陷,以及 HTTP/2 ,HTTP/3的特点"></a>★★☆ HTTP/1.x 的缺陷,以及 HTTP/2 ,HTTP/3的特点</h2><ul><li>请求 / 响应头部(Header)未经压缩就发送,⾸部信息越多延迟越⼤。只能压缩 Body 的部分; </li><li>发送冗⻓的⾸部。每次互相发送相同的⾸部造成的浪费较多; </li><li>服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端⼀直请求不到数据,也就是队头阻塞; </li><li>没有请求优先级控制; 请求只能从客户端开始,服务器只能被动响应</li></ul><p><strong>HTTP/2 相⽐ HTTP/1.1 性能上的改进:</strong></p><ol><li>头部压缩: 消除重复的部分,在客户端和服务器同时维护⼀张头信息表,所有字段都会存⼊这个表,⽣成⼀个索 引号,以后就不发送同样字段了,只发送索引号,这样就提⾼速度了。</li><li>⼆进制格式:收到报⽂后,⽆需再将明⽂的报⽂转 成⼆进制,⽽是直接解析⼆进制报⽂,这增加了数据传输的效率</li><li>数据流:HTTP/2 的数据包不是按顺序发送的,同⼀个连接⾥⾯连续的数据包,可能属于不同的回应。因此,必须要对数据 包做标记,指出它属于哪个回应。</li><li>多路复⽤:移除了 HTTP/1.1 中的串⾏请求,不需要排队等待,也就不会再出现「队头阻塞」问题,降低了延迟,⼤幅度提⾼ 了连接的利⽤率。</li></ol><p><strong>HTTP/2 的缺陷?HTTP/3 做了哪些优化?</strong></p><p> HTTP/2 多个请求复⽤⼀个TCP连接,⼀旦发⽣丢包,就会阻塞住所有的 HTTP 请求</p><p> HTTP/3 使用QUIC协议实现类似 TCP 的可靠性传输,当某个流发⽣丢包时,只会阻塞这个流,其他流不会受到 影响。</p><p> TLS3 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPack 。</p><p> HTTPS 要建⽴⼀个连接,要花费 6 次交互,先是建⽴三次握⼿,然后是 TLS/1.3 的三次握⼿。QUIC 直接 把以往的 TCP 和 TLS/1.3 的 6 次交互合并成了 3 次,减少了交互次数。</p><h2 id="★★☆-HTTP-与-FTP-的比较"><a href="#★★☆-HTTP-与-FTP-的比较" class="headerlink" title="★★☆ HTTP 与 FTP 的比较"></a>★★☆ HTTP 与 FTP 的比较</h2><p><strong>1. 同:</strong> </p><p> (1)都是应用层协议; </p><p> (2)都运行在TCP上,即都使用TCP(而不是UDP)作为其支撑的运输层协议。 </p><p> <strong>2. 异:</strong> </p><p> (1)HTTP是超文本传输协议,是面向网页的;FTP是文件传输协议,是面向文件的。 </p><p> (2)HTTP协议默认端口:80号端口。FTP协议默认端口:21号端口。 </p><p> (3)FTP的控制信息是带外(out-of-band)传送的,而HTTP的控制信息是带内(in-band)传送的。 </p><p> FTP使用两个并行的TCP连接来传输文件,一个是 <strong>控制连接</strong>(control connection),一个是 <strong>数据连接</strong>(data connection)。 </p><p> 控制连接用于在两个主机之间传输控制信息,如用户标识、口令、改变远程目录的命令以及“put”和“get”文件的命令。 </p><p> 数据连接用于实际传输一个文件。 </p><p> <img src="https://uploadfiles.nowcoder.com/images/20210410/613861755_1618026857945_D0879CE15FE8803BB612A6C55B261D56" alt="img"></p><p> 因为FTP协议使用一个分离的控制连接,因此称FTP的控制信息是带外传送的。<br> 而HTTP协议是在传输文件的TCP连接中发送请求和响应首部行的,因此其控制信息是带内传送的。 </p><p> 因为FTP协议使用一个分离的控制连接,因此称FTP的控制信息是带外传送的。<br> 而HTTP协议是在传输文件的TCP连接中发送请求和响应首部行的,因此其控制信息是带内传送的。 </p><p> (4)FTP服务器必须在整个会话期间保留用户的状态(state)信息,而HTTP是无状态的。 </p><p> FTP服务器必须把特定的用户账户与控制连接联系起来,随着用户在远程目录树上移动,服务器必须追踪用户在远程目录树上的当前位置。对每个活动着的用户会话的状态进行追踪,可以对FTP会话总数进行限制。 </p><p> (5)FTP的控制连接是持久连接,数据连接是非持久连接;而HTTP既可以使用非持久连接,也可以使用持久连接,默认方式下,HTTP使用持久连接。 </p><h2 id="★★☆-五种-IO-模型的特点以及比较"><a href="#★★☆-五种-IO-模型的特点以及比较" class="headerlink" title="★★☆ 五种 IO 模型的特点以及比较"></a>★★☆ 五种 IO 模型的特点以及比较</h2><p><strong>阻塞式IO</strong></p><ul><li>使用系统调用,并一直阻塞直到内核将数据准备好,之后再由内核缓冲区复制到用户态,在等待内核准备的这段时间什么也干不了</li><li>下图函数调用期间,一直被阻塞,直到数据准备好且从内核复制到用户程序才返回,这种IO模型为阻塞式IO</li><li>阻塞式IO式最流行的IO模型 </li></ul><p><img src="https://i.loli.net/2021/08/15/Xyu2lk4UOsxrYAE.png" alt="AG9SF_~4B_JLLZF@LZXCYN9.png"></p><p><strong>非阻塞式IO</strong></p><ul><li>内核在没有准备好数据的时候会返回错误码,而调用程序不会休眠,而是不断轮询询问内核数据是否准备好</li><li>下图函数调用时,如果数据没有准备好,不像阻塞式IO那样一直被阻塞,而是返回一个错误码。数据准备好时,函数成功返回。</li><li>应用程序对这样一个非阻塞描述符循环调用成为轮询。</li><li>非阻塞式IO的轮询会耗费大量cpu,通常在专门提供某一功能的系统中才会使用。通过为套接字的描述符属性设置非阻塞式,可使用该功能 </li></ul><p><img src="https://i.loli.net/2021/08/15/PzS5VFK6gZwrtlW.png" alt="HH__Q_Q_9@UFJ_5T6H1J8_3.png"></p><p><strong>IO多路复用</strong></p><ul><li><p>类似与非阻塞,只不过轮询不是由用户线程去执行,而是由内核去轮询,内核监听程序监听到数据准备好后,调用内核函数复制数据到用户态</p></li><li><p>下图中select这个系统调用,充当代理类的角色,不断轮询注册到它这里的所有需要IO的文件描述符,有结果时,把结果告诉被代理的recvfrom函数,它本尊再亲自出马去拿数据</p></li><li><p>IO多路复用至少有两次系统调用,如果只有一个代理对象,性能上是不如前面的IO模型的,但是由于它可以同时监听很多套接字,所以性能比前两者高 </p></li><li><p>多路复用包括: </p><ul><li>select:线性扫描所有监听的文件描述符,不管他们是不是活跃的。有最大数量限制(32位系统1024,64位系统2048)</li><li>poll:同select,不过数据结构不同,需要分配一个pollfd结构数组,维护在内核中。它没有大小限制,不过需要很多复制操作</li><li>epoll:用于代替poll和select,没有大小限制。使用一个文件描述符管理多个文件描述符,使用红黑树存储。同时用事件驱动代替了轮询。epoll_ctl中注册的文件描述符在事件触发的时候会通过回调机制激活该文件描述符。epoll_wait便会收到通知。最后,epoll还采用了mmap虚拟内存映射技术减少用户态和内核态数据传输的开销</li></ul><p><img src="https://i.loli.net/2021/08/15/DWf7A4wZ58FEVxX.png" alt="wwzxb8i6h7.png"></p></li></ul><p><strong>信号驱动式IO</strong></p><ul><li><p>使用信号,内核在数据准备就绪时通过信号来进行通知</p></li><li><p>首先开启信号驱动io套接字,并使用sigaction系统调用来安装信号处理程序,内核直接返回,不会阻塞用户态</p></li><li><p>数据准备好时,内核会发送SIGIO信号,收到信号后开始进行io操作 </p><p><img src="https://i.loli.net/2021/08/15/qDR2YzIubMi8Xmk.png" alt="~_K_X_Y_25EE_ODFHF_GL7Y.png"></p></li></ul><p><strong>异步IO</strong></p><ul><li>异步IO依赖信号处理程序来进行通知</li><li>不过异步IO与前面IO模型不同的是:前面的都是数据准备阶段的阻塞与非阻塞,异步IO模型通知的是IO操作已经完成,而不是数据准备完成</li><li>异步IO才是真正的非阻塞,主进程只负责做自己的事情,等IO操作完成(数据成功从内核缓存区复制到应用程序缓冲区)时通过回调函数对数据进行处理</li><li>unix中异步io函数以aio_或lio_打头 </li></ul><p><img src="https://i.loli.net/2021/08/15/aYOZJwSumfoU5eh.png" alt="screenShot.png"></p><h2 id="★★★-select、poll、epoll-的原理、比较、以及使用场景;epoll-的水平触发与边缘触发"><a href="#★★★-select、poll、epoll-的原理、比较、以及使用场景;epoll-的水平触发与边缘触发" class="headerlink" title="★★★ select、poll、epoll 的原理、比较、以及使用场景;epoll 的水平触发与边缘触发"></a>★★★ select、poll、epoll 的原理、比较、以及使用场景;epoll 的水平触发与边缘触发</h2><p>select、poll 和 epoll 都是 Linux API 提供的 IO 复用方式。</p><p>select将用户传入的数组拷贝到内核空间,然后查询每个fd文件表述符对应的设备状态,32位监听上限是1024,64位上线是2048,采用轮询方式扫描sokect。线性扫描效率低资源浪费</p><p>poll是链表,长度不固定,和slect机制类似。poll还有一个特点是“<strong>水平触发</strong>”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。</p><p>epoll通过内核和用户空间共享一块内存来实现的</p><p>回调机制内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。</p><p>epoll有两种触发模式(EPOLLLT)和(EPOLLET),LT水平触发,默认,ET边缘触发。</p><p><strong>LT模式</strong>下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,而</p><p><strong>ET(边缘触发)模式中</strong>,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误。</p><p>还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。</p><h2 id="★★★从输⼊-URL-到⻚⾯加载完成,发⽣了什么?"><a href="#★★★从输⼊-URL-到⻚⾯加载完成,发⽣了什么?" class="headerlink" title="★★★从输⼊ URL 到⻚⾯加载完成,发⽣了什么?"></a>★★★从输⼊ URL 到⻚⾯加载完成,发⽣了什么?</h2><ol><li>输⼊⽹址按回⻋键或点击跳转</li><li>发送到DNS服务器进⾏DNS解析,获取到我们对应web服务器对应的ip地址</li><li>与Web服务器建⽴TCP连接</li><li>浏览器向web服务器发送http请求</li><li>Web服务器进⾏响应请求并返回指定的url数据(当然这⾥也可能是错误信息或者重定向到新的url地址等)</li><li>浏览器下载web服务器返回的数据及解析html源⽂件</li><li>根据⽂件⽣成DOM树和样式树合成我们的渲染树,解析js,最后渲染我们的⻚⾯然后显示出来</li></ol>]]></content>
<categories>
<category> 计算机网络 </category>
</categories>
<tags>
<tag> 计算机基础 </tag>
<tag> 计算机网络 </tag>
</tags>
</entry>
<entry>
<title>Redis知识点梳理</title>
<link href="2021/08/01/%E6%95%B0%E6%8D%AE%E5%BA%93/Redis%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/"/>
<url>2021/08/01/%E6%95%B0%E6%8D%AE%E5%BA%93/Redis%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/</url>
<content type="html"><![CDATA[<h1 id="Redis知识点梳理"><a href="#Redis知识点梳理" class="headerlink" title="Redis知识点梳理"></a>Redis知识点梳理</h1><h2 id="★★☆-字典和跳跃表原理分析"><a href="#★★☆-字典和跳跃表原理分析" class="headerlink" title="★★☆ 字典和跳跃表原理分析"></a>★★☆ 字典和跳跃表原理分析</h2><p><a href="https://blog.csdn.net/weixin_41622183/article/details/91126155">redis跳表</a></p><h2 id="★★★-使用场景"><a href="#★★★-使用场景" class="headerlink" title="★★★ 使用场景"></a>★★★ 使用场景</h2><p><strong>String</strong> </p><pre class="line-numbers language-none"><code class="language-none">常⽤命令: set,get,decr,incr,mget 等。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规keyvalue缓存应⽤; 常规计数:微博数,粉丝数等。 </p><p><strong>Hash</strong> </p><pre class="line-numbers language-none"><code class="language-none">常⽤命令: hget,hset,hgetall 等<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>hash 是⼀个 string 类型的 field 和 value 的映射表,hash 特别适合⽤于存储对象,后续操作的时 候,你可以直接仅仅修改这个对象中的某个字段的值。 ⽐如我们可以 hash 数据结构来存储⽤户信 息,商品信息等等。⽐如下⾯我就⽤ hash 类型存放了我本⼈的⼀些信息:</p><pre class="line-numbers language-none"><code class="language-none">key=JavaUser293847 value={“id”: 1,“name”: “SnailClimb”, “age”: 22, “location”: “Wuhan, Hubei” }<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>List</strong> </p><pre class="line-numbers language-none"><code class="language-none">常⽤命令: lpush,rpush,lpop,rpop,lrange等 <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>list 就是链表,Redis list 的应⽤场景⾮常多,也是Redis最重要的数据结构之⼀,⽐如微博的关注 列表,粉丝列表,消息列表等功能都可以⽤Redis的 list 结构来实现。</p><p> Redis list 的实现为⼀个双向链表,即可以⽀持反向查找和遍历,更⽅便操作,不过带来了部分额外 的内存开销。 </p><p>另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分⻚查询,这 个很棒的⼀个功能,基于 redis 实现简单的⾼性能分⻚,可以做类似微博那种下拉不断分⻚的东⻄ (⼀⻚⼀⻚的往下⾛),性能⾼。</p><p> <strong>Set</strong> </p><pre class="line-numbers language-none"><code class="language-none">常⽤命令: sadd,spop,smembers,sunion 等 <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>set 对外提供的功能与list类似是⼀个列表的功能,特殊之处在于 set 是可以⾃动排重的。 </p><p>当你需要存储⼀个列表数据,⼜不希望出现重复数据时,set是⼀个很好的选择,并且set提供了判断某 个成员是否在⼀个set集合内的重要接⼝,这个也是list所不能提供的。可以基于 set 轻易实现交集、 并集、差集的操作。 </p><p>⽐如:在微博应⽤中,可以将⼀个⽤户所有的关注⼈存在⼀个集合中,将其所有粉丝存在⼀个集合。 Redis可以⾮常⽅便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程, 具体命令如下: </p><pre class="line-numbers language-none"><code class="language-none">sinterstore key1 key2 key3 将交集存在key1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>Sorted Set</strong> </p><pre class="line-numbers language-none"><code class="language-none">常⽤命令: zadd,zrange,zrem,zcard等 <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>和set相⽐,sorted set增加了⼀个权重参数score,使得集合中的元素能够按score进⾏有序排列。 </p><p>举例: 在直播系统中,实时排⾏信息包含直播间在线⽤户列表,各种礼物排⾏榜,弹幕消息(可以理 解为按消息维度的消息排⾏榜)等信息,适合使⽤ Redis 中的 Sorted Set 结构进⾏存储。 </p><h2 id="★★★-与-Memchached-的比较"><a href="#★★★-与-Memchached-的比较" class="headerlink" title="★★★ 与 Memchached 的比较"></a>★★★ 与 Memchached 的比较</h2><ol><li>redis⽀持更丰富的数据类型(⽀持更复杂的应⽤场景):Redis不仅仅⽀持简单的k/v类型的数 据,同时还提供list,set,zset,hash等数据结构的存储。memcache⽀持简单的数据类型, String。</li><li>Redis⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使 ⽤,⽽Memecache把数据全部存在内存之中。 </li><li>集群模式:memcached没有原⽣的集群模式,需要依靠客户端来实现往集群中分⽚写⼊数据;但 是 redis ⽬前是原⽣⽀持 cluster 模式的.</li><li>Memcached是多线程,⾮阻塞IO复⽤的⽹络模型;Redis使⽤单线程的多路 IO 复⽤模型。</li></ol><h2 id="★★☆redis-KEY-过期策略"><a href="#★★☆redis-KEY-过期策略" class="headerlink" title="★★☆redis KEY 过期策略"></a>★★☆redis KEY 过期策略</h2><ul><li>定时过期 主动 定时器CPU消耗过大</li><li>惰性过期 被动 只有在下次操作时去判断是否删除,内存消耗大</li><li>定时过期 一定数量 采样 惰性+定时</li></ul><h2 id="★☆☆-数据淘汰机制-MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据"><a href="#★☆☆-数据淘汰机制-MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据" class="headerlink" title="★☆☆ 数据淘汰机制(MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据)"></a>★☆☆ 数据淘汰机制(MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据)</h2><p><strong>说明:</strong></p><ul><li>lru: (least recently used) 最近最少使用,时间维度 (maxmemory-samples 采样设置)</li><li>lfu:(least frequently used) 最不常用,热度维度</li><li>random: 随机</li><li>ttl 过期时间</li></ul><p><strong>策略:</strong></p><ol><li><p>volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使⽤的数 据淘汰</p></li><li><p>volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘 汰 </p></li><li><p>volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 </p></li><li><p>allkeys-lru:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的key(这个是 最常⽤的) </p></li><li><p>allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 </p></li><li><p>no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。</p></li><li><p> volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使⽤的数据 淘汰 </p></li><li><p>allkeys-lfu:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最不经常使⽤的key</p></li></ol><h2 id="★★☆-RDB-和-AOF-持久化机制"><a href="#★★☆-RDB-和-AOF-持久化机制" class="headerlink" title="★★☆ RDB 和 AOF 持久化机制"></a>★★☆ RDB 和 AOF 持久化机制</h2><p><strong>RDB快照</strong></p><p>RDB快照是某个时间点的一次全量数据备份,是二进制文件,在存储上非常紧凑。</p><p><strong>优点</strong></p><ul><li>RDB文件小,非常适合定时备份,用于灾难恢复</li><li>Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的是内存数据,而AOF文件中存储的是一条条命令,需要重演命令。</li></ul><p><strong>缺点</strong></p><ul><li>RDB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景</li><li>RDB的cow机制中,fork子进程属于重量级操作,并且会阻塞redis主进程</li><li>存在老版本的Redis不兼容新版本RDB格式文件的问题</li></ul><p><strong>AOF日志</strong></p><p>AOF日志是持续增量的备份,是基于写命令存储的可读的文本文件。AOF日志会在持续运行中持续增大,由于Redis重启过程需要优先加载AOF日志进行指令重放以恢复数据,恢复时间会无比漫长。所以需要定期进行AOF重写,对AOF日志进行瘦身。目前AOF是Redis持久化的主流方式。</p><p><strong>优点</strong> </p><p>AOF只是追加写日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少</p><p><strong>缺点</strong></p><ul><li>AOF方式生成的日志文件太大,需要不断AOF重写,进行瘦身。</li><li>即使经过AOF重写瘦身,由于文件是文本文件,文件体积较大(相比于RDB的二进制文件)。</li><li>AOF重演命令式的恢复数据,速度显然比RDB要慢。</li></ul><p>另外,可以使用下面这种方式。Master使用AOF,Slave使用RDB快照,master需要首先确保数据完整性,它作为数据备份的第一选择;slave提供只读服务或仅作为备机,它的主要目的就是快速响应客户端read请求或灾切换。</p><h2 id="★★☆-事件驱动模型"><a href="#★★☆-事件驱动模型" class="headerlink" title="★★☆ 事件驱动模型"></a>★★☆ 事件驱动模型</h2><p> Redis 是一个事件驱动程序,通过事件的方式来运行 Redis。比如客户端向服务端发起一个 get 请求,在做好了建连、发送请求、响应请求、关闭连接等准备操作后,就触发了一个事件。</p><p><strong>文件事件</strong></p><p>文件事件是 socket 的一个抽象,客户端发送请求到服务端,会先建立连接,然后通过连接发送命令请求,其中每个连接就是一个 socket。对于一个 Redis 服务端,在同一时刻会有很多 socket 连接,每一个 socket 都可以理解成一个文件事件。</p><p>每产生一个文件事件后,就将其交给文件事件处理器去处理,文件事件处理器是由 I/O 多路复用处理器、文件事件分发器、事件处理器几部分组成。</p><p><img src="https://i.loli.net/2021/08/16/8mSaReWKNcX6qgf.png" alt="screenShot.png"></p><p><strong>时间事件</strong></p><p>在 Redis 中最不陌生的应该就是各种定时处理器,每隔一段时间就出发一个操作。具体的应用如 RDB和 AOF 文件定时做持久化操作;如果集群是主从架构的,定时将主库上的数据同步给从库,定期发送心跳信息给集群内各个节点,检查节点是否还在正常提供服务;定期检查库里面设置了过期时间的 key 并将已过期的 key 从内存中提出等等一些实际应用。</p><p>Redis 将所有时间事件通过链表串联起来,每个结点代表一个时间事件,每个结点上存储着当前时间下一次要发生的时间点;每隔一段时间,该链表就会被遍历一次,发现那个时间事件该执行就去执行对应的事件,然后更新其下一次应该执行的时间点。</p><p><img src="https://i.loli.net/2021/08/16/cPFvEBuZh5nXkzY.png" alt="screenShot.png"></p><h2 id="★☆☆-主从复制原理"><a href="#★☆☆-主从复制原理" class="headerlink" title="★☆☆ 主从复制原理"></a>★☆☆ 主从复制原理</h2><p><strong>主从复制过程:</strong></p><ul><li>从节点执行slaveof+masterIP,保存主节点信息</li><li>从节点中的定时任务发现主节点信息,建立和主节点的socket连接</li><li>从节点发送Ping信号,主节点返回Pong,两边能互相通信</li><li>连接建立后,主节点将所有数据发送给从节点(数据同步)</li><li>主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。</li></ul><p><strong>Redis主从复制会存在以下问题:</strong></p><ul><li>一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。</li><li>主节点的写能力受到单机的限制。</li><li>主节点的存储能力受到单机的限制。</li><li>原生复制的弊端在早期的版本中也会比较突出,比如:redis复制中断后,从节点会发起psync。此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或秒级的卡顿。</li></ul><p>所以用哨兵解决以上问题。</p><p><a href="https://zhuanlan.zhihu.com/p/335101550">Redis高可用原理,哨兵机制详解 </a></p><h2 id="★★★-集群与分布式"><a href="#★★★-集群与分布式" class="headerlink" title="★★★ 集群与分布式"></a>★★★ 集群与分布式</h2><h2 id="★★★缓存穿透、击穿、雪崩原理及解决方案"><a href="#★★★缓存穿透、击穿、雪崩原理及解决方案" class="headerlink" title="★★★缓存穿透、击穿、雪崩原理及解决方案"></a>★★★缓存穿透、击穿、雪崩原理及解决方案</h2><p><strong>缓存穿透:</strong>当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据,我们称这种现象为”缓存穿透”。缓存穿透会穿透Redis的保护,提升底层数据库的负载压力,同时这类穿透查询没有数据返回也造成了网络和计算资源的浪费。</p><p><strong>解决方案:</strong></p><ul><li>在接口访问层对用户做校验,如接口传参、登陆状态、n秒内访问接口的次数;</li><li>利用布隆过滤器,将数据库层有的数据key存储在位数组中,以判断访问的key在底层数据库中是否存在;</li></ul><p><strong>缓存击穿:</strong>穿透表示底层数据库没有数据且缓存内也没有数据,击穿表示底层数据库有数据而缓存内没有数据。当热点数据key从缓存内失效时,大量访问同时请求这个数据,就会将查询下沉到数据库层,此时数据库层的负载压力会骤增,我们称这种现象为”缓存击穿”</p><p><strong>解决方案:</strong></p><ul><li>延长热点key的过期时间或者设置永不过期,如排行榜,首页等一定会有高并发的接口;</li><li>利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,一旦查到数据就缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库;</li></ul><p><strong>缓存雪崩:</strong>缓存雪崩是缓存击穿的”大面积”版,缓存击穿是数据库缓存到Redis内的热点数据失效导致大量并发查询穿过redis直接击打到底层数据库,而缓存雪崩是指Redis中大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,此时数据库层的负载压力会骤增,我们称这种现象为”缓存雪崩”。</p><p><strong>解决方案:</strong></p><ul><li>在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,以防止大量的key在同一时刻过期;</li><li>对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;</li><li>延长热点key的过期时间或者设置永不过期,这一点和缓存击穿中的方案一样;</li></ul><h2 id="★★☆-事务原理"><a href="#★★☆-事务原理" class="headerlink" title="★★☆ 事务原理"></a>★★☆ 事务原理</h2><p>原子性中Redis的事务只能保证单个命令的原子性,多个命令就无法保证。原子性没有隔离性也不能保证</p><p>Reids在<strong>进行事务</strong>的时候,不会被中断知道事务的运行结束,也<strong>具有一定的隔离性</strong>,并且*<em>Redis也能持久化数据</em></p><p>这个与Redis的特点:<strong>「快速、高效」</strong>有着密切的关联,<strong>「因为一系列回滚操作、像事务隔离级别那这样加锁、解锁,是非常消耗性能的」</strong>。所以,Redis中执行事务的流程只需要简单的下面三个步骤:</p><ul><li><strong>开始事务(MULTI)</strong></li><li><strong>命令入队</strong></li><li><strong>执行事务(EXEC)、撤销事务(DISCARD )</strong></li></ul><h2 id="★★★-线程安全问题"><a href="#★★★-线程安全问题" class="headerlink" title="★★★ 线程安全问题"></a>★★★ 线程安全问题</h2><p>Redis是线程安全的,采用IO多路复用+C语言实现,速度很快。其主要的性能瓶颈主要在网络 I/O 和内存两个方面</p><ol><li>内存容量问题:如果采用集群部署的方式,可以通过部署多个端口,使得总容量得到提升,在一定程度上可以解决内存的问题。</li><li>网络 I/O 问题:在实际生产环境中,Redis 在执行命令时计算基于内存是可以在很快时间内完成的,但是数据传输过程中需要经过网络 I/O 操作,这一步才是真正耗时所在。所以 Redis 在 6.0 版本引入了所谓的多线程,其主要是在做<strong>网络 I/O</strong> 操作的时候采用了<strong>多线程</strong>的方式加快 I/O 操作,而<strong>命令的执行</strong>还是采用<strong>单线程</strong>进行工作的。</li></ol>]]></content>
<categories>
<category> Redis数据库学习 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> Redis </tag>
</tags>
</entry>
<entry>
<title>MySQL数据库知识点梳理</title>
<link href="2021/06/21/%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/"/>
<url>2021/06/21/%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/</url>
<content type="html"><![CDATA[<h1 id="MySQL知识点梳理"><a href="#MySQL知识点梳理" class="headerlink" title="MySQL知识点梳理"></a>MySQL知识点梳理</h1><h2 id="★★☆-手写-SQL-语句,特别是连接查询与分组查询"><a href="#★★☆-手写-SQL-语句,特别是连接查询与分组查询" class="headerlink" title="★★☆ 手写 SQL 语句,特别是连接查询与分组查询"></a>★★☆ 手写 SQL 语句,特别是连接查询与分组查询</h2><p><strong>连接查询:</strong></p><p><strong>内连接:返回两个表交集的记录(A∩B):</strong></p><p><img src="https://i.loli.net/2021/08/15/wEFXKGJRz7oqM9u.png" alt="screenShot.png"></p><p>语句:</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> a_table a <span class="token keyword">INNER</span> <span class="token keyword">JOIN</span> b_table b<span class="token keyword">ON</span> a<span class="token punctuation">.</span>a_id <span class="token operator">=</span> b<span class="token punctuation">.</span>b_id<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>上述语句将会返回a_id和b_id相等的记录,也就是a_id和b_id两个字段有交集的记录。<br>第一步:先确定主表,FROM <表1><br>第二步:确定连接的表,INNER JOIN <表2><br>第三步:然后确定连接条件,ON <条件…><br>第四步:加上 WHERE 、ORDER BY等句子。</p><p><strong>左外连接:返回左表都存在的记录(A):</strong></p><p><img src="https://i.loli.net/2021/08/15/qVybc4pFSAslhu7.png" alt="screenShot.png"></p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> a_table a <span class="token keyword">LEFT</span> <span class="token keyword">OUTER</span> <span class="token keyword">JOIN</span> b_table b<span class="token keyword">ON</span> a<span class="token punctuation">.</span>a_id <span class="token operator">=</span> b<span class="token punctuation">.</span>b_id<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>上述语句将会返回a_id的所有记录,如果某记录的字段不存在,则显示NULL。</p><p><strong>右外连接:返回右表都存在的记录(B):</strong></p><p><img src="https://i.loli.net/2021/08/15/9o3mwYVxM2CEGK5.png" alt="screenShot.png"></p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> a_table a <span class="token keyword">RIGHT</span> <span class="token keyword">OUTER</span> <span class="token keyword">JOIN</span> b_table b<span class="token keyword">ON</span> a<span class="token punctuation">.</span>a_id <span class="token operator">=</span> b<span class="token punctuation">.</span>b_id<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>上述语句将会返回b_id的所有记录,如果某记录的字段不存在,则显示NULL。</p><p><strong>分组聚合:</strong></p><p>如果我们要统计一班的学生数量,我们知道,可以用</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> num <span class="token keyword">FROM</span> students <span class="token keyword">WHERE</span> class_id <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>如果要继续统计二班、三班的学生数量,难道必须不断修改WHERE条件来执行SELECT语句吗?<br>按class_id分组:</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> num <span class="token keyword">FROM</span> students <span class="token keyword">GROUP</span> <span class="token keyword">BY</span> class_id<span class="token punctuation">;</span><span class="token keyword">SELECT</span> <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> num<span class="token punctuation">,</span>class_id<span class="token punctuation">,</span>gender<span class="token keyword">FROM</span> students<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> class_id<span class="token punctuation">,</span> gender<span class="token keyword">ORDER</span> <span class="token keyword">BY</span> class_id <span class="token keyword">DESC</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="★★☆-连接查询与子查询的比较"><a href="#★★☆-连接查询与子查询的比较" class="headerlink" title="★★☆ 连接查询与子查询的比较"></a>★★☆ 连接查询与子查询的比较</h2><p><strong>子查询:</strong></p><ol><li>MySQL从4.1版本开始支持子查询,使用子查询进行SELECT语句嵌套查询,可以一次完成很多逻辑上需要多个步骤才能完成的SQL操作</li><li>子查询虽然很灵活,但是执行效率并不高</li><li>执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,这里多了一个创建和销毁临时表的过程</li></ol><p><strong>连接查询:</strong></p><p>可以使用连接查询(JOIN)代替子查询,连接查询不需要建立临时表,因此其速度比子查询快</p><h2 id="★★☆-drop、delete、truncate-比较"><a href="#★★☆-drop、delete、truncate-比较" class="headerlink" title="★★☆ drop、delete、truncate 比较"></a>★★☆ drop、delete、truncate 比较</h2><p><strong>区别:</strong></p><ul><li>delete和truncate只删除表的数据不删除表的结构</li><li>delete和truncate只删除表的数据不删除表的结构</li><li>速度,一般来说: drop> truncate >delete</li><li>delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;</li><li>如果有相应的trigger,执行的时候将被触发. truncate,drop是ddl, 操作立即生效,原数据不放到rollback segment中,不能回滚. 操作不触发trigger.</li></ul><p><strong>使用场景:</strong></p><ul><li>不再需要一张表的时候,用drop</li><li>想删除部分数据行时候,用delete,并且带上where子句</li><li>保留表而删除所有数据的时候用truncate</li></ul><h2 id="★★☆-视图的作用,以及何时能更新视图"><a href="#★★☆-视图的作用,以及何时能更新视图" class="headerlink" title="★★☆ 视图的作用,以及何时能更新视图"></a>★★☆ 视图的作用,以及何时能更新视图</h2><p><strong>什么是视图:</strong><br>视图就是一条SELECT语句执行后返回的结果集,<br>视图是一个虚拟表,同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表。</p><p><strong>视图作用:</strong><br>(1)简化用户的操作<br>关键信息来源于多个复杂关联表,可以创建视图提取我们需要的信息,<br>简化操作;</p><p>(2)对机密数据提供保护作用<br>不希望用户访问表中某些含敏感信息的列,比如salary…</p><p><strong>不可更新的视图:</strong><br>某些视图是可更新的。也就是说,可以在诸如<strong>UPDATE、DELETE或INSERT</strong>等语句中使用它们,以更新基表的内容。对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。还有一些特定的其他结构,这类结构会使得视图不可更新。更具体地讲,如果视图包含下述结构中的任何一种,那么它就是不可更新的:</p><p>· 聚合函数(SUM(), MIN(), MAX(), COUNT()等)。<br>· DISTINCT<br>· GROUP BY<br>· HAVING<br>· UNION<br>· 位于选择列表中的子查询<br>· Join<br>· FROM子句中的不可更新视图<br>· WHERE子句中的子查询,引用FROM子句中的表。</p><h2 id="★☆☆-理解存储过程、触发器等作用"><a href="#★☆☆-理解存储过程、触发器等作用" class="headerlink" title="★☆☆ 理解存储过程、触发器等作用"></a>★☆☆ 理解存储过程、触发器等作用</h2><p><strong>存储过程</strong>(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后<em><strong>存储在数据库</strong></em>中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。</p><p><strong>触发器</strong>(trigger)是个<strong>特殊</strong>的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。</p><h2 id="★★★-ACID-的作用以及实现原理"><a href="#★★★-ACID-的作用以及实现原理" class="headerlink" title="★★★ ACID 的作用以及实现原理"></a>★★★ ACID 的作用以及实现原理</h2><p><strong>原子性</strong><br>原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。<br>如果事务中一个sql语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。因此实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。<br>首先我们要简单了解InnoDB存储引擎提供的两种事务日志:redo log(重做日志)和undo log(回滚日志)。redo log用于保证事务持久性;undo log则是事务原子性和隔离性实现的基础。<br>InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。</p><p><strong>持久性</strong><br>持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。<br>InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。<br>Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷到磁盘,就会导致数据的丢失。于是,redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。</p><p><strong>隔离性</strong><br>隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。<br>隔离性追求的是并发情形下事务之间互不干扰,这与多线程程序的执行过程类似。简单起见,我们仅考虑最简单的读操作和写操作(暂时不考虑带锁读等特殊操作),那么隔离性的探讨,主要可以分为两个方面:<br>(一个事务)写操作对(另一个事务)写操作的影响:锁机制保证隔离性<br>(一个事务)写操作对(另一个事务)读操作的影响:MVCC(多版本并发控制)保证隔离性。<br>如果一个事务A在执行写操作时,如果遇到另一个事务B想要执行写操作,那么B的写操作是会被阻塞的,至于锁住的是数据行还是整个表,这要看隔离级别(级别越高越安全,但是效率也越低);而事务B如果想要执行的是读操作,它将被允许通过MVCC提供的数据版本信息来读数据。</p><p><strong>一致性</strong><br>一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)。<br>我对一致性的理解是:数据库能正确地执行事务。数据库本身的完整性约束不会被破坏,事务的逻辑能够成功地执行并对数据产生正确的影响。<br>实现一致性的措施包括:<br>保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证<br>数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等<br>应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额。</p><h2 id="★★★-四大隔离级别,以及不可重复读和幻影读的出现原因"><a href="#★★★-四大隔离级别,以及不可重复读和幻影读的出现原因" class="headerlink" title="★★★ 四大隔离级别,以及不可重复读和幻影读的出现原因"></a>★★★ 四大隔离级别,以及不可重复读和幻影读的出现原因</h2><p><img src="https://i.loli.net/2021/08/15/avJlsVdCf7juyob.png" alt="screenShot.png"></p><p><img src="https://i.loli.net/2021/08/15/7c8qLy4ugwMFXRK.png" alt="screenShot.png"></p><h2 id="★★☆-锁的类型以及粒度,两段锁协议,隐式和显示锁定"><a href="#★★☆-锁的类型以及粒度,两段锁协议,隐式和显示锁定" class="headerlink" title="★★☆ 锁的类型以及粒度,两段锁协议,隐式和显示锁定"></a>★★☆ 锁的类型以及粒度,两段锁协议,隐式和显示锁定</h2><p>封锁类型有两种:读写锁和意向锁</p><ol><li>读写锁分读锁(s锁)和写锁(x锁)。<br>对象加了写锁,可以更新与读取,不能加其他锁。<br>对象加了读锁,只能读取,可以加读锁</li><li>意向锁<br>新增了IS锁和IX锁,都是表锁,分别表达加S/X锁的意愿,有利于支持多粒度。<br>补充:粒度有两种:表锁、行锁,行锁开销大。</li></ol><p><strong>两段锁协议</strong></p><p>两段锁协议规定所有的事务应遵守的规则:<br> ① 在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁。<br> ② 在释放一个封锁之后,事务不再申请和获得其它任何封锁。<br>总结:加锁和解锁分两阶段执行,为事务可串行化调度提供支持</p><p><strong>隐式和显式锁定</strong>:MySQL的InnoDB引擎采用两段锁协议,自动加锁,属于隐式锁定,同时也可以显式锁定</p><h2 id="★★★-乐观锁与悲观锁"><a href="#★★★-乐观锁与悲观锁" class="headerlink" title="★★★ 乐观锁与悲观锁"></a>★★★ 乐观锁与悲观锁</h2><p>乐观锁和悲观锁都是为了事务的并发控制。</p><table><thead><tr><th></th><th>乐观锁</th><th>悲观锁</th></tr></thead><tbody><tr><td>目的</td><td>事务提交时检测冲突</td><td>事务执行时避免冲突</td></tr><tr><td>实现</td><td>版本控制</td><td>数据库锁机制</td></tr></tbody></table><h2 id="★★★-MVCC-原理,当前读以及快照读,Next-Key-Locks-解决幻读"><a href="#★★★-MVCC-原理,当前读以及快照读,Next-Key-Locks-解决幻读" class="headerlink" title="★★★ MVCC 原理,当前读以及快照读,Next-Key Locks 解决幻读"></a>★★★ MVCC 原理,当前读以及快照读,Next-Key Locks 解决幻读</h2><p>MVCC全称是多版本并发控制,是InnoDB实现隔离级别的一种方式,用于实现可提交读与可重复读。MVCC并不是完全不用加锁,而是避免了对SELECT加锁的操作,提高性能。<br>原理:读操作时读取旧版本快照,写操作时更新新版本快照<br>快照读:MVCC的SELECT读取的是旧版本快照<br>当前读:MVCC的UPDATE/DELETE/INSERT需要加锁,读取最新的数据。</p><ul><li><p>行锁(Record Lock):锁直接加在索引记录上面(无索引项时演变成表锁)。</p></li><li><p>间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别的。</p></li><li><p>Next-Key Locks:行锁和间隙锁组合起来就是 Next-Key Lock。避免了幻读,通常和MVCC配合。</p></li></ul><h2 id="★★☆-范式理论"><a href="#★★☆-范式理论" class="headerlink" title="★★☆ 范式理论"></a>★★☆ 范式理论</h2><p>第一范式:<strong>属性不可再分</strong></p><p>第二范式:<strong>确保表中的每列都和主键相关</strong></p><p>第三范式:<strong>确保每列都和主键列直接相关,而不是间接相关</strong></p><h2 id="★★★-SQL-与-NoSQL-的比较"><a href="#★★★-SQL-与-NoSQL-的比较" class="headerlink" title="★★★ SQL 与 NoSQL 的比较"></a>★★★ SQL 与 NoSQL 的比较</h2><table><thead><tr><th></th><th>SQL</th><th>NoSQL</th></tr></thead><tbody><tr><td>性能</td><td>较低</td><td>较高</td></tr><tr><td>应用</td><td>结构性数据,如账号、密码</td><td>非结构性数据,如文章、评论</td></tr><tr><td>表</td><td>固定结构表</td><td>Json或其他方式,更加灵活</td></tr><tr><td>事务</td><td>通过事务控制表的隔离级别</td><td>每一个操作都具有原子性</td></tr><tr><td>外键</td><td>可以添加外键联合查询</td><td>直接在原数据中添加</td></tr></tbody></table><h2 id="★★★-B-Tree-原理,与其它查找树的比较"><a href="#★★★-B-Tree-原理,与其它查找树的比较" class="headerlink" title="★★★ B+ Tree 原理,与其它查找树的比较"></a>★★★ B+ Tree 原理,与其它查找树的比较</h2><p> <strong>B树</strong>:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于</p><p>走右结点;</p><p> <strong>B-树</strong>:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键</p><p>字范围的子结点; 所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;</p><p> <strong>B+树</strong>:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点</p><p>中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;</p><p> <strong>B*树</strong>:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率</p><p>从1/2提高到2/3;</p><h2 id="★★★-MySQL-索引以及优化"><a href="#★★★-MySQL-索引以及优化" class="headerlink" title="★★★ MySQL 索引以及优化"></a>★★★ MySQL 索引以及优化</h2><p>索引分类:普通 主键 唯一 组合</p><p>MySQL索引使⽤的数据结构主要有<strong>BTree索引</strong> 和 <strong>哈希索引</strong> 。</p><p>但对于主要的两种存储引擎的实现⽅式是不同的。</p><p> <strong>MyISAM:</strong> B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,⾸先按照B+Tree 搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址 读取相应的数据记录。这被称为“⾮聚簇索引”。</p><p> <strong>InnoDB:</strong> 其数据⽂件本身就是索引⽂件。相⽐MyISAM,索引⽂件和数据⽂件是分离的,其表数据 ⽂件本身就是按B+Tree组织的⼀个索引结构,树的叶节点data域保存了完整的数据记录。这个索 引的key是数据表的主键,因此InnoDB表数据⽂件本身就是主索引。这被称为“聚簇索引(或聚集 索引)”。⽽其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值⽽不是地址,这也是和MyISAM不同的地⽅。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再⾛⼀遍主索引。 因此,在设计表的时候,<strong>不建议使⽤过⻓的字段作为主键,也不建议使⽤⾮单调的字段作为主键,这样会造成主索引频繁分裂</strong>。</p><p>索引是一颗逻辑上的 B+TREE,需要维护,当对表中的数据进行增删改的时候,索引树也要动态维护(变化)。这样会降低数据增删改的速度,所以索引不能无脑建。</p><h2 id="★★★-查询优化"><a href="#★★★-查询优化" class="headerlink" title="★★★ 查询优化"></a>★★★ 查询优化</h2><ol><li><p>使用EXPLAIN关键词检查SQL。EXPLAIN可以帮你分析你的查询语句或是表结构的性能瓶颈,就得EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的,是否有全表扫描等;</p></li><li><p>查询的条件尽量使用索引字段,如某一个表有多个条件,就尽量使用复合索引查询,复合索引使用要注意字段的先后顺序。</p></li><li><p>多表关联尽量用join,减少子查询的使用。表的关联字段如果能用主键就用主键,也就是尽可能的使用索引字段。如果关联字段不是索引字段可以根据情况考虑添加索引。</p></li><li><p>尽量使用limit进行分页批量查询,不要一次全部获取。</p></li><li><p>绝对避免select *的使用,尽量select具体需要的字段,减少不必要字段的查询;</p></li><li><p>尽量将or 转换为 union all。</p></li><li><p>尽量避免使用is null或is not null。</p></li><li><p>要注意like的使用,前模糊和全模糊不会走索引。</p></li><li><p>Where后的查询字段尽量减少使用函数,因为函数会造成索引失效。</p></li><li><p>避免使用不等于(!=),因为它不会使用索引。</p></li><li><p>用exists代替in,not exists代替not in,效率会更好;</p></li><li><p>避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序,总计等操作。如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。</p></li><li><p>千万不要 ORDER BY RAND()</p></li></ol><h2 id="★★★-InnoDB-与-MyISAM-比较"><a href="#★★★-InnoDB-与-MyISAM-比较" class="headerlink" title="★★★ InnoDB 与 MyISAM 比较"></a>★★★ InnoDB 与 MyISAM 比较</h2><p><strong>MyISAM和InnoDB的区别:</strong></p><ol><li><p><strong>是否⽀持⾏级锁 :</strong> MyISAM 只有表级锁(table-level locking),⽽InnoDB ⽀持⾏级锁(rowlevel locking)和表级锁,默认为⾏级锁。 </p></li><li><p><strong>是否⽀持事务和崩溃后的安全恢复:</strong> MyISAM 强调的是性能,每次查询具有原⼦性,其执⾏速度 ⽐InnoDB类型更快,但是不提供事务⽀持。但是InnoDB 提供事务⽀持事务,外部键等⾼级数据 库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能⼒(crash recovery capabilities) 的事务安全(transaction-safe (ACID compliant))型表。</p></li><li><p><strong>是否⽀持外键:</strong> MyISAM不⽀持,⽽InnoDB⽀持。</p></li><li><p><strong>是否⽀持MVCC :</strong>仅 InnoDB ⽀持。应对⾼并发事务, MVCC⽐单纯的加锁更⾼效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下⼯作;MVCC可以使⽤ 乐观 (optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统⼀</p></li></ol><h2 id="★★☆-水平切分与垂直切分"><a href="#★★☆-水平切分与垂直切分" class="headerlink" title="★★☆ 水平切分与垂直切分"></a>★★☆ 水平切分与垂直切分</h2><p><img src="https://i.loli.net/2021/08/15/cEUFJAS4GkoQ35H.png" alt="screenShot.png"></p><p><img src="https://i.loli.net/2021/08/15/Z1kH2cYRPLfqnoS.png" alt="screenShot.png"></p><p><img src="https://i.loli.net/2021/08/15/uRsE6lbjUmJf5rC.png" alt="screenShot.png"></p><h2 id="★★☆-主从复制原理、作用、实现"><a href="#★★☆-主从复制原理、作用、实现" class="headerlink" title="★★☆ 主从复制原理、作用、实现"></a>★★☆ 主从复制原理、作用、实现</h2><p><strong>原理:</strong></p><p><strong>1.数据库有个bin-log二进制文件,记录了所有sql语句。</strong></p><p><strong>2.我们的目标就是把主数据库的bin-log文件的sql语句复制过来。</strong></p><p><strong>3.让其在从数据的relay-log重做日志文件中再执行一次这些sql语句即可。</strong></p><p><strong>4.下面的主从配置就是围绕这个原理配置</strong></p><p><strong>5.具体需要三个线程来操作:</strong></p><p><strong>1.binlog输出线程</strong>:**每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。</p><p>在从库里,当复制开始的时候,从库就会创建两个线程进行处理:</p><p>**2.从库I/O线程:**当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件。</p><p>**3.从库的SQL线程:**从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。</p><p><strong>可以知道,对于每一个主从复制的连接,都有三个线程。拥有多个从库的主库为每一个连接到主库的从库创建一个binlog输出线程,每一个从库都有它自己的I/O线程和SQL线程。</strong></p><p><strong>作用:</strong></p><ul><li>实现负载均衡读写分离</li><li>实现数据库备份</li><li>实现数据库高可用和故障切换</li></ul><p><strong>实现:</strong></p><p><strong>主节点配置</strong></p><ol><li>确认启用二进制日志,把二进制文件放在独立的目录中</li><li>分配ID号,默认是1,修改配置文件/etc/my.cnf</li><li>确定二进制日志文件位置和二进制日志中所在位置</li><li>创建有复制权限的账号</li></ol><p><strong>从节点配置</strong></p><ol><li>确认启用二进制日志,把二进制文件放在独立的目录中</li><li>分配ID号,设置和主节点不同的ID号</li><li>从数据库开启只读模式</li><li>使用有复制权限的用户账号连接至master主服务</li><li>启动复制线程</li></ol>]]></content>
<categories>
<category> MySQL数据库学习 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> MySQL </tag>
</tags>
</entry>
<entry>
<title>《计算机网络:自顶向下》读书笔记</title>
<link href="2021/06/20/%E8%AE%A1%E7%BD%91+%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E3%80%8A%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E8%87%AA%E9%A1%B6%E5%90%91%E4%B8%8B%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<url>2021/06/20/%E8%AE%A1%E7%BD%91+%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E3%80%8A%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E8%87%AA%E9%A1%B6%E5%90%91%E4%B8%8B%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<h2 id="1-计算机网络与因特网"><a href="#1-计算机网络与因特网" class="headerlink" title="1 计算机网络与因特网"></a>1 计算机网络与因特网</h2><h3 id="1-1-什么是因特网"><a href="#1-1-什么是因特网" class="headerlink" title="1.1 什么是因特网"></a>1.1 什么是因特网</h3><h4 id="1-1-1-具体构成描述"><a href="#1-1-1-具体构成描述" class="headerlink" title="1.1.1 具体构成描述"></a>1.1.1 具体构成描述</h4><pre class="line-numbers language-none"><code class="language-none">所有被联向因特网的设备都称为 主机(host) 或 端系统(end system) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>端系统通过 <strong>通信链路(communication link)</strong> 和 <strong>分组交换机(packet switch)</strong> 连接到一起。不<br>同的链路以不同的速率传输数据, 链路的传输速率是以bps度量的。当一台端系统有数据要向另一台端<br>系统发送时,发送端系统将数据分段,并为每段加上首部字节,由此形成的信息包称为 <strong>分组<br>(packet)</strong> 。</p><p>分组交换机的两种最著名的类型是 <strong>路由器(router)</strong> 和 <strong>链路层交换机(link-layer switch)</strong> ,它们<br>朝着最终目的的转发分组。</p><pre class="line-numbers language-none"><code class="language-none">端系统通过 因特网服务商(Internet Service Provider,ISP) 接入因特网。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>端系统、分组交换机和其他因特网部件,都要运行控制因特网中信息接收和发送的一系列 <strong>协议<br>(protocol)</strong> 。 <strong>TCP(Transmission Control Protocol,传输控制协议)</strong> 和 <strong>IP(Internet<br>Protocol,网际协议)</strong> 是因特网中两个最为重要的协议。</p><pre class="line-numbers language-none"><code class="language-none">有许多专用网络,其内主机不能与其外主机交换信息,这些专用网络被称为 内联网(intranet) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="1-1-2-服务描述"><a href="#1-1-2-服务描述" class="headerlink" title="1.1.2 服务描述"></a>1.1.2 服务描述</h4><p>与因特网相连的端系统提供了一个 <strong>应用程序编程接口(ApplicationProgramming Interface,API)</strong>,规定了运行在一个端系统上的软件请求因特网基础设施向运行在另一个端系统上的特定目的软<br>件交付数据的方式。因特网API是一套发送软件必须遵循的规则集合。</p><h4 id="1-1-3-什么是协议"><a href="#1-1-3-什么是协议" class="headerlink" title="1.1.3 什么是协议"></a>1.1.3 什么是协议</h4><p>一个协议定义了在两个或多个通信实体之间交换的报文格式和次序,以及在报文传输和/或接受或其他事件方面所采取的的动作。</p><h3 id="1-2-网络边缘"><a href="#1-2-网络边缘" class="headerlink" title="1.2 网络边缘"></a>1.2 网络边缘</h3><h4 id="1-2-1-客户机和服务器程序"><a href="#1-2-1-客户机和服务器程序" class="headerlink" title="1.2.1 客户机和服务器程序"></a>1.2.1 客户机和服务器程序</h4><p><strong>客户机程序(client program)</strong> 是运行在一个端系统上的程序,它发出请求,并从运行在另一个端系统上的 <strong>服务器程序(server program)</strong> 接收服务。客户机-服务器因特网应用程序是 <strong>分布式应用程序(distributed application)</strong> ,客户机程序和服务器程序通过因特网互相发送报文而进行交互。</p><p>越来越多的应用程序是 <strong>对等(peer-to-peer,P2P)</strong> 应用程序,其中的端系统互相作用并运行执行客户机和服务器功能的程序。</p><h4 id="1-2-2-接入网"><a href="#1-2-2-接入网" class="headerlink" title="1.2.2 接入网"></a>1.2.2 接入网</h4><pre class="line-numbers language-none"><code class="language-none">接入网(access network) 是将端系统连接到其 边缘路由器(edge router) 的物理链路。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. 住宅接入(residential access) ,将家庭端系统与网络相连。一种住宅接入形式是通过普通模拟电话线用 拨号调制解调器(dial-up modem) 与住宅ISP相连。新型宽带接入技术为住宅用户提供了更高的比特速率,也为用户提供了一种接入因特网的同时还能打电话的手段,主要有两种常见类型: 数字用户线(digital subscriber line,DSL) 和 混合光纤同轴电缆(hybrid fiber-coaxial cable,HFC) 。2. 公司接入(company access) ,局域网通常被用于连接端用户与边缘服务器。3. 无线接入(wireless access) : 无线局域网(wireless LAN) 和 广域无线接入网(wide-area wireless access network) 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="1-2-3-物理媒体"><a href="#1-2-3-物理媒体" class="headerlink" title="1.2.3 物理媒体"></a>1.2.3 物理媒体</h4><p>物理媒体(physical media) 分为 <strong>导引性媒体</strong>(guided media) <strong>非导引性媒体</strong>(unguided media) 。</p><pre class="line-numbers language-none"><code class="language-none">1. 双绞铜线:非屏蔽双绞线(Unshielded Twisted Pair,UTP) 常用在建筑内计算机网络中。2. 同轴电缆:能被用作引导式 共享媒体(shared media) 。3. 光缆:4. 陆地无线电信道5. 卫星无线电信道:常使用两类卫星: 同步卫星(geostationary satellite) 和 低地球轨道卫星(low-earthorbiting satellite) 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="1-3-网络核心"><a href="#1-3-网络核心" class="headerlink" title="1.3 网络核心"></a>1.3 网络核心</h3><h4 id="1-3-1-电路交换和分组交换"><a href="#1-3-1-电路交换和分组交换" class="headerlink" title="1.3.1 电路交换和分组交换"></a>1.3.1 电路交换和分组交换</h4><p>在 <strong>电路交换(circuit switching)</strong> 网络中,沿着端系统通信路径,为端系统之间通信所提供的资源(缓存、链路传输速度)在通信会话期间会被预留;在 <strong>分组交换(packet switching)</strong> 网络中,这些资<br>源则不会被预留,会话的报文按需使用这些资源,这将导致可能不得不等待(即排队)接入通信线路。</p><p>电路交换网络中的多路复用主要有 <strong>频分多路复用(Frequency-Division Multiplexing,FDM)</strong> 和<strong>时分多路复用(Time-Division Multiplexing,TDM)</strong> 。</p><p>分组交换中,各种应用程序在完成其任务时要交换 <strong>报文(message)</strong> ,源主机将长报文划分为较小的数据块,并称之为 <strong>分组(packet)</strong> ,在源和目的地之间,这些分组中的每个都通过通信链路和分组交换机传送,分组以该链路最大传输速率在通信链路上传输。</p><p>多数分组交换机在链路的输入端使用 <strong>储存转发传输(store-and-forward transmission)</strong> 机制,是指在交换机能够开始向输出链路传输改分组的第一个比特之前,必须接收到整个分组。一台主机经分组交换网络向另一台主机发送分组需要的时间称为 <strong>储存转发时延(store-and-forward delay)</strong> 。</p><p>每个分组交换机有多条链路与之相连,对于每条相连的链路,该分组交换机具有一个<strong>输出缓存(output buffer)</strong>(也称为 <strong>输出队列(output queue)</strong> ),它用于储存路由器准备发往那条链路的分组。分组要承受输出缓存的 <strong>排队时延(queuing delay)</strong> ,这些时延变化程度取决于网络中的拥塞水平。一个到达的分组可能发现该缓存被等待传输的分组完全充满了,因为缓存的大小是有限的,在此情况下,将出现 <strong>分组丢失</strong> 或 <strong>丢包(packet lost)</strong> ——可能是到达的分组也可能是已经排队的分组之一被丢弃。</p><h4 id="1-3-2-分组是怎样通过分组交换网形成其通路的"><a href="#1-3-2-分组是怎样通过分组交换网形成其通路的" class="headerlink" title="1.3.2 分组是怎样通过分组交换网形成其通路的"></a>1.3.2 分组是怎样通过分组交换网形成其通路的</h4><h4 id="1-3-3-ISP和因特网主干因特网是网络的网络。"><a href="#1-3-3-ISP和因特网主干因特网是网络的网络。" class="headerlink" title="1.3.3 ISP和因特网主干因特网是网络的网络。"></a>1.3.3 ISP和因特网主干因特网是网络的网络。</h4><p>第一层ISP也被称为 <strong>因特网主干(Internet backbone)</strong> 网络,直接与其他每个第一层ISP相连,与大量的第二层ISP和其他客户网络相连,覆盖国际区域。</p><p>第二层ISP通常具有区域性或国家性覆盖规模,并且非常重要地仅与少数第一层ISP相连接,被称为是它所连接的第一层ISP的 <strong>客户(customer)</strong> ,第一层ISP相对该客户而言是 <strong>提供商(provider)</strong> 。当两个ISP彼此直接相连时,它们被称为彼此是对等的。</p><pre class="line-numbers language-none"><code class="language-none">在一个ISP的网络中,某ISP与其他ISP的连接点被称为 汇集点(Point of Presence,POP) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="1-4-分组交换网中的时延、丢包和吞吐量"><a href="#1-4-分组交换网中的时延、丢包和吞吐量" class="headerlink" title="1.4 分组交换网中的时延、丢包和吞吐量"></a>1.4 分组交换网中的时延、丢包和吞吐量</h3><h4 id="1-4-1-分组交换网中的时延概述"><a href="#1-4-1-分组交换网中的时延概述" class="headerlink" title="1.4.1 分组交换网中的时延概述"></a>1.4.1 分组交换网中的时延概述</h4><pre class="line-numbers language-none"><code class="language-none">以下 4 种时延总体累加起来是 节点总时延(total nodal delay) :<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. 节点处理时延(nodal processing delay) :检查分组首部和决定将该分组导向何处所需要的时间。2. 排队时延(queuing delay) :分组在链路上输出队列中等待传输的时间。3. 传输时延(transmission delay) :将所有分组的比特推(传输)向链路所需要的时间,取决于分组长度和链路传输速率。4. 传播时延(propagation delay) :分组从链路起点传播到下一个路由器所需要的时间,取决于两台路由器之间的距离。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="1-4-2-排队时延和丢包"><a href="#1-4-2-排队时延和丢包" class="headerlink" title="1.4.2 排队时延和丢包"></a>1.4.2 排队时延和丢包</h4><p>排队时延是节点时延中最复杂和令人感兴趣的成分。</p><pre class="line-numbers language-none"><code class="language-none">当分组到达节点时队列已满,路由器将 丢弃(drop) 该分组,即该分组将会 丢失(lost) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="1-4-3-端到端时延"><a href="#1-4-3-端到端时延" class="headerlink" title="1.4.3 端到端时延"></a>1.4.3 端到端时延</h4><h4 id="1-4-4-计算机网络中的吞吐量"><a href="#1-4-4-计算机网络中的吞吐量" class="headerlink" title="1.4.4 计算机网络中的吞吐量"></a>1.4.4 计算机网络中的吞吐量</h4><p>任何瞬间的 <strong>瞬间吞吐量(instantaneous throughput)</strong> 是某主机接收到某文件的速率(以bps计)。</p><pre class="line-numbers language-none"><code class="language-none">一段时间内某主机接收某文件的平均速率是 平均吞吐量(averagethroughput) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>端到端之间的吞吐量是 <strong>瓶颈链路(bottleneck link)</strong> 的传输速率(瓶颈链路,即平均吞吐量最小的一段链路)。</p><h3 id="1-5-协议层次和它们的服务模型"><a href="#1-5-协议层次和它们的服务模型" class="headerlink" title="1.5 协议层次和它们的服务模型"></a>1.5 协议层次和它们的服务模型</h3><h4 id="1-5-1-分层的体系结构"><a href="#1-5-1-分层的体系结构" class="headerlink" title="1.5.1 分层的体系结构"></a>1.5.1 分层的体系结构</h4><p>网络设计者以 <strong>分层(layer)</strong> 的方式组织协议以及现实这些协议的网络硬件和软件。某层向上一层提供的 <strong>服务(service)</strong> ,即所谓的层的 <strong>服务模型(service model)</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">五层因特网 协议栈(protocol stack) ,用 自顶向下方法(top-down approach) 作介绍:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. 应用层(application layer) :网络应用及其应用层协议存留的地方,包括许多协议,如HTTP、SMTP和FTP。应用层的信息分组称为 报文(message) 。2. 运输层(transport layer) :提供了在应用程序端点之间传送应用层报文的服务,因特网中有两个运输层协议:TCP和UDP。运输层分组称为 报文段(segment) 。3. 网络层(network layer) :负责将称为 数据报(datagram) 的网络层分组从一台主机移动到另一台主机。源主机中的因特网运输层协议(如TCP和UDP)向网络层递交运输层报文段和目的地址。网络层包括了著名的IP协议。网络层通过一系列路由器在源和目的地之间发送分组,为了将分组从一个节点(主机或路由器)移动到路径上的下一个节点,网络层必须依靠链路层的服务。4. 链路层(link layer) :在每个节点,网络层将数据报下传给链路层,链路层沿着路径将数据报传递给下一个节点,在该下一个节点,链路层将数据报上传给网络层。链路层分组称为 帧(frame) 。链路层包括了以太网、Wi-Fi和点对点协议(PPP)。5. 物理层(physical layer) :链路层提供的服务是将整个帧从一个网络元素移动到邻近的网络元素,而物理层的任务是将该帧中的一个一个比特从一个节点移动到下一个节点。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>国际标准化组织(ISO)提出了大约为七层的计算机网络应用组织,称为 <strong>开放互连(OSI)</strong> 模型(自顶向下):</p><pre class="line-numbers language-none"><code class="language-none">1. 应用层2. 表示层(presentation layer) :使通信的应用程序能解释交换数据的含义,所提供服务包括数据压缩、数据加密以及数据描述。3. 会话层(session layer) :提供了数据交换的定界和同步功能,包括建立检查点和恢复方案的方法。4. 运输层5. 网络层6. 链路层7. 物理层<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="1-5-2-报文、报文段、数据报和帧"><a href="#1-5-2-报文、报文段、数据报和帧" class="headerlink" title="1.5.2 报文、报文段、数据报和帧"></a>1.5.2 报文、报文段、数据报和帧</h4><p><strong>封装(encapsulation)</strong> : <strong>应用层报文(application-layer message)</strong> 被传送给运输层,运输层<br>附上附加信息,构成了 <strong>运输层报文段(transport-layer segment)</strong> 传递至网络层,网络层增加了网络<br>层首部信息,形成了 <strong>网络层数据报(network-layer datagram)</strong> 并传递给链路层,链路层增加链路层<br>首部信息,使其成为 <strong>链路层帧(link-layer frame)</strong> 。在每一层,分组具有两种类型的字段:首部字段<br>和 <strong>有效载荷字段(payload field)</strong> ,有效载荷字段通常来自上一层的分组。</p><h3 id="1-6-攻击威胁下的网络"><a href="#1-6-攻击威胁下的网络" class="headerlink" title="1.6 攻击威胁下的网络"></a>1.6 攻击威胁下的网络</h3><h3 id="1-7-计算机网络和因特网的历史"><a href="#1-7-计算机网络和因特网的历史" class="headerlink" title="1.7 计算机网络和因特网的历史"></a>1.7 计算机网络和因特网的历史</h3><h2 id="2-应用层"><a href="#2-应用层" class="headerlink" title="2 应用层"></a>2 应用层</h2><h3 id="2-1-应用层协议原理"><a href="#2-1-应用层协议原理" class="headerlink" title="2.1 应用层协议原理"></a>2.1 应用层协议原理</h3><h4 id="2-1-1-网络应用程序体系结构"><a href="#2-1-1-网络应用程序体系结构" class="headerlink" title="2.1.1 网络应用程序体系结构"></a>2.1.1 网络应用程序体系结构</h4><p>主要有两种主流 <strong>应用程序体系结构(application architecture)</strong> :<strong>客户机/服务器体系结构(client-server architecture****)</strong> 和 <strong>P2P体系结构(P2P architecture)</strong> 。</p><h4 id="2-1-2-进程通信"><a href="#2-1-2-进程通信" class="headerlink" title="2.1.2 进程通信"></a>2.1.2 进程通信</h4><p>不同端系统上的进程通过跨越计算机网络交换 报文 而相互通信。发送进程创建并向网络中发送报文,接收进程接受这些报文并可能负责回送报文。</p><p>网络应用程序是由成对的进程组成,这些进程通过网络相互发送报文,两个进程一个标示为 <strong>客户机****(client)</strong>,一个标示为 <strong>服务器(server)</strong> 。</p><p>进程通过一个称为 <strong>套接字(socket)</strong> 的软件接口在网络上发送和接收报文,也称为应用程序和网络之间的 <strong>应用程序编程接口(Application Programming Interface,API)</strong> 。</p><h4 id="2-1-3-可供应用程序使用的运输服务"><a href="#2-1-3-可供应用程序使用的运输服务" class="headerlink" title="2.1.3 可供应用程序使用的运输服务"></a>2.1.3 可供应用程序使用的运输服务</h4><pre class="line-numbers language-none"><code class="language-none">1. 可靠数据传输(reliable data transfer)2. 吞吐量3. 定时4. 安全性<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-1-4-因特网提供的运输服务"><a href="#2-1-4-因特网提供的运输服务" class="headerlink" title="2.1.4 因特网提供的运输服务"></a>2.1.4 因特网提供的运输服务</h4><p>两个因特网运输层协议:</p><p><strong>1. TCP服务:</strong></p><p>(1) 面向连接服务,</p><p>(2) 可靠数据传输服务,</p><p>(3) 具有拥塞机制。</p><p><strong>2. UDP服务:</strong></p><p>无连接、不可靠且无拥塞机制。</p><p>因特网运输层协议缺少对吞吐量和定时保证的讨论。</p><p>为了识别接收进程,需要定义两种信息:1)该主机的名称或地址,2)用来指定目的主机上接收进程的标识。在因特网中,主机是用 <strong>IP地址(IP address)</strong> 进行标识的,目的地 <strong>端口号(port number)</strong> 能识别运行在主机上的接收进程。</p><h4 id="2-1-5-应用层协议"><a href="#2-1-5-应用层协议" class="headerlink" title="2.1.5 应用层协议"></a>2.1.5 应用层协议</h4><p><strong>应用层协议(application-layer protocol)</strong> 定义了运行在不同端系统上的应用程序进程如何相互传递报文,定义了交换的报文类型、各种报文类型的语法、字段的语义、进程何时如何发送报文及对报文进行响应的规则。</p><h4 id="2-1-6-本书涉及的网络应用"><a href="#2-1-6-本书涉及的网络应用" class="headerlink" title="2.1.6 本书涉及的网络应用"></a>2.1.6 本书涉及的网络应用</h4><h3 id="2-2-Web应用和HTTP协议"><a href="#2-2-Web应用和HTTP协议" class="headerlink" title="2.2 Web应用和HTTP协议"></a>2.2 Web应用和HTTP协议</h3><h4 id="2-2-1-HTTP概况"><a href="#2-2-1-HTTP概况" class="headerlink" title="2.2.1 HTTP概况"></a>2.2.1 HTTP概况</h4><p><strong>超文本传输协议(Hypertext Transfer Protocol,HTTP)</strong> 是Web的应用层协议,它是Web的核心,在 <strong>带内(in-band)</strong> 发送控制信息。</p><p><strong>Web页面(Web page)</strong> 是由对象组成的。 <strong>对象(object)</strong> 就是文件,如HTML文件、JPEG图形文件、Java小程序或视频片段文件,这些文件可以通过一个URL地址寻址。多数Web页面含有一个<strong>基本<br>HTML文件(base HTML file)</strong> 以及几个引用对象。每个URL地址由两部分组成:存放对象的服务器主机名和对象的路径名。因为 <strong>Web浏览器(Web browser)</strong> 实现了HTTP的客户机端,所以这种模型又称为 <strong>B/S(browser-server)</strong> 模型。 <strong>Web服务器(Web Server)</strong> 用于存储Web对象,每个对象由URL寻址。Web服务器实现了HTTP的服务器端。</p><p>当用户请求一个Web页面时,浏览器向服务器发出对该页面中所包含对象的HTTP请求报文,服务器接收请求并用包含这些对象的HTTP响应报文进行响应。</p><p>HTTP使用TCP作为它的支撑运输层协议。HTTP客户机发起一个与服务器的TCP连接,一旦连接建立,浏览器和服务器进程就可以通过套接字接口访问TCP。</p><p>因为一个HTTP服务器并不保存关于客户机的任何消息,所以HTTP是一个 <strong>无状态协议(stateless protocol)</strong> 。</p><h4 id="2-2-2-非持久连接和持久连接"><a href="#2-2-2-非持久连接和持久连接" class="headerlink" title="2.2.2 非持久连接和持久连接"></a>2.2.2 非持久连接和持久连接</h4><p>每个请求/响应对经一个单独的TCP连接发送,称为 <strong>非持久连接(non-persistent connection)</strong> 。<br>所有请求/响应对经相同的TCP连接发送,称为 <strong>持久连接(persistent connection)</strong> 。<br>HTTP可以使用非持久连接也可使用持久连接,默认方式下使用持久连接。</p><p><strong>往返时间(Round-Trip Time,RTT)</strong> ,即一个小分组从客户机到服务器再回到客户机所花费的时间。</p><pre class="line-numbers language-none"><code class="language-none">RTT包括分组传播时延、分组在中间路由器和交换机上的排队时延以及分组处理时延。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>HTTP持久连接的情况下,服务器在发送响应后保持该TCP连接打开,经过一定时间间隔仍未被使用,HTTP服务器就关闭该连接。</p><h4 id="2-2-3-HTTP报文格式"><a href="#2-2-3-HTTP报文格式" class="headerlink" title="2.2.3 HTTP报文格式"></a>2.2.3 HTTP报文格式</h4><ol><li><strong>HTTP请求报文:</strong></li></ol><pre class="line-numbers language-none"><code class="language-none">第一行叫做 请求行(request line) ,其后继的行叫做 首部行(header line) ,然后是 请求空行 ,之后就是 实体主体(entity body) 。请求行有 3 个字段:方法字段(GET/POST/HEAD/PUT/DELETE)、URL字段、HTTP协议版本字段。2. HTTP响应报文:分成三个部分:一个初始 状态行(status line) 、六个 首部行(header line) ,然后是 实体主题(entity body) 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-2-4-用户与服务器的交互:cookie"><a href="#2-2-4-用户与服务器的交互:cookie" class="headerlink" title="2.2.4 用户与服务器的交互:cookie"></a>2.2.4 用户与服务器的交互:cookie</h4><p>HTTP服务器是无状态的,然而一个Web站点通常希望能够识别用户,既可能是因为服务器想限制用户的访问,又可能是因为它想把内容与用户身份关联起来,为此HTTP使用了 <strong>cookie</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">cookie技术有 4 个组成部分:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. 在HTTP响应报文中有一个cookie首部行,2. 在HTTP请求报文中有一个cookie首部行,3. 在用户端系统中保留有一个cookie文件,由用户的浏览器管理,4. 在Web站点有一个后端数据库。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-2-5-Web缓存"><a href="#2-2-5-Web缓存" class="headerlink" title="2.2.5 Web缓存"></a>2.2.5 Web缓存</h4><p><strong>Web缓存器(Web cache)</strong> 也叫 <strong>代理服务器(proxy server)</strong> ,它是能够代表初始Web服务器来满足HTTP请求的网络实体。Web缓存器有自己的磁盘存储空间,并在该存储空间中保存最近请求过的对象的拷贝。</p><h4 id="2-2-6-条件GET方法"><a href="#2-2-6-条件GET方法" class="headerlink" title="2.2.6 条件GET方法"></a>2.2.6 条件GET方法</h4><pre class="line-numbers language-none"><code class="language-none">HTTP使用 条件GET(conditional GET) 方法来证实缓存器的对象是最新的。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="2-3-文件传输协议:FTP"><a href="#2-3-文件传输协议:FTP" class="headerlink" title="2.3 文件传输协议:FTP"></a>2.3 文件传输协议:FTP</h3><p><strong>文件传输协议(File Transfer Protocol,FTP)</strong> 是因特网应用层协议,使用两个并行的TCP连接来传输文件,一个是 <strong>控制连接(control connection)</strong> ,一个是 <strong>数据连接(data)</strong> 。</p><p>控制连接用于在两个主机之间传输控制信息,如用户标识、口令、改变远程目录的命令以及put和get文件命令;数据连接用于实际传输一个文件。FTP协议使用一个分离的控制连接,所以FTP的控制信息是 <strong>带外(out-of-band)</strong> 传送的。控制连接贯穿整个用户会话期间,针对会话的每一次文件传输都需<br>要建立一个新的数据连接。</p><pre class="line-numbers language-none"><code class="language-none">FTP服务器必须在整个会话期间保留用户的 状态(state) 信息。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="2-4-因特网中的电子邮件"><a href="#2-4-因特网中的电子邮件" class="headerlink" title="2.4 因特网中的电子邮件"></a>2.4 因特网中的电子邮件</h3><p>因特网电子邮件系统有三个主要组成部分: <strong>用户代理(user agent)</strong> 、 <strong>邮件服务器(mail server)</strong> 和 <strong>简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)</strong> 。</p><p>每个接收方在电子邮件体系结构中的某个服务器上有一个 <strong>邮箱(mailbox)</strong> 。一个典型的邮件发送过程是:从发送方的用户代理开始,传送到发送方的邮件服务器,再传输到接收方的邮件服务器,然后在这里被分发到接收方的邮箱中。</p><p>若发送方的服务器不能将邮件交付到接收方的服务器,发送方的邮件服务器在一个<strong>报文队列(message queue)</strong> 保持该报文并在以后尝试再次发送,通常每 30 分钟左右进行一次尝试,若几天后仍不能成功,服务器删除该报文并以电子邮件的形式通知发送方。</p><h4 id="2-4-1-SMTP"><a href="#2-4-1-SMTP" class="headerlink" title="2.4.1 SMTP"></a>2.4.1 SMTP</h4><p>SMTP限制所有邮件报文主体部分(不只是其首部)只能采用简单的 7 位ACSII码表示。</p><p>SMTP一般不使用中间邮件服务器发送邮件。</p><p>进行文件传送时,SMTP使用持久连接。</p><h4 id="2-4-2-与HTTP的对比"><a href="#2-4-2-与HTTP的对比" class="headerlink" title="2.4.2 与HTTP的对比"></a>2.4.2 与HTTP的对比</h4><p>SMTP和HTTP都用于从一台主机向另一台主机传送文件。</p><pre class="line-numbers language-none"><code class="language-none">SMTP是一个 推协议(push protocol) ,HTTP是一个 拉协议(pull protocol) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">SMTP把所有报文对象放在一个报文之中,HTTP把对象封装到它自己的HTTP响应报文中。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="2-4-3-邮件报文格式和MIME"><a href="#2-4-3-邮件报文格式和MIME" class="headerlink" title="2.4.3 邮件报文格式和MIME"></a>2.4.3 邮件报文格式和MIME</h4><p><strong>多用途互联网邮件扩展(Multipurpose Internet Mail Extensions,MIME)</strong> 类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型。</p><h4 id="2-4-4-邮件访问协议"><a href="#2-4-4-邮件访问协议" class="headerlink" title="2.4.4 邮件访问协议"></a>2.4.4 邮件访问协议</h4><p>目前有多个流行的邮件访问协议:<strong>第三版的邮局协议(Post Office Protocol-version 3,<br>POP3)</strong> 、 <strong>因特网邮件访问协议(Internet Mail Access Protocol,IMAP)</strong> 以及HTTP。</p><pre class="line-numbers language-none"><code class="language-none">1. POP3:三个步骤:特许、事务处理、更新。在特许阶段,用户代理发送(以明文形式)用户名和口令以鉴别用户。事务处理阶段,用户代理取回报文;这个阶段中,用户代理还能对邮件进行操作,如做出报文删除标记,取消报文删除标记,以及获取邮件的统计信息。更新阶段,在客户机发出quit命令之后,目的是结束该POP3会话;此时,邮件服务器删除哪些被标记为删除的报文。在用户代理与邮件服务器使用POP3进行会话时,POP3会保留一些状态信息,特别是哪些用户报文被标记为删除了。会话期间,状态信息并不会被携带,简化了POP3服务的实现。2. IMAP:用户想要在任何一台机器上对所有报文进行访问,他想要实现使用一个再远程服务器上的层次文件夹,而POP3不可能做到这一点,POP3协议没有给用户提供任何创建远程文件夹及为报文指派文件夹的方法。IMAP服务器把每个报文与一个文件夹联系起来;当报文第一次到达服务器时,它是放在收件人的收件箱文件夹里。收件人则可以把邮件移到一个新的、用户创建的文件夹,或阅读邮件、删除邮件等。IMAP为用户提供了创建文件夹以及在文件夹之间移动邮件的命令。此外,IMAP协议还为用户提供了在远程文件夹中查询邮件的命令,按指定条件去查询匹配的邮件。IMAP的另外一个重要特性是,它具有允许用户代理读取报文组件的命令。3. 基于Web的电子邮件:使用HTTP。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="2-5-DNS:因特网的目录服务"><a href="#2-5-DNS:因特网的目录服务" class="headerlink" title="2.5 DNS:因特网的目录服务"></a>2.5 DNS:因特网的目录服务</h3><p>主机的一种识别方法是用它的 <strong>主机名(hostname)</strong> (可能由不定长的字母数字组成,路由很难处理),另一种识别方法是使用所谓 <strong>IP地址(IP address)</strong> 。</p><h4 id="2-5-1-DNS提供的服务"><a href="#2-5-1-DNS提供的服务" class="headerlink" title="2.5.1 DNS提供的服务"></a>2.5.1 DNS提供的服务</h4><p><strong>域名系统(Domain Name System,DNS)</strong> 是:一个由分层的 <strong>DNS服务器(DNS server)</strong> 实现<br>的分布式数据库、一个允许主机查询分布式数据库的应用协议。</p><p>DNS还提供了一些重要的服务: <strong>主机别名(host aliasing)</strong> (有一个 <strong>规范主机名(canonical hostname)</strong> )、 <strong>邮件服务器别名(mail server aliasing)</strong> 、 <strong>负载分配(load distributing)</strong> 。</p><h4 id="2-5-2-DNS工作机理概述"><a href="#2-5-2-DNS工作机理概述" class="headerlink" title="2.5.2 DNS工作机理概述"></a>2.5.2 DNS工作机理概述</h4><h5 id="1-分布式、层次数据库:"><a href="#1-分布式、层次数据库:" class="headerlink" title="1. 分布式、层次数据库:"></a>1. 分布式、层次数据库:</h5><h4 id="三类层次结构DNS服务器:"><a href="#三类层次结构DNS服务器:" class="headerlink" title="三类层次结构DNS服务器:"></a>三类层次结构DNS服务器:</h4><p>(1) 根DNS服务器 ,</p><p>(2) 顶级域(TLD)服务器 ,</p><p>(3) 权威DNS服务器 。</p><pre class="line-numbers language-none"><code class="language-none">本地DNS服务器(local DNS server) 起代理的作用。依次查询浏览器DNS缓存、系统DNS缓存、本地host文件并确认未查找到后,请求主机向本地DNS服务器发送查询请求,本地DNS服务器依次从根DNS服务器、TLD DNS服务器、权威DNS服务器获取相关信息,然后本地DNS服务器返回相关映射至请求主机。从请求主机到本地DNS服务器的查询是 递归查询(recursive query) ,其余的查询是 迭代查询(iterative query) 。2. DNS缓存(DNS caching)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-5-3-DNS记录和报文"><a href="#2-5-3-DNS记录和报文" class="headerlink" title="2.5.3 DNS记录和报文"></a>2.5.3 DNS记录和报文</h4><p>实现DNS分布式数据库的所有DNS服务器共同存储着 <strong>资源记录(Resource Record,RR)</strong> ,RR提供了主机名到IP地址的映射。</p><pre class="line-numbers language-none"><code class="language-none">DNS报文分DNS查询和回答报文,拥有相同格式。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">在DNS数据库中插入记录需要 注册登记机构(register) 的服务。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="2-6-P2P应用"><a href="#2-6-P2P应用" class="headerlink" title="2.6 P2P应用"></a>2.6 P2P应用</h3><h4 id="2-6-1-P2P文件分发"><a href="#2-6-1-P2P文件分发" class="headerlink" title="2.6.1 P2P文件分发"></a>2.6.1 P2P文件分发</h4><h5 id="1-P2P体系结构的扩展性:"><a href="#1-P2P体系结构的扩展性:" class="headerlink" title="1. P2P体系结构的扩展性:"></a>1. P2P体系结构的扩展性:</h5><pre class="line-numbers language-none"><code class="language-none">分发时间(distribution time) 是多个对等方得到文件拷贝所需要的时间。在P2P体系结构中,随着要获得文件拷贝的对等方的数量增加,最小分发时间的增速远小于成正比的线性增长速。2. BitTorrent:BitTorrent是一种用于文件分发的流行P2P协议。参与一个特定文件分发的所有对等方的集合称为一个 洪流(torrent) 。在一个洪流中,对等方彼此下载等长度的文件块。每个洪流具有一个基础设施节点,称为 追踪器(tracker) 。追踪器每次会随机地从参与对等方集合中选择一些对等方,并将其IP地址发送给某个除外的对等方,接受到此列表的对等方会与列表中的对等方并行建立TCP连接,其中所有与其连接的对等方称为“邻近对等方”。在任何时刻,每个对等方都具有来自某文件快的子集,且不同的对等方具有不同的文件块子集。一个对等方会从邻近对等方中选择自己没有且拷贝数量最少的文件块进行下载,这种技术称为 最稀罕优先(rarest first) 技术。BitTorrent的对换算法有效地消除了 搭免费车(free-riding) 问题(只下载不上载,白嫖)。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-6-2-在P2P区域中搜索信息"><a href="#2-6-2-在P2P区域中搜索信息" class="headerlink" title="2.6.2 在P2P区域中搜索信息"></a>2.6.2 在P2P区域中搜索信息</h4><p><strong>P2P应用程序中的信息索引,即信息到主机位置的映射。</strong></p><pre class="line-numbers language-none"><code class="language-none">1. 集中式索引(centralized index) :具有集中式索引的P2P文件共享系统实际上是一个P2P和客户机/服务器混合体系结构。每一个对等方通知和更新或向集中式索引服务器发出查询的请求,各对等方相互间连接进行文件传输。集中式索引的缺点:单点(索引服务器)故障、性能瓶颈和基础设施费用、侵犯版权。2. 查询洪泛:对等方新成了一个抽象的逻辑网络,该网络被称为 覆盖网络(overlay network) 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">对等方通过覆盖网络进行索引查询叫做 查询洪泛(query flooding) 。为避免某个对等方接受过多的查询请求,使用了 范围受限查询洪泛(limited scope query flooding) ,即每个对等点设置查询计数器及其上限。3. 层次覆盖:层次覆盖设计(hierarchical overlay design) 结合了集中式索引与查询洪泛的优秀特征,超级对等方组成覆盖网络,每个超级对作为集中式索引服务器等方连接着多个普通对等方。分布式散列表(Distributed Hash Table,DHT) :产生一个全分布式索引,该索引将文件标识符映射到文件位置;允许用户(原则上)确定文件的所有位置,而不会产生过量的搜索流量。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-6-3-案例学习:Skype的P2P因特网电话"><a href="#2-6-3-案例学习:Skype的P2P因特网电话" class="headerlink" title="2.6.3 案例学习:Skype的P2P因特网电话"></a>2.6.3 案例学习:Skype的P2P因特网电话</h4><h3 id="2-7-TCP套接字编程"><a href="#2-7-TCP套接字编程" class="headerlink" title="2.7 TCP套接字编程"></a>2.7 TCP套接字编程</h3><h4 id="2-7-1-TCP套接字编程"><a href="#2-7-1-TCP套接字编程" class="headerlink" title="2.7.1 TCP套接字编程"></a>2.7.1 TCP套接字编程</h4><p>客户机与服务器TCP三次握手后,创建一个新的TCP连接称为 **连接套接字(connection socket) 。</p><pre class="line-numbers language-none"><code class="language-none">TCP在客户机进程和服务器进程之间提供了 可靠字节流服务(reliable byte-stream service) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>流(stream)</strong> 是流入和流出进程的字符序列。对一个进程来说,每条流或者是 <strong>输入流(input<br>stream)</strong> ,或者是 <strong>输出流(output stream)</strong> 。</p><h4 id="2-7-2-一个Java客户机-服务器应用程序例子"><a href="#2-7-2-一个Java客户机-服务器应用程序例子" class="headerlink" title="2.7.2 一个Java客户机/服务器应用程序例子"></a>2.7.2 一个Java客户机/服务器应用程序例子</h4><h3 id="2-8-UDP套接字编程"><a href="#2-8-UDP套接字编程" class="headerlink" title="2.8 UDP套接字编程"></a>2.8 UDP套接字编程</h3><p>UDP无初始握手阶段,是一种无连接服务。</p><p>UDP为通信进程提供了不可靠的运输服务。</p><p>UDP无流与套接字相联系。</p><h3 id="2-9-小结"><a href="#2-9-小结" class="headerlink" title="2.9 小结"></a>2.9 小结</h3><p>协议是网络中的核心概念。</p><h2 id="3-运输层"><a href="#3-运输层" class="headerlink" title="3 运输层"></a>3 运输层</h2><h3 id="3-1-概述和运输层服务"><a href="#3-1-概述和运输层服务" class="headerlink" title="3.1 概述和运输层服务"></a>3.1 概述和运输层服务</h3><pre class="line-numbers language-none"><code class="language-none">运输层协议为运行在不同主机上的应用进程之间提供了 逻辑通信(logic communication) 功能。运输层协议是在端系统中而不是网络路由器中实现的。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在发送方,运输层将接受到的来自发送应用程序的报文转换成运输层分组,即运输层 报文段(segment),然后运输层将这些报文段传递给传递给网络层。在接收方,网络层从数据报中提取运输层报文段,并将该报文段向上交给运输层,运输层则处理接收到的报文段,使得接收方应用进程可应用该报文段中的数据。</p><h4 id="3-1-1-运输层和网络层的关系"><a href="#3-1-1-运输层和网络层的关系" class="headerlink" title="3.1.1 运输层和网络层的关系"></a>3.1.1 运输层和网络层的关系</h4><p>在因特网协议栈中,运输层刚好位于网络层之上。运输层为运行在不同主机上的进程之间提供了逻辑通信,而网络层则提供了主机之间的逻辑通信。</p><h4 id="3-1-2-因特网运输层概述"><a href="#3-1-2-因特网运输层概述" class="headerlink" title="3.1.2 因特网运输层概述"></a>3.1.2 因特网运输层概述</h4><p>一般而言因特网是一个TCP/IP网络,为应用层提供了两种截然不同的运输层协议:UDP(用户数据报协议,为调用它的应用程序提供了一种不可靠的、无连接服务)和TCP(传输控制协议,为调用它的应用程序提供了一种可靠的面向连接的服务)。开发人员在创建套接字时必须制定是选择UDP还是选择TCP。</p><pre class="line-numbers language-none"><code class="language-none">运输层 报文段(segment) 称为报文段。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>因特网网络层协议有一个名字叫IP,全称是网际协议,IP为主机之间提供了逻辑通信。IP的服务模<br>型是 <strong>尽力而为交付服务(best-effort delivery service)</strong> ,所以IP被称为 <strong>不可靠服务(unreliable service)</strong> 。</p><p>UDP与TCP最基本的任务是,将两个端系统间IP的交付服务扩展为运行在两个端系统上的进程之间的交付服务。将主机间交付扩展到进程间交付,称为<strong>运输层的多路复用(transport-layer multiplexing)</strong> 与 <strong>多路分解(demultiplexing)</strong> 。</p><p>TCP为应用程序提供了几种附加服务: <strong>可靠数据传输(reliable data transfer)</strong> 、 <strong>拥塞控制(congestion control)</strong> 。</p><h3 id="3-2-多路复用与多路分解"><a href="#3-2-多路复用与多路分解" class="headerlink" title="3.2 多路复用与多路分解"></a>3.2 多路复用与多路分解</h3><p>作为网络应用的一部分,进程有一个或多个 <strong>套接字(socket)</strong> ,它相当于从网络向进程传递数据和从进程向网络传递数据的门户。</p><p>将运输层报文段中的数据交付到正确的套接字的工作称为 <strong>多路分解(demultiplexing)</strong> 。从源主机的不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层的工作称为 <strong>多路复用(multiplexing)</strong> 。</p><h3 id="3-3-无连接运输:UDP"><a href="#3-3-无连接运输:UDP" class="headerlink" title="3.3 无连接运输:UDP"></a>3.3 无连接运输:UDP</h3><p>UDP是一种无连接的运输层协议,因为在使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有进行握手。如果应用程序使用的运输层协议是UDP,则应用程序几乎是直接与IP打交道的。</p><p>UDP从应用进程得到数据,附加上用于多路复用/分解服务的源端口号和目的端口号及长度和检验和(即报文段首部)形成报文段,然后将报文段交给网络层。网络层将该运输层报文段封装在IP数据报中,然后尽力而为的尝试将此报文段交付给接收主机。若该报文段到达接受主机,UDP使用目的端口号将报文段中的数据交付给正确的应用进程。</p><p>在进行UDP传输时,需要明确一个是发送端,一个是接收端:</p><p>UDP的发送端:</p><ol><li><p>建立UDP的套接字服务,创建对象时如果没有明确端口,系统会自动分配一个未被使用的端口。</p></li><li><p>明确要发送的具体数据。</p></li><li><p>将数据封装成了数据包。</p></li><li><p>用套接字服务的send方法将数据包发送出去。</p></li><li><p>关闭资源。</p></li></ol><pre class="line-numbers language-none"><code class="language-none">UDP的接收端:1. 创建UDP的套接字服务,必须要明确一个端口,作用在于,只有发送到这个端口的数据才是这个接收端可以处理的数据。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><ol start="2"><li><p>定义数据包,用于存储接收到数据。</p></li><li><p>通过套接字服务的接收方法将收到的数据存储到数据包中。</p></li><li><p>通过数据包的方法获取数据包中的具体数据内容,比如ip、端口、数据等等。</p></li><li><p>关闭资源。</p></li></ol><p>UDP的优势:</p><ol><li>应用层能更好控制要发送的数据和发送时间。</li><li>无需连接建立。</li><li>无连接状态。</li><li>分组首部开销小。</li></ol><p>常见应用层协议对应的运输层协议:</p><pre class="line-numbers language-none"><code class="language-none">SMTP->TCP;Telnet->TCP;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">HTTP->TCP;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">FTP->TCP;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">NFS->通常UDP;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">流式多媒体专用协议->UDP或TCP;因特网电话专用协议->UDP或TCP;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">SNMP->通常UDP;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">RIP->通常UDP;<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">DNS->通常UDP。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="3-3-1-UDP报文段结构"><a href="#3-3-1-UDP报文段结构" class="headerlink" title="3.3.1 UDP报文段结构"></a>3.3.1 UDP报文段结构</h4><p>UDP的报文段结构分为UDP首部与数据字段,首部分为源端口号、目的端口号、长度和检验和,数据字段即来自应用层的报文。</p><h4 id="3-3-2-UDP检验和"><a href="#3-3-2-UDP检验和" class="headerlink" title="3.3.2 UDP检验和"></a>3.3.2 UDP检验和</h4><p>UDP检验和提供了差错检测功能,即检验和用于确定当UDP报文段从源达到目的地时,其中的比特</p><h4 id="是否发生了改变。"><a href="#是否发生了改变。" class="headerlink" title="是否发生了改变。"></a>是否发生了改变。</h4><h3 id="3-4-可靠数据传输的原理"><a href="#3-4-可靠数据传输的原理" class="headerlink" title="3.4 可靠数据传输的原理"></a>3.4 可靠数据传输的原理</h3><p>数据可以通过一条可靠的信道进行传输(不会有传输数据比特收到损坏或丢失,而且所有数据都是</p><p>按照其发送顺序进行传送的),实现这种服务抽象是 <strong>可靠数据传输协议(reliable data transfer protocol)</strong> 的责任。</p><p>以下所提到的是 <strong>单向数据传输(unidirectional data transfer)</strong> 即数据传输是从发送方到接收方的,可靠的 <strong>双向数据传输(bidirectional data transfer)</strong> 即双全工数据传输从概念上理解并不难。</p><h4 id="3-4-1-构造可靠数据传输协议"><a href="#3-4-1-构造可靠数据传输协议" class="headerlink" title="3.4.1 构造可靠数据传输协议"></a>3.4.1 构造可靠数据传输协议</h4><pre class="line-numbers language-none"><code class="language-none">1. 完全可靠信道上的可靠数据传输:rdt1.0 :rdt的发送方只通过rdt_send(data)从较高层接收的数据,产生一个包含该数据的分组(经由make_pkt(data)动作),并将分组发送到信道中,rdt_send(data)事件是由较高层应用是过程调用(如rdt_send())产生的。在接收方,rdt通过rdt_rcv(packet)事件从底层信道接收一个分组,从分组中取出数据(经由extract(packet, data)动作),并将数据上传给较高层(通过<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">deliver_data(data)动作),rdt_rcv(packet)事件是由较低层协议的过程调用(如rdt_rcv())产生的。2. 具有比特差错信道上的可靠数据传输:rdt2.0 :更现实的底层信道模型是分组中的比特可能受损。基于由接收方发送回确认是否正确收到的消息若否则需重传的机制的可靠数据传输协议称为 自动重传请求(Automatic Repeat Request,ARQ)协议 。ARQ中还需另外三种协议来处理比特差错:1)差错检测,2)接收方反馈,3)重传。还要考虑接收方的 肯定/否定确认(ACK/NAK) 报文是否受损(引入序号机制,表示接收方收到的反馈的状态码),还可以引入冗余ACK机制(接收方发送两次ACK报文代替发送NAK报文至发送方)。3. 具有比特差错的丢包信道上的可靠数据传输:rdt3.0 :除了比特受损外,底层信道还会丢包。引入定时器,发送方超时未收到非冗余ACK报文则重传。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="3-4-2-流水线可靠数据传输协议"><a href="#3-4-2-流水线可靠数据传输协议" class="headerlink" title="3.4.2 流水线可靠数据传输协议"></a>3.4.2 流水线可靠数据传输协议</h4><p>不是用停等方式运行,允许发送方可以在等待确认之前发送多个报文。因为从发送方向接收方输送</p><p>的众多分组可以被看成是填充到一条流水线中,故这种技术称为 <strong>流水线(pipelining)</strong> 。解决流水线的差错恢复有两种基本办法: <strong>回退N步(Go-Back-N,GBN)</strong> 和 <strong>选择重传(Selective Repeat,SR)</strong> 。</p><h4 id="3-4-3-回退N步"><a href="#3-4-3-回退N步" class="headerlink" title="3.4.3 回退N步"></a>3.4.3 回退N步</h4><p>当接收方检测到发送的序号不对时,要求发送方发送的序号从最后一个确认的编号开始,但是如果发送方的计时器超时,就要重发该帧与后面的N帧。</p><h4 id="3-4-4-选择重传"><a href="#3-4-4-选择重传" class="headerlink" title="3.4.4 选择重传"></a>3.4.4 选择重传</h4><p>有个窗口,当数据乱序到来的时候也不丢弃,先保存着,发送端的计时器就会超时,之后就会重新发送了。当接收到收到了重传分组那么滑动窗口就向后移。</p><h3 id="3-5-面向连接的运输:TCP"><a href="#3-5-面向连接的运输:TCP" class="headerlink" title="3.5 面向连接的运输:TCP"></a>3.5 面向连接的运输:TCP</h3><h4 id="3-5-1-TCP连接"><a href="#3-5-1-TCP连接" class="headerlink" title="3.5.1 TCP连接"></a>3.5.1 TCP连接</h4><p>TCP是 <strong>面向连接(connection-oriented)</strong> 的,这是因为在一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立确保数据传输所需的参数。</p><pre class="line-numbers language-none"><code class="language-none">TCP连接提供的是 全双工服务(full-duplex service) ,也总是 点对点(point-to-point) 的。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">TCP进行传输之间要进行三次握手建立连接,不需要传输的时候会进行四次挥手断开连接。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>TCP可从缓存中取出并放入报文段中的数据数量受限于 <strong>最大报文段长度(Maximum Segment Size,MSS)</strong> 。MSS通常根据最初确定的最大链路层帧长度来设置,本地发送主机发送长度是这样的帧,即所谓 <strong>最大传输单元(Maximum Transmission Unit,MTU)</strong> ,接着设置该MSS以保证一个TCP报文段(当封装在一个IP数据报中时)适合单个链路层帧。</p><p>TCP将客户机数据加一个TCP首部,从而形成了多个 <strong>TCP报文段(TCP segment)</strong> ,这些报文段被下传给网络层,网络层将其分别封装在网络层IP数据报中。</p><h4 id="3-5-2-TCP报文段结构"><a href="#3-5-2-TCP报文段结构" class="headerlink" title="3.5.2 TCP报文段结构"></a>3.5.2 TCP报文段结构</h4><p>TCP报文段首部组成部分:</p><pre class="line-numbers language-none"><code class="language-none">1. 源端口号和目的端口号(source and destination port number) :用于多路复用/多路分解来自或送至上层应用的数据;2. 检验和字段(checksum filed) ;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">3. 32 比特的 序号字段(sequence number field) 和 32 比特的 确认号字段(acknowledgement number filed) :被TCP发送方和接收方用来实现可靠数据传输服务;4. 16 比特的 接收窗口(receive window)字段 :用于流量控制;5. 4 比特的 首部长度字段(header length field) :指示了以 32 比特的字为单位的TCP首部长度(由于TCP选项字段原因,TCP首部的长度是可变的,通常选项字段为空,所以一般TCP首部的长度就是 20 字节);6. 可选与变长的 选项字段(flag field) :用于当发送方与接收方协商最大报文段长度(MSS),或在高速网络环境下用作窗口调节因子时使用(首部字段中还定义了一个时间戳选项);7. 比特的 标志字段(flag field) :ACK比特用于指示确认字段中的值是有效的(即该报文段包括一个对已被成功接受报文段的确认),RST、SYN和FIN比特用于连接建立和拆除,当PSH比特被设置时就指示接收方应立即将数据交给上层,URG比特用来指示报文段里存在着被发送方的上层实体置为“紧急”的数据(紧急数据的最后一个字节由 16 比特的 紧急数据指针字段(urgent data pointer field) 指出,当紧急数据存在并给出指向紧急数据尾的指针时TCP必须通知接收方的上层实体)。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="3-5-3-往返时延的估计与超时"><a href="#3-5-3-往返时延的估计与超时" class="headerlink" title="3.5.3 往返时延的估计与超时"></a>3.5.3 往返时延的估计与超时</h4><ol><li>估计往返时延:</li></ol><pre class="line-numbers language-none"><code class="language-none">RTT表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认,不包含数据传输时间)总共经历的时延,由三个部分决定:链路的传播时延、末端系统的处理时延、路由器的缓存中的排队和处理时延。随着路由器的拥塞和端系统负载的变化,RTT估计值也会变化,一直刷新 指数加权移动平均(Exponential Weighted Moving Average,EWMA) 设置和管理重传超时间隔:根据RTT估计值而设置,波动较大时则大,波动较小时则小。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="3-5-4-可靠数据传输"><a href="#3-5-4-可靠数据传输" class="headerlink" title="3.5.4 可靠数据传输"></a>3.5.4 可靠数据传输</h4><p>TCP发送方的三个与发送和重传有关的事件:</p><ol><li><p>TCP从上层应用程序接收数据,将数据封装在一个报文段中,然后交给IP。</p></li><li><p>超时后,TCP重传超时报文,然后,重启定时器。</p></li><li><p>收到ACK后,将其中确认号与发送方的最早未被确认的字节序号比较,若确认号大于最早未被确认的字节序号则在确认一个或多个先前未被确认的报文段且会更新最早未被确认的字节序号,反之重启定时器。</p></li></ol><p><strong>TCP重传:</strong></p><ol><li><p>由于确认报文丢失而重传</p></li><li><p>连续发送的报文段的确认报文延迟而先重传前一个</p></li><li><p>累计确认避免了第一个报文段的重传</p></li></ol><p>TCP使用优雅的方式,每个发送方的重传都是经过越来越长的时间间隔后进行的。</p><p>超时重传存在的问题之一就是超时周期可能较长,当一个报文段丢失时,通过超时重传来恢复报文,就会增加端到端的时延,但是可以通过检测收到的冗余ACK来进行对丢失报文段的重传。当发送方收到 3 个冗余ACK,就说明被确认过三次的报文段之后的那个报文段已经丢失,TCP就执行 快速重传</p><p><strong>(fast retransmit)</strong> ,在丢失报文段定时器超时之前重传丢失报文段。</p><pre class="line-numbers language-none"><code class="language-none">TCP的差错恢复机制为GBN协议和SR协议的混合体。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="3-5-5-流量控制"><a href="#3-5-5-流量控制" class="headerlink" title="3.5.5 流量控制"></a>3.5.5 流量控制</h4><p>TCP为应用程序提供了 <strong>流量控制服务(flow-control service)</strong> 以消除发送方使接收方缓存溢出的可能性,即让发送方的发送速率不要太快,要让接收方来得及接收。</p><p>TCP通过让发送方维护一个称为 <strong>接收窗口(receive window)</strong> 的变量来提供流量控制。接收窗口用于告诉发送方,该接收方还有多少可用的缓存空间。因为TCP是全双工通信,在连接两端的发送方都各自维护一个接收窗口。</p><h4 id="3-5-6-TCP连接管理"><a href="#3-5-6-TCP连接管理" class="headerlink" title="3.5.6 TCP连接管理"></a>3.5.6 TCP连接管理</h4><p><strong>TCP建立连接( 三次握手 )全过程:</strong></p><ol><li><p>客户端发送SYN给服务器,说明客户端请求建立连接;</p></li><li><p>服务端收到客户端发的SYN,并回复SYN+ACK给客户端(同意建立连接);</p></li><li><p>客户端收到服务端的SYN+ACK后,回复ACK给服务端(表示客户端收到了服务端发的同意报文);</p></li><li><p>服务端收到客户端的ACK,连接已建立,可以数据传输。</p></li></ol><p>TCP 建立连接要进行三次握手的原因:因为信道不可靠,而TCP想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值;因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信;为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。</p><p><strong>TCP释放连接( 四次握手 )全过程:</strong></p><ol><li><p>客户端发送FIN给服务器,说明客户端不必发送数据给服务器了(请求释放从客户端到服务器的连接);</p></li><li><p>服务器接收到客户端发的FIN,并回复ACK给客户端(同意释放从客户端到服务器的连接);</p></li><li><p>客户端收到服务端回复的ACK,此时从客户端到服务器的连接已释放(但服务端到客户端的连接还未释放,并且客户端还可以接收数据);</p></li><li><p>服务端继续发送之前没发完的数据给客户端;</p></li><li><p>服务端发送FIN+ACK给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放);</p></li><li><p>客户端收到服务端的FIN+ACK,并回复ACK给客户端(同意释放从服务端到客户端的连接);</p></li><li><p>服务端收到客户端的ACK后,释放从服务端到客户端的连接。</p></li></ol><p>TCP 释放连接要进行四次握手的原因:因为TCP是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向</p><p>客户端的连接关闭(三四次挥手),所以TCP释放连接时服务器的ACK和FIN是分开发送的(中间隔着数据传输),而TCP建立连接时服务器的ACK和SYN是一起发送的(第二次握手),所以TCP建立连接需要三次,而释放连接则需要四次。</p><p>TCP 连接时可以 ACK 和 SYN 一起发送而释放时则 ACK 和 FIN 分开发送的原因:因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端FIN请求(服务端发送ACK),然后</p><p>数据传输,传输完成后,服务端再提出FIN请求(服务端发送FIN);而连接时则没有中间的数据传输,因此连接时可以ACK和SYN一起发送。</p><p>客户端释放最后需要 TIME-WAIT 等待 2MSL 的原因:为了保证客户端发送的最后一个ACK报文能够到达服务端,若未成功到达,则服务端超时重传FIN+ACK报文段,客户端再重传ACK,并重新计时;防已失效的连接请求报文段出现在本连接中,TIME-WAIT持续2MSL可使本连接持续的时间内所产生的所有报文段都从网络中消失,这样可使下次连接中不会出现旧的连接报文段。</p><h3 id="3-6-拥塞控制原理"><a href="#3-6-拥塞控制原理" class="headerlink" title="3.6 拥塞控制原理"></a>3.6 拥塞控制原理</h3><p><strong>拥塞(congestion)</strong> 现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象.</p><h4 id="3-6-1-拥塞原因与开销"><a href="#3-6-1-拥塞原因与开销" class="headerlink" title="3.6.1 拥塞原因与开销"></a>3.6.1 拥塞原因与开销</h4><ol><li>两个发送方和一个具有无穷大缓存的路由器:</li></ol><p>拥塞网络的一种开销,即当分组到达速率接近链路容量时,分组将经历较长的排队时延。</p><ol start="2"><li>两个发送方和一个具有有限缓存的路由器:</li></ol><p>拥塞网络的另外一种开销,即发送方必须执行重传以补偿因为缓存溢出而丢弃(丢失)的分组。</p><p>发送方在遇到大时延时所进行的不必要重传,导致路由器需要利用其链路带宽来转发不 必要的分组。</p><ol start="3"><li>四个发送方、具有有限缓存的多台路由器和多跳路径:</li></ol><p>拥塞的另一种开梢,即当一个分组沿一条路径被丢弃时每个上游路由器用于转发该分组 而使用的传输容量最终被浪费掉了。</p><h4 id="3-6-2-拥塞控制方法"><a href="#3-6-2-拥塞控制方法" class="headerlink" title="3.6.2 拥塞控制方法"></a>3.6.2 拥塞控制方法</h4><ol><li><p>端到端拥塞控制</p></li><li><p>网络辅助的拥塞控制</p></li></ol><h4 id="3-6-3-网络辅助的拥塞控制例子:ATM-ABR拥塞控制"><a href="#3-6-3-网络辅助的拥塞控制例子:ATM-ABR拥塞控制" class="headerlink" title="3.6.3 网络辅助的拥塞控制例子:ATM ABR拥塞控制"></a>3.6.3 网络辅助的拥塞控制例子:ATM ABR拥塞控制</h4><h3 id="3-7-TCP拥塞控制"><a href="#3-7-TCP拥塞控制" class="headerlink" title="3.7 TCP拥塞控制"></a>3.7 TCP拥塞控制</h3><p>TCP发送方在感知拥塞时,为调节其发送速率采用了 <strong>TCP拥塞控制算法(TCP congestion control algorithm)</strong> ,包括了三个部分:</p><pre class="line-numbers language-none"><code class="language-none">1. 加性增(additive-increase) 、 乘性减(multiplicative-decrease) :发送速率谨慎(线性)增加,折半减少。2. 慢启动(slow start) :发送速率先指数增加,遇到第一个丢包时转为线性增加:超时事件发生后直接将发送速率降低为 1 个MSS然后指数增长、线性增长;收到 3 个冗余ACK后将发送速率减半然后线性增长。3. 对超时事件做出反应 :通过一个阈值来判断线性增长还是指数增长,通过遇超时事件或收到 2 、 3 个冗余ACK来决定阈值以及发送速率的改变。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">高速网络下需要设计新的TCP。TCP与UDP并发使用、TCP与并发TCP并发使用会产生不公平。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="3-8-小结"><a href="#3-8-小结" class="headerlink" title="3.8 小结"></a>3.8 小结</h3><p>TCP与UDP的区别:TCP面向连接,UDP是无连接的;TCP提供可靠的服务,也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达,UDP尽最大努力交付,即不保证可靠交付;</p><p>TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道;每一条TCP连接只能是点到点的,</p><p>UDP支持一对一,一对多,多对一和多对多的交互通信;TCP面向字节流(可能出现黏包问题),实际上是TCP把数据看成一连串无结构的字节流,UDP是面向报文的(不会出现黏包问题);UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议</p><p>等);TCP首部开销 20 字节;UDP的首部开销小,只有 8 个字节。</p><p>为使TCP实现面向连接可靠数据传输,TCP建立连接需要三次握手、关闭连接需要四次握手,需要差</p><p>错恢复、估计往返时延、流量控制和拥塞控制机制。</p><h4 id=""><a href="#" class="headerlink" title=""></a></h4><h2 id="4-网络层"><a href="#4-网络层" class="headerlink" title="4 网络层"></a>4 网络层</h2><h3 id="4-1-概述"><a href="#4-1-概述" class="headerlink" title="4.1 概述"></a>4.1 概述</h3><h4 id="4-1-1-转发和选路"><a href="#4-1-1-转发和选路" class="headerlink" title="4.1.1 转发和选路"></a>4.1.1 转发和选路</h4><p>网络层的作用是将分组从一台发送主机移动到一台接收主机,为此需要两种重要的网络层功能:</p><p>1)转发(当一个分组到达某路由器的一条输入链路时,该路由器必须将该分组移动到适当的输出链路),</p><p>2)选路(当分组从发送方流向接收方时,网络层必须决定这些分组所采用的路由或路径,计算这些路径的算法被称为 <strong>选路算法(routing algorithm)</strong> )。</p><p>转发是指将分组从一个输入链路接口转移到适当的输出链路接口的路由器本地动作。选路是指分组从源到目的地时,决定端到端路径的网络范围的进程。</p><p>每台路由器具有一张 <strong>转发表(forwarding table)</strong> ,路由器通过检查到达分组首部中的一个字段的值,然后使用该值在该路由器的转发表中索引查询来转发一个分组,查询转发表的结果是分组将被转发的路由器的链路接口。</p><p><strong>分组交换机</strong> 是指一台通用分组交换设备,它根据分组首部字段中的值,从输入链路接口到输出链路接口传送分组。某些分组交换机称为 <strong>链路层交换机(link-layer switch)</strong> ,它们基于链路层中的作转发决定。其他分组交换机称为 <strong>路由器(router)</strong> ,它们基于网络层字段中的值作转发决定。</p><pre class="line-numbers language-none"><code class="language-none">网络层还有一种重要的网络功能,即 连接建立(connection setup) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="4-1-2-网络服务模型"><a href="#4-1-2-网络服务模型" class="headerlink" title="4.1.2 网络服务模型"></a>4.1.2 网络服务模型</h4><p><strong>网络服务模型(network service model)</strong> 定义网络的一侧边缘到另一侧边缘之间(即发送端系统与接收端系统之间)分组的端到端运输特性。</p><p>发送主机中,当运输层向网络层传递一个分组时,能由网络层提供的特定服务包括:确保交付、具有时延上界的确保交付,此外这些服务能够为给定的源和目的地之间提供分组的流:有序分组交付、确保最小带宽、确保最大时延抖动、安全。</p><pre class="line-numbers language-none"><code class="language-none">因特网的网络层提供了单一的服务,称为 尽力而为服务(best-effort service) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="4-2-虚电路和数据报电路"><a href="#4-2-虚电路和数据报电路" class="headerlink" title="4.2 虚电路和数据报电路"></a>4.2 虚电路和数据报电路</h3><p>仅在网络层提供连接服务的计算机网络被称为 <strong>虚电路(Virtual-Circuit,VC)网络</strong> ;仅在网络层提供无连接服务的计算机网络被称为 <strong>数据报网络(datagram network)</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">因特网是数据报网络。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="4-2-1-虚电路网络"><a href="#4-2-1-虚电路网络" class="headerlink" title="4.2.1 虚电路网络"></a>4.2.1 虚电路网络</h4><p>一条虚电路(VC)的组成如下:</p><p>1)源和目的主机之间的路径;</p><p>2)VC号,沿着该路径的每段链路一个号码;</p><p>3)沿着该路径的每台路由器中的转发表表项。属于一条虚电路的分组将在它的首部携带一个VC号,因为一条虚电路在每条链路上可能具有不同的VC号,所以每台中间路由器必须用一个新的VC号替代每个传输分组的VC号,该新的VC号从转发表获得。</p><p>在虚电路网络中,该网络的路由器必须为进行中的连接维持 <strong>连接状态信息(connection state information)</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">在虚电路中有 3 个明显不同的阶段:1)虚电路建立,2)数据传送,3)虚电路拆除。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>端系统向网络发送指示虚电路启动与终止的报文,以及路由器之间传递的用于建立虚电路(即修改路由器表中的连接状态)的报文被称为 <strong>信令报文(signaling message)</strong> ,用来交换这些报文的协议常称为 <strong>信令协议(signaling protocol)</strong> 。</p><h4 id="4-2-2-数据报网络"><a href="#4-2-2-数据报网络" class="headerlink" title="4.2.2 数据报网络"></a>4.2.2 数据报网络</h4><p>在 <strong>数据报网络(datagram network)</strong> 中,每当一个端系统要发送分组时,它就为该分组加上目的地端系统的地址,然后将该分组推进网络中,完成这些无需建立任何虚电路。在数据报网络中,路由器不维护任何有关虚电路的状态信息。</p><p>路由器用分组的目的地址的 <strong>前缀(prefix)</strong> 与转发表中的表项进行匹配,如果存在匹配,该路由器向与该匹配相联系的链路转发分组。路由器使用 **最长前缀匹配规则(longest prefix matching rule) **,即在转发表中寻找最长的匹配项,并向与最长前缀匹配的链路接口转发该分组。</p><h4 id="4-2-3-虚电路和数据报网络的由来"><a href="#4-2-3-虚电路和数据报网络的由来" class="headerlink" title="4.2.3 虚电路和数据报网络的由来"></a>4.2.3 虚电路和数据报网络的由来</h4><h3 id="4-3-路由器工作原理"><a href="#4-3-路由器工作原理" class="headerlink" title="4.3 路由器工作原理"></a>4.3 路由器工作原理</h3><p>网络层的 <strong>转发功能(forwarding function)</strong> ,即实际将分组从一台路由器的入链路传送到适当的出链路。</p><pre class="line-numbers language-none"><code class="language-none">一台路由器有 4 个组成部分:输入端口、交换结构、输出端口和选路处理器。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="4-3-1-输入端口"><a href="#4-3-1-输入端口" class="headerlink" title="4.3.1 输入端口"></a>4.3.1 输入端口</h4><pre class="line-numbers language-none"><code class="language-none">达到 线路速度(line speed) ,即执行一次查找的时间少于从输入端口接收一个分组所需的时间。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>内容可寻址内存(Content Addressable Momery,CAM)</strong> 允许一个 32 比特IP地址提交给CAM,由它再以基本上常数时间返回该地址对应的转发表表项内容。也可以将转发表表项保存在高速缓存中。</p><p>一个分组可能会在进入交换结构时暂时 <strong>阻塞(blocked)</strong> ,这是由于来自其他输入端口的分组当前正在使用该交换结构。</p><h4 id="4-3-2-交换结构"><a href="#4-3-2-交换结构" class="headerlink" title="4.3.2 交换结构"></a>4.3.2 交换结构</h4><p>三种交换技术是:经内存交换、经一根总线交换和经一个互联网络交换。</p><h4 id="4-3-3-输出端口"><a href="#4-3-3-输出端口" class="headerlink" title="4.3.3 输出端口"></a>4.3.3 输出端口</h4><h4 id="4-3-4-何时出现排队"><a href="#4-3-4-何时出现排队" class="headerlink" title="4.3.4 何时出现排队"></a>4.3.4 何时出现排队</h4><p>输入端口和输出端口处都能形成分组队列,随着这些队列的增长,路由器的缓存空间将会最终耗尽,且出现 <strong>丢包(packet loss)</strong> 。</p><p><strong>交换结构速率(switching fabric speed)</strong> 即交换结构能够从输入端口到输出端口移动分组的速率。</p><p>输出端口排队的后果就是,输出端口上的一个 <strong>分组调度程序(packet scheduler)</strong> 必须在这些排队的分组中选出一个来传送,如先来先服务(FCFS)调度和加权公平排队(WFQ,在具有排队等待传输的<br>分组的不同端到端连接之间公平地共享输出链路)。分组调度程序在提供 <strong>服务质量保证(quality-of-service guarantee)</strong> 方面起着关键作用。</p><p>如果没有足够的内存来缓存一个入分组,要么丢弃到达的分组(一种称为 <strong>弃尾(drop-tail)</strong> 的策略),要么删除一个或多个排队的分组以便为新来的分组腾出空间。<strong>主动队列管理(Active QueueManagement,AQM)算法</strong> 通过在路由器队列中丢弃或标记数据包将拥塞情况隐式或显式地通知源<br>端,源端相应地减小数据发送速率来响应数据包的丢弃或标记,避免更严重的拥塞发生。<strong>随机早期检测(Random Early Detection,RED)</strong>算法是一种得到最广泛地研究和实现的AQM算法,在RED算法中,为输出队列长度维护着一个加权平均值,如果平均队列长度小于最小阈值则当一个分组到达时该分</p><p>组被接纳进入队列,相反,如果队列满或平均队列长度大于最大阈值则当一个分组到达时该分组被标记或丢弃,最后,如果一个分组到达发现平均队列长度在最大阈值和最小阈值之间则该分组以某种概率值</p><p>(一般是与平均队列长度、最小阈值和最大阈值有关的函数值)被标记或丢弃。</p><p>输入排队交换机中的 <strong>线路前部(Head-Of-the-Line,HOL)阻塞</strong> ,即在一个输入队列中排队的分组必须等待通过交换结构发送(即使输出端口是空闲的),因为它由位于线路前部的另一个分组阻塞。</p><h3 id="4-4-网际协议:因特网中的转发和编址"><a href="#4-4-网际协议:因特网中的转发和编址" class="headerlink" title="4.4 网际协议:因特网中的转发和编址"></a>4.4 网际协议:因特网中的转发和编址</h3><h4 id="4-4-1-数据报格式"><a href="#4-4-1-数据报格式" class="headerlink" title="4.4.1 数据报格式"></a>4.4.1 数据报格式</h4><pre class="line-numbers language-none"><code class="language-none">IPv4数据报中的关键字段有:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ol><li><strong>版本号</strong> :<br>4 比特规定了数据报的IP协议版本。</li><li><strong>首部长度</strong> :<br>需要 4 比特来确定IP数据报中的数据部分实际从哪里开始,一般的IP数据报都有 20 字节的首部。</li><li><strong>服务类型(TOS)</strong> :<br>使不同类型的IP数据报能相互区分开来。</li><li><strong>数据报长度</strong> :<br>IP数据报的总长度(首部加上数据)。</li><li><strong>标识、标志、偏移量</strong> :<br>与IP分片有关。</li><li><strong>寿命(Time-To-Live,TTL)</strong> :<br>确保数据报不会永远在网络中循环。</li><li><strong>协议</strong> :<br>指明了IP数据报的数据部分应交给哪个运输层协议。</li><li><strong>首部检验和</strong> :<br>帮助路由器检测收到的IP数据报中的比特错误。</li><li><strong>源和目的IP地址</strong></li><li><strong>选项</strong> :<br>允许IP首部被扩展。</li><li><strong>数据(有效载荷)</strong><br>一个链路层帧能承载的最大数据量叫做 <strong>最大传输单元(Maximum Transmission Unit,<br>MTU)</strong> 。当IP数据报比MTU小时,将IP数据报中的数据分片成两个或更多个较小的数据报,用单独的链路层帧封装这些较小的IP数据报,然后向输出链路上发送这些帧,这些较小的数据报叫<strong>片(fragment)</strong> 。</li></ol><h4 id="4-4-2-IPv4编址"><a href="#4-4-2-IPv4编址" class="headerlink" title="4.4.2 IPv4编址"></a>4.4.2 IPv4编址</h4><p>主机与物理链路之间的边界叫做 <strong>接口(interface)</strong> 。互联几台主机的接口与路由器的一个接口的网络形成一个 <strong>子网(subnet)</strong> 。指明一个IP地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码叫做 <strong>子网掩码(subnet mask)</strong> 。</p><p>因特网的地址分配策略被称为 <strong>无类别域间选路(Classless Interdomain Routing,CIDR)</strong> ,对于子网寻址, 32 比特的IP地址被划分为两部分,并且也具有点分十进制形式a.b.c.d/x,其中x指示了在地址的第一部分中的比特数。形式为a.b.c.d/x的地址的x最高比特构成了IP地址的网络部分,并且经常被称为该地址的 <strong>前缀(prefix)</strong> 或网络前缀。</p><p>在采用CIDR之前,限制具有 8 、 16 和 24 比特子网地址的网路分别被称为A、B和C类网络,这种编址方案称为 <strong>分类编址(classful addressing)</strong> 。</p><p>为了获取一块IP地址用于一个组织的子网,网络管理员也许首先会与其ISP联系,ISP会从已分给它的更大地址中提供一些地址。一个组织一旦获得了一块地址,它就可为该组织内的主机与路由器接口分配独立的IP地址,这项任务目前常使用 <strong>动态主机配置协议(Dynamic Host Configuration Protocol,<br>DHCP)</strong> 来完成。利用DHCP,主机可以自动获取IP地址,网络管理员可以配置DHCP,以便某给定主机每次与该网络连接时能得到一个相同的IP地址,或者被分配一个<em>临时的IP地址*<em>(temporary IP address)</em></em> ,主机每次与该网络连接时该地址都可能是不同的。默认网关即第一跳路由器地址。由于<br>DHCP具有能将主机连接进一个网络的自动化网络相关方面的能力,故它又常被称为<em>即插*<em>即用协议(plug-and-play protocol)</em></em> 。</p><pre class="line-numbers language-none"><code class="language-none">DHCP协议的 4 个步骤是:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. DHCP服务器发现 :新到达的主机的首要任务是发现一个要与其交互的DHCP服务器,可通过 DHCP发现报文(DHCP discover message) 来完成,客户机在UDP分组中向端口 67 发送该发现报文(将该IP数据报传递给链路层,链路层然后将该帧广播到所有与该子网连接的子网)。2. DHCP服务器提供 :DHCP服务器收到一个DHCP发现报文时,用一个 DHCP提供报文(DHCP offer message) 对客户机做出响应,每个服务器提供报文中含有收到的发现报文的事务ID、向客户机推荐的IP地址、网络掩码以及 IP地址租用期(IP address lease time,即IP地址有效的时间量) 。3. DHCP请求 :新到达的客户机从一个或多个服务器中选择一个,并用一个 DHCP请求报文(DHCP request message) 对选中的服务器进行响应,回显配置参数。4. DHCP ACK :服务器用 DHCP ACK报文(DHCP ACK message) 对DHCP请求报文进行响应,证实所要求的参数。一旦客户机收到DHCP ACK后,交互便完成了,该客户机就能够在租用期内使用DHCP分配给它的IP地址。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当在专用网内部的一些主机本来已经分配到了本地IP地址(即仅在本专用网内使用的专用地址),但现在又想和因特网上的主机通信(并不需要加密)时,可使用 <strong>网络地址转换(Network Address<br>Translation,NAT)</strong> 。NAT路由器中有一张 <strong>NAT转换表(NAT translation table)</strong> ,并且在表项中包含了端口号和IP地址,负责WAN端IP地址与LAN端IP地址间的转换。</p><p>NAT不允许外网主机访问内网主机,所以可以在两个对等方之间加一个中间对等方(不在NAT之后且与其中在NAT之后的对等方建立了TCP连接),一旦这对对等方之间创建了一个直接的P2P TCP连接,这两个对等方就能够交换报文或文件,这种雇佣关系称为 <strong>连接反转(connection reversal)</strong> ,实际上<br>被许多P2P应用程序用于NAT穿越。</p><p>NAT穿越越来越多地由通用即插即用(UPnP)提供,UPnP是一种允许主机发现并配置邻近NAT的协议。</p><h4 id="4-4-3-ICMP:互联网控制报文协议"><a href="#4-4-3-ICMP:互联网控制报文协议" class="headerlink" title="4.4.3 ICMP:互联网控制报文协议"></a>4.4.3 ICMP:互联网控制报文协议</h4><p>用于主机和路由器彼此交互网络层信息。</p><h4 id="4-4-4-IPv"><a href="#4-4-4-IPv" class="headerlink" title="4.4.4 IPv"></a>4.4.4 IPv</h4><p>IPv6将IP地址长度从 32 比特增加到了 128 比特,还引入了一种称为 <strong>任播地址(anycast address)</strong>的新型地址(可以使一个数据报能交付给一组主机中的任意一个)。IPv6有简单高效的 40 字节首部。IPv6有一个难以捉摸的 <strong>流(flow)</strong> 定义,流标签字段即给特殊流(发送方要求特殊处理的流)的分组加上标签。IPv6首部还有一个 8 比特的流量类型字段,用于给出一个流中某些数据报的优先级。</p><pre class="line-numbers language-none"><code class="language-none">IPv6中定义的字段:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ol><li><p>版本号 :<br>这 4 比特用于标识IP版本号,将该字段值设为 6 。</p></li><li><p>流量类型 :</p><p>这 8 比特能使不同类型的IP数据报能相互区分开来。</p></li><li><p>流标签 :</p><p>该 20 比特用于标识一个数据报的流。</p><ol start="4"><li>有效载荷长度 :</li></ol></li></ol><p>该 16 比特作为一个无符号整数,给出了IPv6数据报中跟在定长的 40 字节数据报首部后面的字节数量。</p><ol start="5"><li>下一个首部 :<br>标识该数据报中的内容需要交付给哪个协议(如TCP或UDP)。</li><li>跳限制 :<br>转发数据报的路由器对该字段内容减 1 ,如果跳限制计数到达 0 ,则该数据报将被丢弃。</li><li>源和目的地址</li><li>数据 :<br>这是IPv6数据报的有效载荷部分。</li></ol><pre class="line-numbers language-none"><code class="language-none">存在于IPv4数据报格式中却不存在与IPV6数据报格式中的字段:<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">1. 分片/重新组装 :IPv6不允许在中间路由器上进行分片与重新组装(只能在源与目的地上执行)。2. 首部检验和 :运输层和链路层协议执行了检验操作,所以不需要在网络层设置这种功能。3. 选项 :不再是标准IP首部的一部分,但是可能出现在IPv6首部中由下一个首部指出的位置上。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="4-4-5-IP安全性概述"><a href="#4-4-5-IP安全性概述" class="headerlink" title="4.4.5 IP安全性概述"></a>4.4.5 IP安全性概述</h4><pre class="line-numbers language-none"><code class="language-none">IPsec是流行的安全网络层协议之一,它是面向连接的。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">由IPsec会话提供的服务包括:密码技术协约、IP数据报有效载荷的加密、数据完整性、初始鉴别。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="4-5-选路算法"><a href="#4-5-选路算法" class="headerlink" title="4.5 选路算法"></a>4.5 选路算法</h3><p>一台主机通常直接与一台路由器相连接,该路由器即为该主机的 <strong>默认路由器(default router)</strong> ,又称为该主机的 <strong>第一跳路由器(first-hop router)</strong> ,每当某主机发送一个分组时,该分组都被传送给它的默认路由器,我们将源主机的默认路由器称为 <strong>源路由器(source router)</strong> ,把目的主机的默认路<br>由器称为 <strong>目的路由器(destination router)</strong> 。</p><p><strong>最低费用路径(least-cost path)</strong> 即 <strong>最短路径(shortest path)</strong> 。<br><strong>全局选路算法(global routing algorithm)</strong> 用完整的、全局性的网络知识来计算从源到目的之间的最低费用路径。</p><p><strong>分布式选路算法(decentralized routing algorithm)</strong> 以迭代的、分布式的方式计算出最低费用路径。</p><h4 id="4-5-1-链路状态(Link-State,LS)选路算法"><a href="#4-5-1-链路状态(Link-State,LS)选路算法" class="headerlink" title="4.5.1 链路状态(Link-State,LS)选路算法"></a>4.5.1 链路状态(Link-State,LS)选路算法</h4><p>是一种使用全局信息的算法。在实践中,经常由 <strong>链路广播状态(link state broadcast)</strong> 算法来完成。链路费用相当于承载的流量。要确保并非所有路由器都同时运行LS算法。LS算法与Dijkstra算法相关。</p><h4 id="4-5-2-距离向量(Distance-Vector,DV)选路算法"><a href="#4-5-2-距离向量(Distance-Vector,DV)选路算法" class="headerlink" title="4.5.2 距离向量(Distance-Vector,DV)选路算法"></a>4.5.2 距离向量(Distance-Vector,DV)选路算法</h4><p>是一种迭代的、异步的和分布式(每个节点都要从一个或多个直接相连的邻居接收某些信息,执行计算,然后将计算结果发回给邻居)的算法。DV算法与Bellman–Ford算法相关。</p><h4 id="4-5-3-层次选路"><a href="#4-5-3-层次选路" class="headerlink" title="4.5.3 层次选路"></a>4.5.3 层次选路</h4><p>可以将路由器组织进 <strong>自治系统(Autonomous System,AS)</strong> 。在一个AS内运行的选路算法叫做<strong>自治系统内部选路协议(intra-autonomous system routing protocol)</strong> 。在一个AS内的一台或多台路由器负责向本AS之外的目的地转发分组,这些路由器被称为 <strong>网关路由器(gateway router)</strong> 。从相邻AS获取可达性信息以及向该AS中的所有路由器传播可达性信息这两项任务由 <strong>自治系统间选路协议<br>(inter-autonomous system routing protocol)</strong> 负责,解决固定维序路由当出现竞争时只能等待的一种实践方法是 <strong>热土豆选路(hot potato routing)</strong> ,即当一个分组到来时,节点必须尽快脱内手,将其放入输出列最短的方向上排队,而不管该方向通向何方。</p><h3 id="4-6-因特网中的选路"><a href="#4-6-因特网中的选路" class="headerlink" title="4.6 因特网中的选路"></a>4.6 因特网中的选路</h3><p>AS内部选路协议又称为 <strong>内部网关协议(interior gateway protocol)</strong> 。两个选路协议曾被广泛用于因特网上自治系统内的选路: <strong>选路信息协议(Routing Information Protocol,RIP)</strong> 与<strong>开放最短路<br>径优先(Open Shortest Path First,OSPF)</strong> 。</p><h4 id="4-6-1-因特网中自治系统内部选路:RIP"><a href="#4-6-1-因特网中自治系统内部选路:RIP" class="headerlink" title="4.6.1 因特网中自治系统内部选路:RIP"></a>4.6.1 因特网中自治系统内部选路:RIP</h4><p>RIP通常被设置在较低层ISP和企业网中,运行方式很像DV协议。</p><p>RIP使用术语 跳 ,跳是沿着从源路由器到目的子网(包括目的子网)的最短路径所经过的子网数量。</p><p>在RIP中,选路更新信息在邻居之间通过使用一种 <strong>RIP响应报文(RIP response message)</strong> 交换,大约 30 秒相互交换一次,由一台路由器或主机发出的响应报文包含了一个由多达 25 个AS内的目的子网列表,还有发送方到其中每个子网的距离,响应报文又被称作 <strong>RIP通告(RIP advertisement)</strong> 。</p><p>每台路由器维护一张称为 <strong>选路表(routing table)</strong> 的RIP表,包括该路由器的距离向量和该路由器的转发表。</p><h4 id="4-6-2-因特网中AS内部选路:OSPF"><a href="#4-6-2-因特网中AS内部选路:OSPF" class="headerlink" title="4.6.2 因特网中AS内部选路:OSPF"></a>4.6.2 因特网中AS内部选路:OSPF</h4><p>OSPF通常被设置在较顶层的ISP中,核心就是使用一个洪泛LS协议。</p><p>在一个区域内,一台或多台 <strong>区域边界路由器(area border router)</strong> 负责为发送到该区域以外的分组选路,准确地说,AS内的一个OSPF区域配置成 <strong>主干(backbone)</strong> 区域,主干区域的主要作用是为AS内其他区域之间的流量选路。</p><h4 id="4-6-3-自治系统间的选路:BGP(Broder-Gateway-Protocol,即边界网关协议)"><a href="#4-6-3-自治系统间的选路:BGP(Broder-Gateway-Protocol,即边界网关协议)" class="headerlink" title="4.6.3 自治系统间的选路:BGP(Broder Gateway Protocol,即边界网关协议)"></a>4.6.3 自治系统间的选路:BGP(Broder Gateway Protocol,即边界网关协议)</h4><p>位于某条TCP连接端点的两台路由器被称为 <strong>BGP对等方(BGP peer)</strong> ,沿着该连接发送所有BGP报文的TCP连接被称为 <strong>BGP会话(BGP session)</strong> 。BGP使得每个AS知道经过其相邻AS,哪些目的地是可达的。跨越两个AS的BGP会话被称为 <strong>外部BGP(eBGP)会话(external BGP session)</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">在BGP中,目的地不是主机而是CDIR化的前缀,每个前缀表示一个子网或一个子网的集合。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>当一台路由器通过BGP会话通告一个前缀时,随着前缀包括一些 <strong>BGP属性(BGP attribute)</strong> ,带有属性的前缀被称为一条 <strong>路由(route)</strong> ,因此BGP对等方彼此通告路由。</p><p>当一台网关路由器接收到一个路由器通告时,它使用其 <strong>输入策略(import policy)</strong> 来决定是否接收或过滤该路由,是否设置某种属性。</p><p>所有进入 <strong>桩网络(stub network)</strong> 的流量必定是以该网络为目的地的,所有离开桩网络的流量必定是源于该网络的。经由多个不同的提供商连到其余网络的桩网络叫做<strong>多宿桩网络(multi-homed stub network)</strong> 。</p><h3 id="4-7-广播和多播选路"><a href="#4-7-广播和多播选路" class="headerlink" title="4.7 广播和多播选路"></a>4.7 广播和多播选路</h3><p>在 <strong>广播选路(broadcast routing)</strong> 中,网络层提供了从一个源节点到网络中的所有其他节点交付分组的服务; <strong>多播选路(multicast routing)</strong> 使单个源节点能够向其他网络节点的一个子集发送分组的拷贝。</p><h4 id="4-7-1-广播选路酸法"><a href="#4-7-1-广播选路酸法" class="headerlink" title="4.7.1 广播选路酸法"></a>4.7.1 广播选路酸法</h4><p>完成广播通信的最直接方式是 <strong>N次单播(N-way-unicast)</strong> ,但是低效、开销大。<br>可以使用使用无控制 <strong>洪泛(flooding)</strong> 方法,该方法要求源节点向它的所有邻居发送一个分组的拷贝,当某节点接收了一个广播分组时,它复制该分组并向它的所有邻居转发。</p><p>还可以用受控洪泛方法,一种是通过 <strong>序号控制洪泛(sequence-number-contolled flooding)</strong> 实现,源节点将其地址(或其他的唯一标识符)以及 <strong>广播序号(broadcast sequence number)</strong> 放人广播分组,再向它的所有邻居发送该分组,每个节点维护它已经收到的、复制的和转发的源地址和每个广播分组的序号列表,当一个节点接收到一个广播分组时,它首先检查该分组是否在该列表中,如果在,丢弃该分组,如果不在,复制该分组并向该节点的所有邻居转发;另一种方法称为反向路径转发,有时也称为 <strong>反向路径广播(Reverse Path Fordwarding,RPF,有时也称为反向路径广播即RPB)</strong> ,RPB的基本思想是当一台路由器接收到具有给定源地址的广播分组时,仅当该分组到达的链路正好是位于它自己到其源的最短单播路径上,它才向其所有出链路(除了它接收分组的那个)传输分组,否则,该路由器丢弃入分组。</p><p>另一种提供广播的方法是首先对网络节点构造出一棵生成树,当一个源节点要发送一个广播分组时,它向所有属于该生成树的特定链路发送分组,接收广播分组的节点则向生成树中的所有邻居转发该分组(它接收该分组的邻居除外),生成树不仅消除了冗余广播分组,而且能够被任何节点用于开始广<br>播分组。</p><pre class="line-numbers language-none"><code class="language-none">广播协议在实践中通常被用于应用层和网络层。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="4-7-2-多播"><a href="#4-7-2-多播" class="headerlink" title="4.7.2 多播"></a>4.7.2 多播</h4><p>在因特网体系结构中,多播数据报使用 <strong>间接地址(address indirection)</strong> 来编址。在因特网中,表示一组接收方的单一标识就是一个D类多播地址。与一个D类地址相关联多个接收方称为一个 <strong>多播组<br>(multicast group)</strong> 。</p><p>IGMP为一台主机提供了手段,可让它通知与其相连的路由器,在本主机上运行的一个应用程序如何加入一个特定的多播组。在实践中有两种方法用于确定多播选路树:1)使用一棵组共享树进行多播选路,2)使用一棵基于源的树进行多播选路。</p><h3 id="4-8-小结"><a href="#4-8-小结" class="headerlink" title="4.8 小结"></a>4.8 小结</h3><p>为使路由器的工作更容易,可以使用数据报网络层而不是虚电路网络层,使用一种流水线和固定长度的首部,取消分片,提供唯一的尽力而为服务。</p><p>选路算法把计算机网络抽象为一个具有节点和链路的图的方法,一种是全局方法(每个节点得到一张完整的网络图并且独立地应用一种最短路径选路算法),一种是分布式方法(各节点只有整个网络的部分知识且各节点一起协调工作以便沿最短路径交付分组),可以通过将大型网络划分成称为自治系统</p><p>(AS)的独立管理域来解决规模问题。</p><h2 id="5-链路层和局域网"><a href="#5-链路层和局域网" class="headerlink" title="5 链路层和局域网"></a>5 链路层和局域网</h2><h3 id="5-1-链路层:概述和服务"><a href="#5-1-链路层:概述和服务" class="headerlink" title="5.1 链路层:概述和服务"></a>5.1 链路层:概述和服务</h3><pre class="line-numbers language-none"><code class="language-none">沿着通信路径连接相邻节点的通信信道称为 链路(link) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-1-1-链路层提供的服务"><a href="#5-1-1-链路层提供的服务" class="headerlink" title="5.1.1 链路层提供的服务"></a>5.1.1 链路层提供的服务</h4><p><strong>链路层协议(link-layer protocol)</strong> 定义了在链路两端的节点之间交互的分组格式,以及当发送和接收分组时这些节点采取的动作。链路层协议交换的数据单元称为 <strong>帧(frame)</strong> 。链路层协议能够提供的可能服务包括: <strong>成帧(framing)</strong> 、 <strong>链路接入(link access)</strong> 、 <strong>可靠交付(reliable delivery)</strong> 、<strong>流量控制(flow control)</strong> 、 <strong>差错检测(error detection)</strong> 、 <strong>差错纠正(error correction)</strong> 、 <strong>半双工<br>和全双工(half-duplex and full-duplex)</strong> 。</p><h4 id="5-1-2-链路层在何处实现"><a href="#5-1-2-链路层在何处实现" class="headerlink" title="5.1.2 链路层在何处实现"></a>5.1.2 链路层在何处实现</h4><p>链路层的主体部分是在 <strong>网路适配器(network adapter)</strong> 中实现的,网络适配器也称为了 <strong>网络接口卡(Network Interface Card,NIC)</strong> ,网络适配器的内核是链路层控制器,该控制器通常是实现了许多链路层服务的单个特定目的的芯片。</p><p>在发送方,控制器取得了由协议栈较高层生成并存储在主机内存中的数据报,在链路层帧中封装该数据报,然后遵循链路接入协议将该帧传进通信链路中,在接收端,控制器接收整个帧,提取出网络层数据报,如果链路层执行差错检测,则需要发送适配器在该帧的首部设置差错检测比特,以及接收适配<br>器执行差错检测,如果该链路层执行流量控制,则发送控制器和接收控制器交换流量控制信息,使得发送方以接收方能够处理的速率发送帧。</p><h3 id="5-2-差错检测和纠错技术"><a href="#5-2-差错检测和纠错技术" class="headerlink" title="5.2 差错检测和纠错技术"></a>5.2 差错检测和纠错技术</h3><p><strong>比特级差错检测和纠错(bit-level error detection and correction)</strong> 通常是链路层提供的两种服务:对一个节点发送到另一个物理上连接的邻近节点的链路层帧,检测和纠正其中的比特差错。</p><p>在发送节点,为了避免比特差错,使用<strong>差错检测和纠察比特(Error Detection and Correction bit,EDC bit)</strong> 来增强数据。即使采用差错检测比特,也还是可能有<strong>未检出比特差错(undetected bit error)</strong> 的情况。</p><pre class="line-numbers language-none"><code class="language-none">传输数据中检测差错的 3 种技术:奇偶校验、检验和方法与循环冗余检测。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-2-1-奇偶校验"><a href="#5-2-1-奇偶校验" class="headerlink" title="5.2.1 奇偶校验"></a>5.2.1 奇偶校验</h4><p>差错检测最简单的方式是用单个的 <strong>奇偶校验位(parity bit)</strong> 。发送方只需要包含一个附加的比特,在偶校验方案中 1 的总数是偶数,在奇校验方案中 1 的总数是奇数。使用一种<strong>二维奇偶校验(two-dimensional parity)</strong> 方案,包含比特值改变的列和行的校验值都将会出现差错,而且可以利用存在奇<br>偶校验差错的列和行的下标来识别实际发生差错的比特并纠正它。接收方检测和纠正差错的能力被称为<strong>前向纠错(Forward Error Correction,FEC)</strong> 。</p><h4 id="5-2-2-检验和方法"><a href="#5-2-2-检验和方法" class="headerlink" title="5.2.2 检验和方法"></a>5.2.2 检验和方法</h4><p><strong>互联网检验和(Internet checksum)</strong> ,即(检验位设置为 0 )数据的两个字节作为 16 比特的整数对待并取反求和,这个和形成了携带在报文段首部的互联网检验和,可以让接收方通过对接收的数据的和取反码,并且检测其结果是否全为 0 比特来检测检验和。</p><h4 id="5-2-3-循环冗余检测"><a href="#5-2-3-循环冗余检测" class="headerlink" title="5.2.3 循环冗余检测"></a>5.2.3 循环冗余检测</h4><p><strong>循环冗余检验(Cyclic Redundancy Check,CRC)</strong> 编码也称为<strong>多项式编码(polynomial code)</strong> ,因为该编码能够将要发送的比特串看作是系数为 0 和 1 的一个多项式。</p><h3 id="5-3-多路访问协议"><a href="#5-3-多路访问协议" class="headerlink" title="5.3 多路访问协议"></a>5.3 多路访问协议</h3><p><strong>点对点链路(point-to-point link)</strong> 是由链路一端的单个发送方和链路另一端的单个接收方组成的。</p><p><strong>点对点协议(Point-to-Point Protocol,PPP)</strong> 和<strong>高级数据链路控制(High-level Data Link Control,HDLC)</strong> 是两种为点对点链路设计的链路层协议。</p><p><strong>广播链路(broadcast link)</strong> 能够让多个发送和接收节点都连接到相同的、单一的、共享的广播信道上。 <strong>多路访问问题(multiple access problem)</strong> :如何协调多个发送和接收节点对一个共享广播信道的访问。</p><p><strong>多路访问控制协议(Multiple Access Control protocol,MAC protocol)</strong> 即节点通过这些协议来规范它们在共享的广播信道上的传输行为。</p><p>两个以上的节点可能会同时传输帧,这时所有节点同时接到多个帧,也就是说,传输的帧在所有的接收方处 <strong>碰撞(collide)</strong> 了。</p><p>多路访问协议划分为 3 种类型: <strong>信道划分协议(channel partitioning protocol)</strong> 、<strong>随机接入协议(random access protocol)</strong> 和 <strong>轮流协议(taking-turns protocol)</strong> 。</p><h4 id="5-3-1-信道划分协议"><a href="#5-3-1-信道划分协议" class="headerlink" title="5.3.1 信道划分协议"></a>5.3.1 信道划分协议</h4><p>时分多路复用(TDM)和频分多路复用(FDM)是两种能在所有共享信道节点之间用于划分广播信</p><p>道带宽的技术。</p><pre class="line-numbers language-none"><code class="language-none">另一种信道划分协议是 码分多址(Code Division Multiple Access,CDMA) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-3-2-随机接入协议"><a href="#5-3-2-随机接入协议" class="headerlink" title="5.3.2 随机接入协议"></a>5.3.2 随机接入协议</h4><p>在随机接入协议中,一个传输节点总是以信道的全部速率进行发送,当有碰撞时,涉及碰撞的每个节点反复地重发它的帧,直到该帧无碰撞地通过为止,但是当一个节点经受一次碰撞时,它不必立刻重发该帧,相反,它在重传该帧之前等待一个随机时延,涉及碰撞的每个节点独立地选择随机时延,因为该随机时延是独立选择的,下述现象有可能发生:这些节点之一所选择的时延远远小于其他碰撞节点的时延,并因此能够无碰撞地将它的帧在信道中发出。</p><p><strong>常用的随机接入协议:</strong></p><ol><li>时隙ALOHA :</li></ol><p>假设 1)所有帧大小相同,2)时间被划分为等长的时隙,3)节点只能在时隙开始时刻发送帧,4)节点间时钟同步,5)如果 2 及以上个节点在同一时隙发送帧则立即检测到冲突;</p><p>运行:1)当节点有新的帧时在下一个时隙发送,2)若无冲突则该节点在下一时隙发送新的帧,3)若有冲突则该节点在下一时隙以一个概率重传该帧直到成功。优点:1)单个节点活动时可连续以信道全部</p><p>速率传输数据,2)高度分散化:只需同步时隙,3)简单;缺点:1)冲突造成时隙浪费,2)空闲时隙,3)节点也许能以远小于分组传输时间检测到冲突。</p><ol start="2"><li>ALOHA :</li></ol><p>较之时隙ALOHA,当有新的帧生成时立即发送。</p><pre class="line-numbers language-none"><code class="language-none">3. 载波侦听多路访问(Carrier Sense Multiple Access,CSMA) :CSMA协议:发送帧之前,监听信道(载波):若信道空闲,则发送完整帧;若信道忙,则推迟发送;若信号传播延迟,冲突仍然可能发生,继续发送冲突帧会浪费信道资源。 具有碰撞检测的CSMA(CSMA with Collision Detection,CSMA/CD) (一般在有线网络中使用,用检测信号强度的方式):短时间内可检测到冲突,冲突后传输终止——减少信道浪费。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="5-3-3-轮流协议"><a href="#5-3-3-轮流协议" class="headerlink" title="5.3.3 轮流协议"></a>5.3.3 轮流协议</h4><p><strong>轮询协议(polling protocol)</strong> :主节点以循环的方式 <strong>轮询(poll)</strong> 每个节点(即邀请其发数据),存在的问题是:轮询有开销、等待有延迟、单点会故障。</p><p><strong>令牌传递协议(token-passing protocol)</strong> :控制 <strong>令牌(token)</strong> 依次从一个节点传递到下一个节点(拿到令牌才有资格传数据,没数据可传了才将令牌转发至下一个节点),存在的问题是:令牌有开销、等待有延迟、单点会故障。</p><h4 id="5-3-4-局域网"><a href="#5-3-4-局域网" class="headerlink" title="5.3.4 局域网"></a>5.3.4 局域网</h4><p>有两类LAN技术很流行:基于随机接入的以太网LAN和由令牌传递技术组成的LAN技术包括 令牌环<strong>(token ring)</strong> 以及 <strong>光纤式分布数据接口(Fiber Distributed Data Interface,FDDI)</strong> 。</p><h3 id="5-4-链路层编址"><a href="#5-4-链路层编址" class="headerlink" title="5.4 链路层编址"></a>5.4 链路层编址</h3><p>节点(即主机和路由器)的适配器具有链路层地址。</p><h4 id="5-4-1-MAC地址"><a href="#5-4-1-MAC地址" class="headerlink" title="5.4.1 MAC地址"></a>5.4.1 MAC地址</h4><p>LAN地址有各种不同的称呼: <strong>LAN地址(LAN address)</strong> 、 <strong>物理地址(physical address)</strong> 或<strong>MAC地址(MAC address)</strong> 。</p><p>对大多数LAN而言,MAC地址长度为 6 字节,共有 2 的 48 次方个可能的LAN地址,地址的每个字节被表示为一对十六进制数。</p><pre class="line-numbers language-none"><code class="language-none">适配器的MAC地址具有扁平结构,而且不论适配器到哪里用都不会变化。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>当某适配器要向某些目的适配器发送一个帧时,发送适配器将目的适配器的MAC地址插入到该帧中,并将该帧发送到LAN上,如果该LAN是一个广播LAN,这个帧会被该LAN上所有其他适配器接收和处理,特别是,接收到该帧的每个适配器将检查,看在该帧中的目的MAC地址是否与它自己的MAC地址<br>匹配,如果匹配,该适配器提取出封装的数据报,并将该数据报沿协议栈向上传递给它的父节点,如果不匹配,该适配器丢弃该帧,则不会沿协议栈向上传递该网络层数据报,所以,当目的节点收到该帧时,仅有它将会中断其父节点。然而,有时某些发送适配器的确要让LAN上所有的其他适配器来接收并<br>处理它打算发送的帧,在这种情况下,发送适配器在该帧的目的地址字段中插入一个特殊的MAC<strong>广播地址(broadcast address)</strong> ,对于使用 6 字节地址的LAN来说,广播地址是 48 个连续的 1 组成的字符串<br>(即以十六进制表示法表示的FF-FF-FF-FF-FF-FF)。</p><h4 id="5-4-2-地址解析协议"><a href="#5-4-2-地址解析协议" class="headerlink" title="5.4.2 地址解析协议"></a>5.4.2 地址解析协议</h4><p>网络层地址和链路层地址之间进行转换,这是<strong>地址解析协议(Address Resolution Protocol,ARP)</strong> 。</p><p>每个节点(主机或路由器)的ARP模块都在它的RAM中有一个 <strong>ARP表(ARP table)</strong> ,这张表包含IP地址到MAC地址的映射关系,该表还包含一个 <strong>生存期(TTL)</strong> 值,它指示了从表中删除每个映射的时间。</p><p>将网络层数据报封装成链路层帧时,若目的IP地址在子网外,会将下一跳节点接口本侧MAC地址(用ARP表查询)设为目的MAC地址,等到了下一跳节点,拆分链路层帧为网络层数据报,依目的IP地址查询路由转发表找到下一跳的IP,通过ARP表查询另侧接口的MAC地址以及下一跳节点本侧接口的MAC地址,并将其设为源和目的MAC地址,从而将网络层数据报重新封装成链路层帧,以此类推。</p><h3 id="5-5-以太网"><a href="#5-5-以太网" class="headerlink" title="5.5 以太网"></a>5.5 以太网</h3><pre class="line-numbers language-none"><code class="language-none">以太网使用星型拓扑,位于中心的 集线器(hub) 被 交换机(switch) 所替代。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-5-1-以太网帧结构"><a href="#5-5-1-以太网帧结构" class="headerlink" title="5.5.1 以太网帧结构"></a>5.5.1 以太网帧结构</h4><p>以太网的 6 个字段:</p><pre class="line-numbers language-none"><code class="language-none">1. 数据字段(data field) :承载了IP数据报,最大传输单元是 1500 字节,最小长度为 46 字节。2. 目的地址(destination address) :包含目的适配器的MAC地址,长度为 6 字节。3. 源地址(source address) :包含传输该帧到LAN上的适配器的MAC地址,长度为 6 字节。4. 类型字段(type field) :允许以太网复用多种网络层协议,长度为 2 字节。5. 循环冗余检测(Cyclic Redundancy Check,CRC) :使接收适配器检测帧中是否引入了差错,长度为 4 字节。6. 前同步码(preamble) :用在发送方和接收方之间同步时钟和比特流,长度为 8 字节。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>所有的以太网技术都向网络层提供 <strong>无连接服务(connectionless service)</strong> 与 <strong>不可靠服务<br>(unreliable service)</strong> 。</p><h4 id="5-5-2-CSMA-CD:以太网的多路访问协议"><a href="#5-5-2-CSMA-CD:以太网的多路访问协议" class="headerlink" title="5.5.2 CSMA/CD:以太网的多路访问协议"></a>5.5.2 CSMA/CD:以太网的多路访问协议</h4><p>CSMA/CD协议使用了以下机制:</p><ol><li><p>适配器可以在任何时刻开始传输,这就是说没有时隙的概念;</p></li><li><p>当一个适配器侦听到有某些其他的适配器正在传输,它决不会传输帧,这就是说它使用了载波侦听;</p></li><li><p>一旦传输中的适配器检测到另一个适配器正在传输,就中止它的传输,这就是说它使用了碰撞检测;</p></li><li><p>在尝试重传之前,适配器等待一个随机时间,这个时间通常比传输一帧的时间要短。</p></li></ol><p>CSMA/CD协议按下列方式工作:</p><ol><li><p>适配器从网络层得到一个数据报,准备一个以太网帧,并把该帧放到适配器缓存区中;</p></li><li><p>如果适配器帧听到信道空闲,它开始传输该帧,如果适配器侦听到信道忙,它等待到侦听不到信号能量,然后开始传输该帧;</p></li><li><p>在传输过程时,适配器监视来自其他适配器的信号能量的出现,如果该适配器传输了整个帧,而没有检测到来自其他适配器的信号能量,它就完成了该帧的传输;</p></li><li><p>如果适配器在传输中检测到来自其他适配器的信号能量,它就停止传输它的帧,而代之以传输</p></li></ol><pre class="line-numbers language-none"><code class="language-none">一个 48 比特的 阻塞(jam) 信号;5. 在中止以后,适配器进入一个 指数后退(exponential backoff) 阶段。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><strong>以太网效率(efficiency of Ethernet)</strong> :当有大量的活跃节点,且每个节点有大量的帧要发送时,帧在信道中无碰撞地传输的那部分时间占长期运行时的份额。</p><h4 id="5-5-3-以太网技术"><a href="#5-5-3-以太网技术" class="headerlink" title="5.5.3 以太网技术"></a>5.5.3 以太网技术</h4><p>以太网通过使用 <strong>转发器(repeater)</strong> 能够得到更长的距离,而转发器是一种物理层设备,能在输入端接收信号,并在输出端再生该信号。</p><h3 id="5-6-链路层交换机"><a href="#5-6-链路层交换机" class="headerlink" title="5.6 链路层交换机"></a>5.6 链路层交换机</h3><p>交换机的任务是接收入链路层帧并将它们转发出链路。</p><pre class="line-numbers language-none"><code class="language-none">交换机自身对节点是 透明的(transparent) 。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-6-1-交换机转发和过滤"><a href="#5-6-1-交换机转发和过滤" class="headerlink" title="5.6.1 交换机转发和过滤"></a>5.6.1 交换机转发和过滤</h4><pre class="line-numbers language-none"><code class="language-none">过滤(filtering) 是交换机决定一个帧是应该转发到某个接口还是应当将其丢弃的功能。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>转发(forwarding)</strong> 是决定一个帧应该被导向哪个接口,并把该帧接口移动到这些接口的交换机功能。</p><p>交换机的过滤和转发借助于 <strong>交换机表(switch table)</strong> 完成,交换机表中的一个表项包含:1)节点的MAC地址,2)到达该节点的交换机接口,3)用于节点的表项放置在表中的时间。</p><h4 id="5-6-2-自学习"><a href="#5-6-2-自学习" class="headerlink" title="5.6.2 自学习"></a>5.6.2 自学习</h4><p>交换机表是自动地、动态地、自治地建立的,即没有来自网络管理员或配置协议的任何干预,即交换机是 <strong>自学习(self-learning)</strong> 的,这种能力是以如下方式实现的:</p><pre class="line-numbers language-none"><code class="language-none">1. 交换机表初始为空;2. 对于在某接口接收到的每个入帧,该交换机在其表中存储:在该帧源地址字段中的MAC地址、该帧到达的接口、当前的时间;3. 如果在一段时间(称为 老化期(aging time) )后,交换机没有接收到以该址作为源地址的帧,就在表中删除这个地址。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">交换机是 即插即用设备(plug-and-play device) ,因为他们不需要网络管理员或用户的干预。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-6-3-链路层交换机的性质"><a href="#5-6-3-链路层交换机的性质" class="headerlink" title="5.6.3 链路层交换机的性质"></a>5.6.3 链路层交换机的性质</h4><p>使用交换机的优点:1)消除碰撞,2)异质的链路,3)管理。</p><h4 id="5-6-4-交换机和路由器的比较"><a href="#5-6-4-交换机和路由器的比较" class="headerlink" title="5.6.4 交换机和路由器的比较"></a>5.6.4 交换机和路由器的比较</h4><p>交换机是链路层的分组交换机,路由器是网络层的分组交换机。</p><p>交换机是即插即用的,路由器不是的。</p><p>交换机的活跃拓扑限制为一棵生成树,路由器能用各种拓扑结构来构建因特网。</p><h3 id="5-7-PPP:点对点协议"><a href="#5-7-PPP:点对点协议" class="headerlink" title="5.7 PPP:点对点协议"></a>5.7 PPP:点对点协议</h3><p><strong>点对点协议(PPP)</strong> 是一个运行于 <strong>点对点链路(point-to-point link)</strong> 之上的链路层协议,即一条直接连接两个节点的链路,链路的每一端有一个节点。</p><p>对PPP协议的设计提出的初始要求:分组成帧、透明性、多种网络层协议、多种类型链路、差错检测、连接的活性、网络层地址协商和简单性。</p><pre class="line-numbers language-none"><code class="language-none">不要求PPP实现的协议功能:差错纠正、流量控制、有序和多点链路。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><pre class="line-numbers language-none"><code class="language-none">PPP帧包含的字段:标志字段、地址字段、控制字段、协议、信息和检验和。为解决接收方会错误地检测为PPP帧结束,可以使用一种称为 字节填充(byte stuffing) 的技术。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="5-8-链路虚拟化:网络作为链路层"><a href="#5-8-链路虚拟化:网络作为链路层" class="headerlink" title="5.8 链路虚拟化:网络作为链路层"></a>5.8 链路虚拟化:网络作为链路层</h3><h4 id="5-8-1-异步传输方式"><a href="#5-8-1-异步传输方式" class="headerlink" title="5.8.1 异步传输方式"></a>5.8.1 异步传输方式</h4><p><strong>异步传输方式(Asynchronous Transfer Mode,ATM)</strong> :将比特分成小组进行传送,发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。</p><pre class="line-numbers language-none"><code class="language-none">ATM是一种分组交换、虚电路网络体系结构。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>3 个ATM层次自顶向下为: <strong>ATM适配层(ATM Adaptation Layer,AAL)</strong> 、<strong>ATM层(ATM layer)</strong> 和 <strong>ATM物理层(ATM physical layer)</strong> ,端系统拥有这个三个层次,而ATM交换机上拥有后两个层次。</p><p>AAL从或向较高层应用程序或协议传递数据。AAL已经被定义用于恒定比特率服务和电路仿真,用于如可变比特率视频这样的可变比特率服务,以及用于如IP数据报传输这样的数据服务。由AAL处理的数据单元称为AAL <strong>协议数据单元(Protocol Data Unit,PDU)</strong> 。</p><p>ATM层位于ATM体系结构的核心,定义了ATM信元的结构和该信元中各个字段的含义。ATM信元首部中的字段(信元的前 5 个字节,余下的 48 字节为有效载荷)为: <strong>虚通道标识(VCI)</strong> 、<strong>负载类型(PT)</strong> 、 <strong>信元丢失优先级(CLP)比特</strong> 和 <strong>首部差错控制(HEC)字节</strong> 。</p><pre class="line-numbers language-none"><code class="language-none">ATM物理层位于ATM协议栈的最底层,处理物理媒体上的电压、比特定时和成帧。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="5-8-2-多协议标签交换"><a href="#5-8-2-多协议标签交换" class="headerlink" title="5.8.2 多协议标签交换"></a>5.8.2 多协议标签交换</h4><p><strong>多协议标签交换(Multiprotocol Label Switching,MPLS)</strong> 是一种在开放的通信网上利用标签引导数据高速、高效传输的新技术。多协议的含义是指MPLS不但可以支持多种网络层层面上的协议,还可以兼容第二层的多种数据链路层技术。</p><p>一个MPLS加强的帧仅能在两个均为MPLS使能的路由器之间发送。<br>IP数据报进入MPLS网络时,MPLS入口的边缘路由器分析IP数据报的内容并且为这些IP数据报添加合适的标签,所有MPLS网络中的标签交换路由器根据标签转发数据,当该IP数据报离开MPLS网络时,标签由出口边缘路由器弹出。</p><p>使用MPLS的一种简单形式的 <strong>流量工程(traffic engineering)</strong> ,其中网络运行者能够超越普通的IP选路,迫使某些流量沿着一条路径朝给定的目的地引导,并且朝着相同目的地的其他流量沿着另一条路径流动。</p><p>MPLS能够并且已经被用于实现 <strong>虚拟专用网络(Virtual Private Network,VPN)</strong> :利用公用网络架设专用网络。</p><h2 id="5-9-小结"><a href="#5-9-小结" class="headerlink" title="5.9 小结"></a>5.9 小结</h2><p>链路层的基本服务是将网络层的数据报从一个节点移动到一个相邻的节点。</p><p>ARP协议用于IP地址与MAC地址间的转换。</p>]]></content>
<categories>
<category> 计算机网络 </category>
</categories>
<tags>
<tag> 计算机基础 </tag>
<tag> 计算机网络 </tag>
</tags>
</entry>
<entry>
<title>LeetCode146-- LRU Cache</title>
<link href="2021/05/25/LeetCode/LeetCode146LUR%E7%BC%93%E5%AD%98/"/>
<url>2021/05/25/LeetCode/LeetCode146LUR%E7%BC%93%E5%AD%98/</url>
<content type="html"><![CDATA[<h1 id="第146题:LRU缓存"><a href="#第146题:LRU缓存" class="headerlink" title="第146题:LRU缓存"></a>第146题:LRU缓存</h1><h3 id="题目描述:"><a href="#题目描述:" class="headerlink" title="题目描述:"></a>题目描述:</h3><p>运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。<br>实现 LRUCache 类:</p><ul><li>LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存</li><li>int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。</li><li>void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。</li></ul><p>进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?</p><h3 id="思路分析:"><a href="#思路分析:" class="headerlink" title="思路分析:"></a>思路分析:</h3><p>看到 <code>O(1)</code> 的时间复杂度首先想到的就是用 <code>HashMap</code> 去存储。</p><p>之后的问题就是怎么实现,当存满的时候删除最远一次操作过的元素。</p><p>可以用一个链表,每 <code>put</code> 一个 元素,就把它加到链表尾部。如果 <code>get</code> 某个元素,就把这个元素移动到链表尾部。当存满的时候,就把链表头的元素删除。</p><p>接下来还有一个问题就是,移动某个元素的时候,我们可以通过 <code>HashMap</code> 直接得到这个元素,但对于链表,如果想移动一个元素,肯定需要知道它的前一个节点才能操作。</p><p>而找到前一个元素,最直接的方法就是遍历一遍,但是这就使得算法的时间复杂度就不再是 <code>O(1)</code> 了。</p><p>另一种思路,就是使用双向链表,这样就可以直接得到它的前一个元素,从而实现移动操作。</p><p>综上,<code>HashMap</code> 加上双向链表即可解这道题了。</p><p><img src="https://windliang.oss-cn-beijing.aliyuncs.com/146_2.jpg" alt="img"></p><h3 id="解法:"><a href="#解法:" class="headerlink" title="解法:"></a>解法:</h3><p>首先定义节点:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">MyNode</span> <span class="token punctuation">{</span> <span class="token class-name">Object</span> key<span class="token punctuation">;</span> <span class="token class-name">Object</span> value<span class="token punctuation">;</span> <span class="token class-name">MyNode</span> prev <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token class-name">MyNode</span> next <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token class-name">MyNode</span><span class="token punctuation">(</span><span class="token class-name">Object</span> k<span class="token punctuation">,</span> <span class="token class-name">Object</span> v<span class="token punctuation">)</span> <span class="token punctuation">{</span> key <span class="token operator">=</span> k<span class="token punctuation">;</span> value <span class="token operator">=</span> v<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>定义双向链表类。</p><p>这里用了一个 <code>dummyHead</code> ,也就是哨兵节点,不存数据,可以把链表头结点等效于其他的节点,从而简化一些操作。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">DoubleLinkedList</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">MyNode</span> dummyHead <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyNode</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 头节点</span> <span class="token keyword">private</span> <span class="token class-name">MyNode</span> tail <span class="token operator">=</span> dummyHead<span class="token punctuation">;</span> <span class="token comment">//添加节点到末尾</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token class-name">MyNode</span> myNode<span class="token punctuation">)</span> <span class="token punctuation">{</span> tail<span class="token punctuation">.</span>next <span class="token operator">=</span> myNode<span class="token punctuation">;</span> myNode<span class="token punctuation">.</span>prev <span class="token operator">=</span> tail<span class="token punctuation">;</span> tail <span class="token operator">=</span> myNode<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//得到头结点</span> <span class="token keyword">public</span> <span class="token class-name">MyNode</span> <span class="token function">getHead</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> dummyHead<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//移除当前节点</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">removeMyNode</span><span class="token punctuation">(</span><span class="token class-name">MyNode</span> myNode<span class="token punctuation">)</span> <span class="token punctuation">{</span> myNode<span class="token punctuation">.</span>prev<span class="token punctuation">.</span>next <span class="token operator">=</span> myNode<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token comment">//判断删除的是否是尾节点</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>myNode<span class="token punctuation">.</span>next <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> myNode<span class="token punctuation">.</span>next<span class="token punctuation">.</span>prev <span class="token operator">=</span> myNode<span class="token punctuation">.</span>prev<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> tail <span class="token operator">=</span> myNode<span class="token punctuation">.</span>prev<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//全部指向 null</span> myNode<span class="token punctuation">.</span>prev <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> myNode<span class="token punctuation">.</span>next <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//移动当前节点到末尾</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">moveToTail</span><span class="token punctuation">(</span><span class="token class-name">MyNode</span> myNode<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">removeMyNode</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">add</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>接下来就是我们的LRU类</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LRUCache</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">int</span> capacity <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">,</span> <span class="token class-name">MyNode</span><span class="token punctuation">></span></span> map <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">DoubleLinkedList</span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DoubleLinkedList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">LRUCache</span><span class="token punctuation">(</span><span class="token keyword">int</span> capacity<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>capacity <span class="token operator">=</span> capacity<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//get 的同时要把当前节点移动到末尾</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token keyword">int</span> key<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>map<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">MyNode</span> myNode <span class="token operator">=</span> map<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span> list<span class="token punctuation">.</span><span class="token function">moveToTail</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> myNode<span class="token punctuation">.</span>value<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//对于之前存在的节点单独考虑</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">int</span> key<span class="token punctuation">,</span> <span class="token keyword">int</span> value<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>map<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">MyNode</span> myNode <span class="token operator">=</span> map<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span> myNode<span class="token punctuation">.</span>value <span class="token operator">=</span> value<span class="token punctuation">;</span> list<span class="token punctuation">.</span><span class="token function">moveToTail</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">//判断是否存满</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>map<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> capacity<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//从 map 和 list 中都删除头结点</span> <span class="token class-name">MyNode</span> head <span class="token operator">=</span> list<span class="token punctuation">.</span><span class="token function">getHead</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> map<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> head<span class="token punctuation">.</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span> list<span class="token punctuation">.</span><span class="token function">removeMyNode</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//插入当前元素</span> <span class="token class-name">MyNode</span> myNode <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyNode</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token class-name">MyNode</span> myNode <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyNode</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> myNode<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>最长公共子串与子序列问题</title>
<link href="2021/05/24/LeetCode/%E6%9C%80%E9%95%BF%E5%AD%90%E4%B8%B2%E4%B8%8E%E6%9C%80%E9%95%BF%E5%AD%90%E5%BA%8F%E5%88%97%E9%97%AE%E9%A2%98/"/>
<url>2021/05/24/LeetCode/%E6%9C%80%E9%95%BF%E5%AD%90%E4%B8%B2%E4%B8%8E%E6%9C%80%E9%95%BF%E5%AD%90%E5%BA%8F%E5%88%97%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<h1 id="最长公共子序列问题:"><a href="#最长公共子序列问题:" class="headerlink" title="最长公共子序列问题:"></a>最长公共子序列问题:</h1><p> 假如S1的最后一个元素 与 S2的最后一个元素<strong>相等</strong>,那么S1和S2的LCS就等于 {S1减去最后一个元素} 与 {S2减去最后一个元素} 的 LCS 再加上 S1和S2相等的最后一个元素。</p><p> 假如S1的最后一个元素 与 S2的最后一个元素<strong>不等</strong>(本例子就是属于这种情况),那么S1和S2的LCS就等于 : {S1减去最后一个元素} 与 S2 的LCS或者{S2减去最后一个元素} 与 S1 的LCS 中的最大的那个序列。</p><p><img src="https://img-blog.csdn.net/20160529234714513?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="img"></p><p>一、求长度:</p><p>二、求子序列:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token operator">*</span><span class="token punctuation">;</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">{</span> <span class="token comment">/** * longest common subsequence * @param s1 string字符串 the string * @param s2 string字符串 the string * @return string字符串 */</span> <span class="token keyword">public</span> <span class="token class-name">String</span> LCS <span class="token punctuation">(</span><span class="token class-name">String</span> s1<span class="token punctuation">,</span> <span class="token class-name">String</span> s2<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> len1 <span class="token operator">=</span> s1<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> len2 <span class="token operator">=</span> s2<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>len1 <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">||</span> len2 <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">"-1"</span><span class="token punctuation">;</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dp <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span>len1<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span>len2<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> len1<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator"><</span> len2<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">//初始化行列第一个元素</span> <span class="token keyword">if</span><span class="token punctuation">(</span>i <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">||</span> j <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>s1<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>i<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">==</span> s2<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> dp<span class="token punctuation">[</span>i<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span> dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>dp<span class="token punctuation">[</span>i<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">,</span> dp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span>j<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//找出一个最长的公共子序列</span> <span class="token class-name">StringBuilder</span> sb <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> s1L <span class="token operator">=</span> len1<span class="token punctuation">,</span> s2L <span class="token operator">=</span> len2<span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span>s1L <span class="token operator">!=</span> <span class="token number">0</span> <span class="token operator">&&</span> s2L <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>s1<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>s1L<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">==</span> s2<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>s2L<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> sb<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>s1<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>s1L <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> s1L<span class="token operator">--</span><span class="token punctuation">;</span> s2L<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>dp<span class="token punctuation">[</span>s1L<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span>s2L<span class="token punctuation">]</span> <span class="token operator">></span> dp<span class="token punctuation">[</span>s1L<span class="token punctuation">]</span><span class="token punctuation">[</span>s2L<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">{</span> s1L<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span> s2L<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>sb<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">"-1"</span><span class="token punctuation">;</span> <span class="token keyword">return</span> sb<span class="token punctuation">.</span><span class="token function">reverse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode235,236-- Lowest Common Ancestor of a Binary Tree</title>
<link href="2021/05/23/LeetCode/LeetCode236%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88/"/>
<url>2021/05/23/LeetCode/LeetCode236%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88/</url>
<content type="html"><![CDATA[<h1 id="第235题:二叉搜索树的最近公共祖先"><a href="#第235题:二叉搜索树的最近公共祖先" class="headerlink" title="第235题:二叉搜索树的最近公共祖先"></a>第235题:二叉搜索树的最近公共祖先</h1><h3 id="题目内容:"><a href="#题目内容:" class="headerlink" title="题目内容:"></a>题目内容:</h3><p>给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。</p><p>百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”</p><p>例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]</p><p><img src="https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/14/binarysearchtree_improved.png" alt="img"></p><p>示例 1:</p><pre class="line-numbers language-none"><code class="language-none">输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8输出: 6 解释: 节点 2 和节点 8 的最近公共祖先是 6。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>示例 2:</p><pre class="line-numbers language-none"><code class="language-none">输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4输出: 2解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="解法一:递归"><a href="#解法一:递归" class="headerlink" title="解法一:递归"></a>解法一:递归</h3><p>由于是二叉搜索树,所以找最近的共同祖先比较容易,总共就三种情况。</p><ul><li>如果给定的两个节点的值都小于根节点的值,那么最近的共同祖先一定在左子树</li><li>如果给定的两个节点的值都大于根节点的值,那么最近的共同祖先一定在右子树</li><li>如果一个大于等于、一个小于等于根节点的值,那么当前根节点就是最近的共同祖先了</li></ul><p>至于前两种情况用递归继续去解决即可。</p><p>代码的话,我们可以通过交换使得 <code>p.val <= q.val</code> ,这样就可以简化后边 <code>if</code> 语句的判断。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">TreeNode</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span><span class="token class-name">TreeNode</span> root<span class="token punctuation">,</span> <span class="token class-name">TreeNode</span> p<span class="token punctuation">,</span> <span class="token class-name">TreeNode</span> q<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 保持 p.val <= q.val</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>p<span class="token punctuation">.</span>val <span class="token operator">></span> q<span class="token punctuation">.</span>val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span>root<span class="token punctuation">,</span> q<span class="token punctuation">,</span> p<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//如果有一个是根节点就可以提前结束, 当然这个 if 不要也可以</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>p<span class="token punctuation">.</span>val <span class="token operator">==</span> root<span class="token punctuation">.</span>val <span class="token operator">||</span> q<span class="token punctuation">.</span>val <span class="token operator">==</span> root<span class="token punctuation">.</span>val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> root<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>q<span class="token punctuation">.</span>val <span class="token operator"><</span> root<span class="token punctuation">.</span>val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>left<span class="token punctuation">,</span> p<span class="token punctuation">,</span> q<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>p<span class="token punctuation">.</span>val <span class="token operator">></span> root<span class="token punctuation">.</span>val<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>right<span class="token punctuation">,</span> p<span class="token punctuation">,</span> q<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> root<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h1 id="第236题:二叉树的最近公共祖先"><a href="#第236题:二叉树的最近公共祖先" class="headerlink" title="第236题:二叉树的最近公共祖先"></a>第236题:二叉树的最近公共祖先</h1><h3 id="题目内容:-1"><a href="#题目内容:-1" class="headerlink" title="题目内容:"></a>题目内容:</h3><p>给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。</p><p>百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”</p><p>示例 1:</p><p><img src="https://assets.leetcode.com/uploads/2018/12/14/binarytree.png" alt="img"></p><pre class="line-numbers language-none"><code class="language-none">输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出:3解释:节点 5 和节点 1 的最近公共祖先是节点 3 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>示例 2:</p><p><img src="https://assets.leetcode.com/uploads/2018/12/14/binarytree.png" alt="img"></p><pre class="line-numbers language-none"><code class="language-none">输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4输出:5解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="解法一:递归-1"><a href="#解法一:递归-1" class="headerlink" title="解法一:递归"></a>解法一:递归</h3><p>我们注意到如果两个节点在左子树中的最近共同祖先是 <code>r</code>,那么当前树的最近公共祖先也就是 <code>r</code>,所以我们可以用递归的方式去解决。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">TreeNode</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span><span class="token class-name">TreeNode</span> root<span class="token punctuation">,</span> <span class="token class-name">TreeNode</span> p<span class="token punctuation">,</span> <span class="token class-name">TreeNode</span> q<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>root <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> root <span class="token operator">==</span> p <span class="token operator">||</span> root <span class="token operator">==</span> q <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> root<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">TreeNode</span> leftCommonAncestor <span class="token operator">=</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>left<span class="token punctuation">,</span> p<span class="token punctuation">,</span> q<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">TreeNode</span> rightCommonAncestor <span class="token operator">=</span> <span class="token function">lowestCommonAncestor</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>right<span class="token punctuation">,</span> p<span class="token punctuation">,</span> q<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//在左子树中没有找到,那一定在右子树中</span> <span class="token keyword">if</span><span class="token punctuation">(</span>leftCommonAncestor <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> rightCommonAncestor<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//在右子树中没有找到,那一定在左子树中</span> <span class="token keyword">if</span><span class="token punctuation">(</span>rightCommonAncestor <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> leftCommonAncestor<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//不在左子树,也不在右子树,那说明是根节点</span> <span class="token keyword">return</span> root<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对于 <code>lowestCommonAncestor</code> 这个函数的理解的话,它不一定可以返回最近的共同祖先,如果子树中只能找到 <code>p</code> 节点或者 <code>q</code> 节点,它最终返回其实就是 <code>p</code> 节点或者 <code>q</code> 节点。这其实对应于最后一种情况,也就是 <code>leftCommonAncestor</code> 和 <code>rightCommonAncestor</code> 都不为 <code>null</code>,说明 <code>p</code> 节点和 <code>q</code> 节点分处于两个子树中,直接 <code>return root</code>。</p><p>相对于解法一的话快了很多,因为不需要每次都遍历一遍二叉树,这个解法所有节点只会遍历一次。</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode232-- Implement Queue using Stacks</title>
<link href="2021/05/22/LeetCode/LeetCode232%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/"/>
<url>2021/05/22/LeetCode/LeetCode232%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/</url>
<content type="html"><![CDATA[<h1 id="题目232:用栈实现队列"><a href="#题目232:用栈实现队列" class="headerlink" title="题目232:用栈实现队列"></a>题目232:用栈实现队列</h1><h3 id="题目内容:"><a href="#题目内容:" class="headerlink" title="题目内容:"></a>题目内容:</h3><p><img src="https://i.loli.net/2021/08/20/wUbH8cWxTzoeuXE.png" alt="screenShot.png"></p><h3 id="解法一"><a href="#解法一" class="headerlink" title="解法一"></a>解法一</h3><p>我们使用两个栈,一个栈输入,一个栈输出。当需要查看或者出队的时候,我们就将输入栈元素依次放入到输出栈中,此时的输出栈的输出顺序刚好和队列是相符的。</p><p>这样的话,每个元素只会遍历一次了。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">MyQueue</span> <span class="token punctuation">{</span> <span class="token class-name">Stack</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">></span></span> input <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Stack</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">></span></span> output <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span> <span class="token punctuation">{</span> input<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">peek</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> output<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">peek</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>output<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>input<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> output<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>input<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> output<span class="token punctuation">.</span><span class="token function">peek</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> input<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> output<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode141-- Linked List Cycle</title>
<link href="2021/05/21/LeetCode/LeetCode141%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8/"/>
<url>2021/05/21/LeetCode/LeetCode141%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8/</url>
<content type="html"><![CDATA[<h1 id="题目141:环形链表"><a href="#题目141:环形链表" class="headerlink" title="题目141:环形链表"></a>题目141:环形链表</h1><h3 id="题目内容:"><a href="#题目内容:" class="headerlink" title="题目内容:"></a>题目内容:</h3><p><img src="https://i.loli.net/2021/08/20/bAaV8DG43lnWdEX.png" alt="screenShot.png"></p><h3 id="解法一:结果去重"><a href="#解法一:结果去重" class="headerlink" title="解法一:结果去重"></a>解法一:结果去重</h3><p>遍历链表,使用HashSet保存起来,如果重复就说明有环</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">hasCycle</span><span class="token punctuation">(</span><span class="token class-name">ListNode</span> head<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">ListNode</span><span class="token punctuation">></span></span> set <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>head <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span><span class="token punctuation">;</span> head <span class="token operator">=</span> head<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>set<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>head<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="解法二:快慢指针"><a href="#解法二:快慢指针" class="headerlink" title="解法二:快慢指针"></a>解法二:快慢指针</h3><p>想象一下圆形跑道,两个人跑步,如果一个人跑的快,一个人跑的慢,那么不管两个人从哪个位置出发,跑的过程中两人一定会相遇。</p><p>所以这里我们用两个指针 <code>fast</code> 和 <code>slow</code>。<code>fast</code> 每次走两步,<code>slow</code> 每次走一步,如果 <code>fast</code> 到达了 <code>null</code> 就说明没有环。如果 <code>fast</code> 和 <code>slow</code> 相遇了就说明有环。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">hasCycle</span><span class="token punctuation">(</span><span class="token class-name">ListNode</span> head<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">ListNode</span> slow <span class="token operator">=</span> head<span class="token punctuation">;</span> <span class="token class-name">ListNode</span> fast <span class="token operator">=</span> head<span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>fast <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>fast<span class="token punctuation">.</span>next <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> slow <span class="token operator">=</span> slow<span class="token punctuation">.</span>next<span class="token punctuation">;</span> fast <span class="token operator">=</span> fast<span class="token punctuation">.</span>next<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>fast <span class="token operator">==</span> slow<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode206-- Reverse Linked List</title>
<link href="2021/05/20/LeetCode/LeetCode206%E5%8F%8D%E8%BD%AC%E9%93%BE%E8%A1%A8/"/>
<url>2021/05/20/LeetCode/LeetCode206%E5%8F%8D%E8%BD%AC%E9%93%BE%E8%A1%A8/</url>
<content type="html"><![CDATA[<h1 id="第206题:反转链表(Easy)"><a href="#第206题:反转链表(Easy)" class="headerlink" title="第206题:反转链表(Easy)"></a>第206题:反转链表(Easy)</h1><h3 id="题目描述:"><a href="#题目描述:" class="headerlink" title="题目描述:"></a>题目描述:</h3><p><img src="https://i.loli.net/2021/08/20/zoruTb4vDFhXIHn.png"></p><h3 id="这题采用迭代思想"><a href="#这题采用迭代思想" class="headerlink" title="这题采用迭代思想"></a>这题采用迭代思想</h3><p>首先看一下原链表。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l0.jpg" alt="img"></p><p>总共需要添加两个指针,<code>pre</code> 和 <code>next</code>。</p><p>初始化 <code>pre</code> 指向 <code>NULL</code> 。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l00.jpg" alt="img"></p><p>然后就是迭代的步骤,总共四步,顺序一步都不能错。</p><ul><li><p><code>next</code> 指向 <code>head</code> 的 <code>next</code> ,防止原链表丢失</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l1.jpg" alt="img"></p></li><li><p><code>head</code> 的 <code>next</code> 从原来链表脱离,指向 <code>pre</code> 。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l2.jpg" alt="img"></p></li><li><p><code>pre</code> 指向 <code>head</code></p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l3.jpg" alt="img"></p></li><li><p><code>head</code> 指向 <code>next</code></p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l4.jpg" alt="img"></p></li></ul><p>一次迭代就完成了,如果再进行一次迭代就变成下边的样子。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l5.jpg" alt="img"></p><p>可以看到整个过程无非是把旧链表的 <code>head</code> 取下来,添加的新链表头部。代码怎么写呢?</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">next <span class="token operator">=</span> head <span class="token operator">-></span> next<span class="token punctuation">;</span> <span class="token comment">//保存 head 的 next , 以防取下 head 后丢失</span>head <span class="token operator">-></span> next <span class="token operator">=</span> pre<span class="token punctuation">;</span> <span class="token comment">//将 head 从原链表取下来,添加到新链表上</span>pre <span class="token operator">=</span> head<span class="token punctuation">;</span><span class="token comment">// pre 右移</span>head <span class="token operator">=</span> next<span class="token punctuation">;</span> <span class="token comment">// head 右移</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>接下来就是停止条件了,我们再进行一次循环。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/l6.jpg" alt="img"></p><p>可以发现当 <code>head</code> 或者 <code>next</code> 指向 <code>null</code> 的时候,我们就可以停止了。此时将 <code>pre</code> 返回,便是逆序了的链表了。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">ListNode</span> <span class="token function">reverseList</span><span class="token punctuation">(</span><span class="token class-name">ListNode</span> head<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>head <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token class-name">ListNode</span> pre <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token class-name">ListNode</span> next<span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>head <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> next <span class="token operator">=</span> head<span class="token punctuation">.</span>next<span class="token punctuation">;</span> head<span class="token punctuation">.</span>next <span class="token operator">=</span> pre<span class="token punctuation">;</span> pre <span class="token operator">=</span> head<span class="token punctuation">;</span> head <span class="token operator">=</span> next<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> pre<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode105-- Construct Binary Tree from Preorder and Inorder Traversal</title>
<link href="2021/05/17/LeetCode/LeetCode105%E6%A0%B9%E6%8D%AE%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E5%92%8C%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E6%9E%84%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91/"/>
<url>2021/05/17/LeetCode/LeetCode105%E6%A0%B9%E6%8D%AE%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E5%92%8C%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E6%9E%84%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91/</url>
<content type="html"><![CDATA[<h1 id="第105题:从前序和中序遍历构造二叉树(Medium)"><a href="#第105题:从前序和中序遍历构造二叉树(Medium)" class="headerlink" title="第105题:从前序和中序遍历构造二叉树(Medium)"></a>第105题:从前序和中序遍历构造二叉树(Medium)</h1><p>题目描述:给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。</p><p>解法:我们可以利用一个栈,用迭代实现。</p><p>假设我们要还原的树是下图</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"> <span class="token number">3</span> <span class="token operator">/</span> \ <span class="token number">9</span> <span class="token number">7</span> <span class="token operator">/</span> \<span class="token number">20</span> <span class="token number">15</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>首先假设我们只有先序遍历的数组,如果还原一颗树,会遇到什么问题。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">preorder <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>首先我们把 <code>3</code> 作为根节点,然后到了 <code>9</code> ,就出现一个问题,<code>9</code> 是左子树还是右子树呢?</p><p>所以需要再加上中序遍历的数组来确定。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">inorder <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>我们知道中序遍历,首先遍历左子树,然后是根节点,最后是右子树。这里第一个遍历的是 <code>20</code> ,说明先序遍历的 <code>9</code> 一定是左子树,利用反证法证明。</p><p>假如 <code>9</code> 是右子树,根据先序遍历 <code>preorder = [ 3, 9, 20, 15, 7 ]</code>,说明根节点 <code>3</code> 的左子树是空的,</p><p>左子树为空,那么中序遍历就会先遍历根节点 <code>3</code>,而此时是 <code>20</code>,假设不成立,说明 <code>9</code> 是左子树。</p><p>接下来的 <code>20</code> 同理,所以可以目前构建出来的树如下。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"> <span class="token number">3</span> <span class="token operator">/</span> <span class="token number">9</span> <span class="token operator">/</span> <span class="token number">20</span><span class="token class-name">Copy</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>同时,还注意到此时先序遍历的 <code>20</code> 和中序遍历 <code>20</code> 相等了,说明什么呢?</p><p>说明中序遍历的下一个数 <code>15</code> 不是左子树了,如果是左子树,那么中序遍历的第一个数就不会是 <code>20</code>。</p><p>所以 <code>15</code> 一定是右子树了,现在还有个问题,它是 <code>20</code> 的右子树,还是 <code>9</code> 的右子树,还是 <code>3</code> 的右子树?</p><p>我们来假设几种情况,来想一下。</p><ol><li>如果是 <code>3</code> 的右子树, <code>20</code> 和 <code>9</code> 的右子树为空,那么中序遍历就是<code>20 9 3 15</code>。</li><li>如果是 <code>9</code> 的右子树,<code>20</code> 的右子树为空,那么中序遍历就是<code>20 9 15</code>。</li><li>如果是 <code>20</code> 的右子树,那么中序遍历就是<code>20 15</code>。</li></ol><p>之前已经遍历的根节点是 <code>3 9 20</code>,<strong>把它倒过来,即<code>20 9 3</code><strong>,然后和上边的三种中序遍历比较,会发现 <code>15</code> 就是</strong>最后一次相等</strong>的节点的右子树。</p><p>第 1 种情况,中序遍历是<code>20 9 3 15</code>,和<code>20 9 3</code> 都相等,所以 <code>15</code> 是<code>3</code> 的右子树。</p><p>第 2 种情况,中序遍历是<code>20 9 15</code>,只有<code>20 9</code> 相等,所以 <code>15</code> 是 <code>9</code> 的右子树。</p><p>第 3 种情况,中序遍历就是<code>20 15</code>,只有<code>20</code> 相等,所以 <code>20</code> 是 <code>15</code> 的右子树。</p><p>而此时我们的中序遍历数组是<code>inorder = [ 20, 9 ,15, 3, 7 ]</code>,<code>20</code> 匹配,<code>9</code>匹配,最后一次匹配是 <code>9</code>,所以 <code>15</code> 是 <code>9</code>的右子树。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"> <span class="token number">3</span> <span class="token operator">/</span> <span class="token number">9</span> <span class="token operator">/</span> \ <span class="token number">20</span> <span class="token number">15</span><span class="token class-name">Copy</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>综上所述,我们用一个栈保存已经遍历过的节点,遍历前序遍历的数组,一直作为当前根节点的左子树,直到当前节点和中序遍历的数组的节点相等了,那么我们正序遍历中序遍历的数组,倒着遍历已经遍历过的根节点(用栈的 pop 实现),找到最后一次相等的位置,把它作为该节点的右子树。</p><p>上边的分析就是迭代总体的思想,代码的话还有一些细节注意一下。用一个栈保存已经遍历的节点,用 curRoot 保存当前正在遍历的节点。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">TreeNode</span> <span class="token function">buildTree</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> preorder<span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> inorder<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>preorder<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">Stack</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TreeNode</span><span class="token punctuation">></span></span> roots <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stack</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TreeNode</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> pre <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">int</span> in <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//先序遍历第一个值作为根节点</span> <span class="token class-name">TreeNode</span> curRoot <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TreeNode</span><span class="token punctuation">(</span>preorder<span class="token punctuation">[</span>pre<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">TreeNode</span> root <span class="token operator">=</span> curRoot<span class="token punctuation">;</span> roots<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>curRoot<span class="token punctuation">)</span><span class="token punctuation">;</span> pre<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token comment">//遍历前序遍历的数组</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>pre <span class="token operator"><</span> preorder<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//出现了当前节点的值和中序遍历数组的值相等,寻找是谁的右子树</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>curRoot<span class="token punctuation">.</span>val <span class="token operator">==</span> inorder<span class="token punctuation">[</span>in<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//每次进行出栈,实现倒着遍历</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>roots<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> roots<span class="token punctuation">.</span><span class="token function">peek</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>val <span class="token operator">==</span> inorder<span class="token punctuation">[</span>in<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> curRoot <span class="token operator">=</span> roots<span class="token punctuation">.</span><span class="token function">peek</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> roots<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> in<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//设为当前的右孩子</span> curRoot<span class="token punctuation">.</span>right <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TreeNode</span><span class="token punctuation">(</span>preorder<span class="token punctuation">[</span>pre<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//更新 curRoot</span> curRoot <span class="token operator">=</span> curRoot<span class="token punctuation">.</span>right<span class="token punctuation">;</span> roots<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>curRoot<span class="token punctuation">)</span><span class="token punctuation">;</span> pre<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">//否则的话就一直作为左子树</span> curRoot<span class="token punctuation">.</span>left <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TreeNode</span><span class="token punctuation">(</span>preorder<span class="token punctuation">[</span>pre<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> curRoot <span class="token operator">=</span> curRoot<span class="token punctuation">.</span>left<span class="token punctuation">;</span> roots<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>curRoot<span class="token punctuation">)</span><span class="token punctuation">;</span> pre<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> root<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode104-- Maximum Depth of Binary Tree</title>
<link href="2021/05/15/LeetCode/LeetCode104%20%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E5%A4%A7%E6%B7%B1%E5%BA%A6/"/>
<url>2021/05/15/LeetCode/LeetCode104%20%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E5%A4%A7%E6%B7%B1%E5%BA%A6/</url>
<content type="html"><![CDATA[<h1 id="第104题:二叉树的最大深度-Easy"><a href="#第104题:二叉树的最大深度-Easy" class="headerlink" title="第104题:二叉树的最大深度(Easy)"></a>第104题:二叉树的最大深度(Easy)</h1><p>题目:给定一个二叉树,找出其最大深度。</p><p>二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。</p><p><strong>说明:</strong> 叶子节点是指没有子节点的节点。</p><p><strong>示例:</strong><br>给定二叉树 <code>[3,9,20,null,null,15,7]</code>,</p><pre class="line-numbers language-none"><code class="language-none"> 3 / \9 20 / \ 15 7<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>返回它的最大深度 3 。</p><p>解答:依旧是考的二叉树的遍历。最简单的思路就是用递归进行 DFS 即可。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">maxDepth</span><span class="token punctuation">(</span><span class="token class-name">TreeNode</span> root<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>root <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span><span class="token function">maxDepth</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>left<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">maxDepth</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>right<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode102--Binary Tree Level Order Traversal</title>
<link href="2021/05/12/LeetCode/LeetCode102%20%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86/"/>
<url>2021/05/12/LeetCode/LeetCode102%20%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86/</url>
<content type="html"><![CDATA[<h1 id="第102题:二叉树的层序遍历(Medium)"><a href="#第102题:二叉树的层序遍历(Medium)" class="headerlink" title="第102题:二叉树的层序遍历(Medium)"></a>第102题:二叉树的层序遍历(Medium)</h1><p>题目:给你一个二叉树,请你返回其按 <strong>层序遍历</strong> 得到的节点值。 (即逐层地,从左到右访问所有节点)。</p><p><strong>示例:</strong><br>二叉树:<code>[3,9,20,null,null,15,7]</code>,</p><pre class="line-numbers language-none"><code class="language-none"> 3 / \9 20 / \ 15 7<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>返回其层序遍历结果:</p><pre class="line-numbers language-none"><code class="language-none">[ [3], [9,20], [15,7]]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>解答:这道题考的就是 BFS,我们可以通过 DFS 实现。只需要在递归过程中将当前 level 传入即可。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">List</span><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">levelOrder</span><span class="token punctuation">(</span><span class="token class-name">TreeNode</span> root<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">List</span><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">></span><span class="token punctuation">></span></span> ans <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">DFS</span><span class="token punctuation">(</span>root<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> ans<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">DFS</span><span class="token punctuation">(</span><span class="token class-name">TreeNode</span> root<span class="token punctuation">,</span> <span class="token keyword">int</span> level<span class="token punctuation">,</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">List</span><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">></span><span class="token punctuation">></span></span> ans<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>root <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//当前层数还没有元素,先 new 一个空的列表</span> <span class="token keyword">if</span><span class="token punctuation">(</span>ans<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator"><=</span>level<span class="token punctuation">)</span><span class="token punctuation">{</span> ans<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//当前值加入</span> ans<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>level<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>val<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">DFS</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>left<span class="token punctuation">,</span>level<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">,</span>ans<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">DFS</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>right<span class="token punctuation">,</span>level<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">,</span>ans<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode96--Unique Binary Search Tree</title>
<link href="2021/05/11/LeetCode/LeetCode96%20%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/"/>
<url>2021/05/11/LeetCode/LeetCode96%20%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/</url>
<content type="html"><![CDATA[<h1 id="第96题:不同的二叉搜索树(Medium)"><a href="#第96题:不同的二叉搜索树(Medium)" class="headerlink" title="第96题:不同的二叉搜索树(Medium)"></a>第96题:不同的二叉搜索树(Medium)</h1><p>题目描述:给你一个整数 <code>n</code> ,求恰由 <code>n</code> 个节点组成且节点值从 <code>1</code> 到 <code>n</code> 互不相同的 <strong>二叉搜索树</strong> 有多少种?返回满足题意的二叉搜索树的种数。</p><p> <img src="https://assets.leetcode.com/uploads/2021/01/18/uniquebstn3.jpg" alt="img"></p><pre class="line-numbers language-none"><code class="language-none">输入:n = 3输出:5<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>解法:我们可以利用动态规划的思想,直接从底部往上走。求出长度是 0,长度是 1,长度是 2….长度是 n 的解。用一个数组 dp 把这些结果全部保存起来。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">numTrees</span><span class="token punctuation">(</span><span class="token keyword">int</span> n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dp <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span> dp<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 长度为 1 到 n</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> len <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> len <span class="token operator"><=</span> n<span class="token punctuation">;</span> len<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 将不同的数字作为根节点,只需要考虑到 len</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> root <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> root <span class="token operator"><=</span> len<span class="token punctuation">;</span> root<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> left <span class="token operator">=</span> root <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 左子树的长度</span> <span class="token keyword">int</span> right <span class="token operator">=</span> len <span class="token operator">-</span> root<span class="token punctuation">;</span> <span class="token comment">// 右子树的长度</span> dp<span class="token punctuation">[</span>len<span class="token punctuation">]</span> <span class="token operator">+=</span> dp<span class="token punctuation">[</span>left<span class="token punctuation">]</span> <span class="token operator">*</span> dp<span class="token punctuation">[</span>right<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> dp<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode09--Palindrome Number</title>
<link href="2021/05/09/LeetCode/LeetCode09%20%E5%9B%9E%E6%96%87%E6%95%B0/"/>
<url>2021/05/09/LeetCode/LeetCode09%20%E5%9B%9E%E6%96%87%E6%95%B0/</url>
<content type="html"><![CDATA[<h1 id="题目09:回文数(简单难度)"><a href="#题目09:回文数(简单难度)" class="headerlink" title="题目09:回文数(简单难度)"></a>题目09:回文数(简单难度)</h1><h3 id="解法一:"><a href="#解法一:" class="headerlink" title="解法一:"></a>解法一:</h3><p>思路:我们应该联想到第七题,整数的反转,判断一个数字是不是回文数,我们可以将它反转过后与原数字比较。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">reverse</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> rev <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span>x<span class="token operator">!=</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> pop <span class="token operator">=</span> x<span class="token operator">%</span><span class="token number">10</span><span class="token punctuation">;</span> x<span class="token operator">/=</span><span class="token number">10</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>rev<span class="token operator">></span><span class="token class-name">Integer</span><span class="token punctuation">.</span>MAX_VALUE<span class="token operator">/</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>rev<span class="token operator"><</span><span class="token class-name">Integer</span><span class="token punctuation">.</span>MIN_VALUE<span class="token operator">/</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> rev <span class="token operator">=</span> rev<span class="token operator">*</span><span class="token number">10</span><span class="token operator">+</span>pop<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> rev<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isPalindrome</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>x <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>x <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">int</span> rev <span class="token operator">=</span> <span class="token function">reverse</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> x<span class="token operator">==</span>rev<span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:和求转置⼀样,x 有多少位,就循环多少次,所以是 O(log(x)) 。</p><p>空间复杂度:O(1)。</p><h3 id="解法二:"><a href="#解法二:" class="headerlink" title="解法二:"></a>解法二:</h3><p>思路:我们只需要将这个数的一半进行反转比较就可以了,这样可以减少一半的计算量。</p><p>这里我们要会求一个整数的位数是多少,对于位数较少的情况我们可以用if >10,>100…来确定。对于位数较多的一般求法是(int)Math.log10(x)+1。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isPalindrome</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>x <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>x <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">int</span> digit <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">log10</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> reverse <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span>pop <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>i <span class="token operator"><</span> digit<span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> pop <span class="token operator">=</span> x<span class="token operator">%</span><span class="token number">10</span><span class="token punctuation">;</span> reverse <span class="token operator">=</span> reverse<span class="token operator">*</span><span class="token number">10</span><span class="token operator">+</span>pop<span class="token punctuation">;</span> x <span class="token operator">/=</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>digit<span class="token operator">%</span><span class="token number">2</span><span class="token operator">==</span><span class="token number">0</span><span class="token operator">&&</span>x<span class="token operator">==</span>reverse<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>digit<span class="token operator">%</span><span class="token number">2</span><span class="token operator">!=</span><span class="token number">0</span><span class="token operator">&&</span>x<span class="token operator">/</span><span class="token number">10</span><span class="token operator">==</span>reverse<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:循环 x 的总位数的⼀半次,所以时间复杂度依旧是 O(log(x))。 </p><p>空间复杂度:O(1),常数个变量。</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode07--Reverse Integer</title>
<link href="2021/05/07/LeetCode/LeetCode07%20%E6%95%B4%E6%95%B0%E5%8F%8D%E8%BD%AC/"/>
<url>2021/05/07/LeetCode/LeetCode07%20%E6%95%B4%E6%95%B0%E5%8F%8D%E8%BD%AC/</url>
<content type="html"><![CDATA[<h1 id="题目07:整数反转(简单难度)"><a href="#题目07:整数反转(简单难度)" class="headerlink" title="题目07:整数反转(简单难度)"></a>题目07:整数反转(简单难度)</h1><h3 id="解法一:"><a href="#解法一:" class="headerlink" title="解法一:"></a>解法一:</h3><p>思路:我们不难想到将这个数的每一位除出去存储在新的数中</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">reverse</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> rev <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span>x<span class="token operator">!=</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> pop<span class="token operator">=</span>x<span class="token operator">%</span><span class="token number">10</span><span class="token punctuation">;</span> x<span class="token operator">/=</span><span class="token number">10</span><span class="token punctuation">;</span> rev <span class="token operator">=</span> rev<span class="token operator">*</span><span class="token number">10</span> <span class="token operator">+</span> pop<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">return</span> rev<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>但是:</strong>题目中要求反转后的数如果超过32位则返回0,所以我们应该进行判断,我们可以把rev设置成long类型</p><pre class="line-numbers language-none"><code class="language-none">public int reverse(int x) { long rev = 0; while(x!=0){ int pop=x%10; x/=10; rev = rev*10 + pop; }if (rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE ) return 0;return (int)rev;}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:循环多少次呢?数字有多少位,就循环多少次,也就是 $$log_{10}(x) + 1$$ 次,所以时间复 杂度是 O(log(x))</p><p>空间复杂度:O(1)</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode05--Longest Palindromic Substring</title>
<link href="2021/05/07/LeetCode/LeetCode05%20%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/"/>
<url>2021/05/07/LeetCode/LeetCode05%20%E6%9C%80%E9%95%BF%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2/</url>
<content type="html"><![CDATA[<h1 id="第五题:最长回文子串"><a href="#第五题:最长回文子串" class="headerlink" title="第五题:最长回文子串"></a>第五题:最长回文子串</h1><h3 id="题目描述:"><a href="#题目描述:" class="headerlink" title="题目描述:"></a>题目描述:</h3><p>给你一个字符串 s,找到 s 中最长的回文子串。</p><p>示例 1:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">输入:s <span class="token operator">=</span> <span class="token string">"babad"</span>输出:<span class="token string">"bab"</span>解释:<span class="token string">"aba"</span> 同样是符合题意的答案。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>示例 2:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">输入:s <span class="token operator">=</span> <span class="token string">"cbbd"</span>输出:<span class="token string">"bb"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="解法:扩散中心法"><a href="#解法:扩散中心法" class="headerlink" title="解法:扩散中心法"></a>解法:扩散中心法</h3><p>我们知道回文串一定是对称的,所以我们可以每次循环选择一个中心,进行左右扩展,判断左右字符是否相等即可。</p><p><img src="http://windliang.oss-cn-beijing.aliyuncs.com/5_6.jpg" alt="img"></p><p>由于存在奇数的字符串和偶数的字符串,所以我们需要从一个字符开始扩展,或者从两个字符之间开始扩展,所以总共有 n + n - 1 个中心。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">longestPalindrome</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>s <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token keyword">int</span> start <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> end <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> len1 <span class="token operator">=</span> <span class="token function">expandAroundCenter</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> i<span class="token punctuation">,</span> i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> len2 <span class="token operator">=</span> <span class="token function">expandAroundCenter</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> i<span class="token punctuation">,</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> len <span class="token operator">=</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>len1<span class="token punctuation">,</span> len2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>len <span class="token operator">></span> end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token punctuation">{</span> start <span class="token operator">=</span> i <span class="token operator">-</span> <span class="token punctuation">(</span>len <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> end <span class="token operator">=</span> i <span class="token operator">+</span> len <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> s<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>start<span class="token punctuation">,</span> end <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">private</span> <span class="token keyword">int</span> <span class="token function">expandAroundCenter</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">,</span> <span class="token keyword">int</span> left<span class="token punctuation">,</span> <span class="token keyword">int</span> right<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> <span class="token class-name">L</span> <span class="token operator">=</span> left<span class="token punctuation">,</span> <span class="token class-name">R</span> <span class="token operator">=</span> right<span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token class-name">L</span> <span class="token operator">>=</span> <span class="token number">0</span> <span class="token operator">&&</span> <span class="token class-name">R</span> <span class="token operator"><</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span><span class="token class-name">L</span><span class="token punctuation">)</span> <span class="token operator">==</span> s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span><span class="token class-name">R</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">L</span><span class="token operator">--</span><span class="token punctuation">;</span> <span class="token class-name">R</span><span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token class-name">R</span> <span class="token operator">-</span> <span class="token class-name">L</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token class-name">Copy</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:O(n²)。</p><p>空间复杂度:O(1)。</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode03--Longest Substring Without Repeating Characters</title>
<link href="2021/05/06/LeetCode/LeetCode03%20%E6%97%A0%E9%87%8D%E5%A4%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%97%E4%B8%B2/"/>
<url>2021/05/06/LeetCode/LeetCode03%20%E6%97%A0%E9%87%8D%E5%A4%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E6%9C%80%E9%95%BF%E5%AD%97%E4%B8%B2/</url>
<content type="html"><![CDATA[<h1 id="题目3:无重复字符的最长子串"><a href="#题目3:无重复字符的最长子串" class="headerlink" title="题目3:无重复字符的最长子串"></a>题目3:无重复字符的最长子串</h1><h3 id="解法一:"><a href="#解法一:" class="headerlink" title="解法一:"></a>解法一:</h3><p>思路:我们设置两个循环,将未重复的字符添加到集合中,并计算长度,如果出现重复元素则清空集合重新开始。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">lengthOfLongestSubstring</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator"><=</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> ans <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token comment">//保存当前满足条件的子串的最大值</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Character</span><span class="token punctuation">></span></span> set <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator"><</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator"><</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>set<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ans <span class="token operator">=</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>ans<span class="token punctuation">,</span>j<span class="token operator">-</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">removeAll</span><span class="token punctuation">(</span>set<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:O(n^2)</p><p>空间复杂度:使⽤了⼀个 set,判断⼦串中有没有重复的字符。由于 set 中没有重复的字符,所以最⻓就是 整个字符集,假设字符集的⼤⼩为 m ,那么 set 最⻓就是 m 。另⼀⽅⾯,如果字符串的⻓度⼩于 m ,是 n 。那么 set 最⻓也就是 n 了。综上,空间复杂度为 O(min(m,n))</p><h3 id="解法二:(解法一优化)"><a href="#解法二:(解法一优化)" class="headerlink" title="解法二:(解法一优化)"></a>解法二:(解法一优化)</h3><p>思路:我们不难发现,清空一整个集合是没有必要的,因为当i+1的时候,一直到上一个j肯定是没有重复元素的,因此我们可以减少一个循环优化一下。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token function">lengthOfLongestSubstring</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator"><=</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> ans <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">int</span> n <span class="token operator">=</span> s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Character</span><span class="token punctuation">></span></span> set <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span>i <span class="token operator"><</span> n <span class="token operator">&&</span> j <span class="token operator"><</span> n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>set<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ans <span class="token operator">=</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>ans<span class="token punctuation">,</span>j<span class="token operator">-</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token punctuation">{</span> set<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:在最坏的情况下,while 循环中的语句会执⾏ 2n 次,例如 abcdefgg,开始的时候 j ⼀直后移 直到到达第⼆个 g 的时候固定不变 ,然后 i 开始⼀直后移直到 n ,所以总共执⾏了 2n 次,时间复杂度为 O (n)。 </p><p>空间复杂度:和上边的类似,需要⼀个 Hash 保存⼦串,所以是 O(min(m,n))</p><h3 id="解法三:(解法二的优化)"><a href="#解法三:(解法二的优化)" class="headerlink" title="解法三:(解法二的优化)"></a>解法三:(解法二的优化)</h3><p>思路:在解法二中当我们j向后移动到重复元素且该重复字符在子字符串的中间甚至后面时,我们得在HashSet中一一删除前面的元素直到找到重复的那个元素然后再开始;我们可以用Map优化,使i直接跳到重复的位置开始。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">lengthOfLongestSubstring</span><span class="token punctuation">(</span><span class="token class-name">String</span> s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> n <span class="token operator">=</span>s<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>ans <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Character</span><span class="token punctuation">,</span><span class="token class-name">Integer</span><span class="token punctuation">></span></span> map <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span>i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>j<span class="token operator"><</span>n<span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>map<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> i <span class="token operator">=</span> <span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>map<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">,</span>j<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ans<span class="token operator">=</span><span class="token class-name">Math</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>ans<span class="token punctuation">,</span>j<span class="token operator">-</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>与解法⼆相⽐ 由于采取了 i 跳跃的形式,所以 map 之前存的字符没有进⾏ remove ,所以 if 语句中进⾏了Math.max ( map.get ( s.charAt ( j ) ) , i ),要确认得到的下标不是 i 前边的。 还有个不同之处是 j 每次循环都进⾏了⾃加 1 ,因为 i 的跳跃已经保证了 str[ i , j] 内没有重复的字符串,所 以 j 直接可以加 1 。⽽解法⼆中,要保持 j 的位置不变,因为不知道和 j 重复的字符在哪个位置。 最后个不同之处是, ans 在每次循环中都进⾏更新,因为 ans 更新前 i 都进⾏了更新,已经保证了当前的 ⼦串符合条件,所以可以更新 ans 。⽽解法⼆中,只有当当前的⼦串不包含当前的字符时,才进⾏更新。</p><p> 时间复杂度:我们将 2n 优化到了 n ,但最终还是和之前⼀样,O(n)。 </p><p>空间复杂度:也是⼀样的,O(min(m,n))。</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>LeetCode02--Add Two Numbers</title>
<link href="2021/05/03/LeetCode/LeetCode02%20%E4%B8%A4%E6%95%B0%E7%9B%B8%E5%8A%A0%20/"/>
<url>2021/05/03/LeetCode/LeetCode02%20%E4%B8%A4%E6%95%B0%E7%9B%B8%E5%8A%A0%20/</url>
<content type="html"><![CDATA[<h1 id="题目02:两数相加(中等难度)"><a href="#题目02:两数相加(中等难度)" class="headerlink" title="题目02:两数相加(中等难度)"></a>题目02:两数相加(中等难度)</h1><h3 id="题目描述:"><a href="#题目描述:" class="headerlink" title="题目描述:"></a>题目描述:</h3><p>给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。</p><p>请你将两个数相加,并以相同形式返回一个表示和的链表。</p><p>你可以假设除了数字 0 之外,这两个数都不会以 0 开头。</p><p>示例1:</p><p><img src="https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2021/01/02/addtwonumber1.jpg"></p><pre class="line-numbers language-none"><code class="language-none">输入:l1 = [2,4,3], l2 = [5,6,4]输出:[7,0,8]解释:342 + 465 = 807.<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>示例2:</p><pre class="line-numbers language-none"><code class="language-none">输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]输出:[8,9,9,9,0,0,0,1]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><strong>思路:</strong>由于这个链表是倒序的,那么两个链表的相加是符合我们正常的加法运算的。我们通常会对相应位数的两个数字相加,如果大于十则产生进位,我们可以用一个变量carry记录并传到下一位的加法运算上。当两个节点有一个节点为空时则代表后面没有数字了,我们只需要将剩下的那个链表遍历完即可。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">ListNode</span> <span class="token function">addTwoNumbers</span><span class="token punctuation">(</span><span class="token class-name">ListNode</span> l1<span class="token punctuation">,</span> <span class="token class-name">ListNode</span> l2<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">ListNode</span> head <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ListNode</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ListNode</span> p <span class="token operator">=</span> l1<span class="token punctuation">,</span>q <span class="token operator">=</span> l2<span class="token punctuation">,</span>current <span class="token operator">=</span> head<span class="token punctuation">;</span> <span class="token keyword">int</span> carry <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">while</span><span class="token punctuation">(</span>p<span class="token operator">!=</span><span class="token keyword">null</span><span class="token operator">||</span>q<span class="token operator">!=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> x <span class="token operator">=</span> <span class="token punctuation">(</span>p<span class="token operator">!=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token operator">?</span>p<span class="token punctuation">.</span>val<span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">int</span> y <span class="token operator">=</span> <span class="token punctuation">(</span>q<span class="token operator">!=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token operator">?</span>q<span class="token punctuation">.</span>val<span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">int</span> sum <span class="token operator">=</span> x<span class="token operator">+</span>y<span class="token operator">+</span>carry<span class="token punctuation">;</span> carry <span class="token operator">=</span> sum<span class="token operator">/</span><span class="token number">10</span><span class="token punctuation">;</span> current<span class="token punctuation">.</span>next <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ListNode</span><span class="token punctuation">(</span>sum<span class="token operator">%</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> current <span class="token operator">=</span> current<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>p <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>p <span class="token operator">=</span> p<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>q <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>q <span class="token operator">=</span> q<span class="token punctuation">.</span>next<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>carry<span class="token operator">></span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> current<span class="token punctuation">.</span>next <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ListNode</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> head<span class="token punctuation">.</span>next<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:O(max(len(l1),len(l2))) </p><p>空间复杂度:O(max(len(l1),len(l2))</p><h3 id="思考:"><a href="#思考:" class="headerlink" title="思考:"></a>思考:</h3><p>如果链表存储的顺序反过来怎么办?</p><p>思路:</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
<entry>
<title>MySql数据库学习笔记(一)</title>
<link href="2021/05/01/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%88%E4%B8%80%EF%BC%89/"/>
<url>2021/05/01/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%88%E4%B8%80%EF%BC%89/</url>
<content type="html"><![CDATA[<h1 id="数据库学习笔记(一)"><a href="#数据库学习笔记(一)" class="headerlink" title="数据库学习笔记(一)"></a>数据库学习笔记(一)</h1><h2 id="第一章:在单一表格中检索数据"><a href="#第一章:在单一表格中检索数据" class="headerlink" title="第一章:在单一表格中检索数据"></a>第一章:在单一表格中检索数据</h2><ul><li><strong>USE</strong>:表示选择一个默认的数据库。</li><li><strong>SELECT :</strong> 选择指定表格中的列。可以包含运算符也可以用AS创建新的列,如果列表中要包含空格则用“ ”括起来。用”,”分隔。SELECT*代表选择当前表格所有列</li><li><strong>WHERE :</strong> 表述筛选条件。可以包含运算符“AND、OR、NOT”</li><li><strong>IN:</strong>表示多个条件 IN(‘ ’,‘ ’)。可以与NOT连用</li><li><strong>BETWEEN :</strong> 表示>=a AND <=b。 等价于 BETWEEN a AND b 。</li><li>**LIKE : **筛选字符串。 LIKE‘%a’表示代替n位 LIKE’_b’表示代替一位</li><li><strong>REGEXP :</strong> 正则表达式。 REGEX ‘ abc’ 表示字符串中包含abc的所有结果 “^abc”表示限制abc开头,”abc$”表示限制abc结尾 ,‘a|b|c’表示包含a或b或c,‘[abc]d’表示ad或bd或cd,‘[a-h]e’表示(a-h)+e</li><li><strong>NULL :</strong> WHERE IS NULL 表示条件为空的结果</li><li><strong>ORDER BY :</strong> 表示按条件排序,可以包含多个列。 ORDER BY list1,list2 <strong>DESC</strong>表示降序</li><li><strong>LIMIT:</strong>LIMIT a 表示限制数量,LIMIT a,b 表示从a开始获取b个数据。LIMIT子句要放在最后</li></ul><h2 id="第二章:在多张表格中检索数据"><a href="#第二章:在多张表格中检索数据" class="headerlink" title="第二章:在多张表格中检索数据"></a>第二章:在多张表格中检索数据</h2><h4 id="内连接-INNER-JOIN"><a href="#内连接-INNER-JOIN" class="headerlink" title="内连接 INNER JOIN"></a>内连接 <strong>INNER JOIN</strong></h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="跨数据库连接"><a href="#跨数据库连接" class="headerlink" title="跨数据库连接"></a>跨数据库连接</h4><p>只需要在JOIN连接的时候加上位置的前缀</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> sql2<span class="token punctuation">.</span>table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="自连接"><a href="#自连接" class="headerlink" title="自连接"></a>自连接</h4><p>我们可以将数据库自己的列相连接来显示关系结构图</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table1 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id1 <span class="token operator">=</span> t2<span class="token punctuation">.</span>id2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="多表连接"><a href="#多表连接" class="headerlink" title="多表连接"></a>多表连接</h4><p>我们可以使用多个JOIN-ON来实现多表连接</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span class="token keyword">JOIN</span> table3 te<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t3<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="复合连接条件"><a href="#复合连接条件" class="headerlink" title="复合连接条件"></a>复合连接条件</h4><p>有的时候单列无法作为唯一标识,因此我们用AND标识符在ON后面来实现多个列的连接</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id1 <span class="token operator">=</span> t2<span class="token punctuation">.</span>id1<span class="token operator">AND</span> t1<span class="token punctuation">.</span>id2 <span class="token operator">=</span> t2<span class="token punctuation">.</span>id2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="隐式连接-不建议使用"><a href="#隐式连接-不建议使用" class="headerlink" title="隐式连接(不建议使用)"></a>隐式连接(不建议使用)</h4><p>1中的式子可以替换为:</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">FROM</span> table1 t1<span class="token punctuation">,</span>table2 t2<span class="token keyword">WHERE</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>注意:如果忘记打WHERE 数据会交叉</p><h4 id="外连接-OUTER-JOIN"><a href="#外连接-OUTER-JOIN" class="headerlink" title="外连接 OUTER JOIN"></a>外连接 OUTER JOIN</h4><p>当不仅仅想要对应的数据时我们会使用外连接来列出全部数据</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span><span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token keyword">LEFT</span><span class="token operator">/</span><span class="token keyword">RIGHT</span> <span class="token keyword">JOIN</span> table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><strong>注意:</strong>LEFT JOIN返回t1中的所有数据 ,不管t2中有没有相匹配的数据。</p><p> RIGHT JOIN 返回t2中的所有数据,不管t1中有没有相匹配的数据。</p><h4 id="多表外连接"><a href="#多表外连接" class="headerlink" title="多表外连接"></a>多表外连接</h4><p>如果想展现多表的全部,我们要使用多重LEFT/RIGHT JOIN 。</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span><span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token keyword">LEFT</span><span class="token operator">/</span><span class="token keyword">RIGHT</span> <span class="token keyword">JOIN</span> table2 t2<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span class="token keyword">LEFT</span><span class="token operator">/</span><span class="token keyword">RIGHT</span> <span class="token keyword">JOIN</span> table3 t3<span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t3<span class="token punctuation">.</span>id<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>注意:</strong>为了代码的可读性,在多表外连接时我们尽量只使用LEFT JOIN!</p><h4 id="自外连接"><a href="#自外连接" class="headerlink" title="自外连接"></a>自外连接</h4><p>在自连接的前面加个LEFT即可返回所有的数据。</p><h4 id="USING子句"><a href="#USING子句" class="headerlink" title="USING子句"></a>USING子句</h4><p>如果两个表中有列的名称是完全一样的</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table2 t2<span class="token comment">--ON t1.id = t2.id</span><span class="token keyword">USING</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token keyword">JOIN</span> <span class="token keyword">table</span> t3<span class="token keyword">USING</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token keyword">JOIN</span> table2 t2<span class="token comment">--ON t1.id = t2.id AND t1.name = t2.name</span><span class="token keyword">USING</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span>name<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>注意:</strong>USING关键字只能在不同表中列名完全一样的情况下使用</p><h4 id="自然连接"><a href="#自然连接" class="headerlink" title="自然连接"></a>自然连接</h4><p>自动连接两个数据库相匹配的列</p><pre class="line-numbers language-none"><code class="language-none">SELECT *FROM table1 t1NATURAL JOIN table2 t2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="交叉连接"><a href="#交叉连接" class="headerlink" title="交叉连接"></a>交叉连接</h4><p>在一个型号表中,如果我们需要将所有的型号和所有的颜色组合,我们可以使用交叉连接</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token keyword">CROSS</span> <span class="token keyword">JOIN</span> table2 t2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> table1 t1<span class="token punctuation">,</span>table2 t2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="联合"><a href="#联合" class="headerlink" title="联合"></a>联合</h4><p>UNION关键字可以合并多段查询的记录</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">USE</span> sql_store<span class="token punctuation">;</span><span class="token keyword">SELECT</span> customer_id<span class="token punctuation">,</span>first_name<span class="token punctuation">,</span>points<span class="token punctuation">,</span> <span class="token string">'Bronze'</span> <span class="token keyword">AS</span> <span class="token keyword">type</span><span class="token keyword">FROM</span> customers<span class="token keyword">WHERE</span> points<span class="token operator"><</span><span class="token number">2000</span><span class="token keyword">UNION</span><span class="token keyword">SELECT</span> customer_id<span class="token punctuation">,</span>first_name<span class="token punctuation">,</span>points<span class="token punctuation">,</span> <span class="token string">'Siliver'</span> <span class="token keyword">AS</span> <span class="token keyword">type</span><span class="token keyword">FROM</span> customers<span class="token keyword">WHERE</span> points <span class="token operator">BETWEEN</span> <span class="token number">2000</span> <span class="token operator">AND</span> <span class="token number">3000</span><span class="token keyword">UNION</span><span class="token keyword">SELECT</span> customer_id<span class="token punctuation">,</span>first_name<span class="token punctuation">,</span>points<span class="token punctuation">,</span> <span class="token string">'GOLD'</span> <span class="token keyword">AS</span> <span class="token keyword">type</span><span class="token keyword">FROM</span> customers<span class="token keyword">WHERE</span> points<span class="token operator">></span><span class="token number">3000</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="第三章:插入、更新和删除数据"><a href="#第三章:插入、更新和删除数据" class="headerlink" title="第三章:插入、更新和删除数据"></a>第三章:插入、更新和删除数据</h2><h4 id="列属性"><a href="#列属性" class="headerlink" title="列属性"></a>列属性</h4><p>PK(Primary Key):主键 </p><p>NN (Not Null):非空</p><p>AI (AUTO_INCREMENT):自动递增,通常用在主键列</p><p>Default:每列的默认值</p><h4 id="插入单行-INSERT-INTO"><a href="#插入单行-INSERT-INTO" class="headerlink" title="插入单行(INSERT INTO)"></a>插入单行(INSERT INTO)</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> customers<span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token keyword">Default</span><span class="token punctuation">,</span><span class="token string">'John'</span><span class="token punctuation">,</span><span class="token string">'Smith'</span><span class="token punctuation">,</span><span class="token string">'123-234-567'</span><span class="token punctuation">)</span><span class="token comment">--或者</span><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> customers<span class="token punctuation">(</span>first_name<span class="token punctuation">,</span>last_name<span class="token punctuation">,</span>telephone<span class="token punctuation">)</span><span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token string">'John'</span><span class="token punctuation">,</span><span class="token string">'Smith'</span><span class="token punctuation">,</span><span class="token string">'123-456-567'</span><span class="token punctuation">)</span>其余的都为默认<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="插入多行"><a href="#插入多行" class="headerlink" title="插入多行"></a>插入多行</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> shippers<span class="token punctuation">(</span>name<span class="token punctuation">,</span>sex<span class="token punctuation">)</span><span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'aaa'</span><span class="token punctuation">,</span><span class="token string">'male'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token string">'bbb'</span><span class="token punctuation">,</span><span class="token string">'female'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token string">'ccc'</span><span class="token punctuation">,</span><span class="token string">'male'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="插入分层行"><a href="#插入分层行" class="headerlink" title="插入分层行"></a>插入分层行</h4><p>即往多表插入数据</p><p>LAST_INSERT_ID()为mysql内置方法,返回上一次插入的id</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> orders(customer_id<span class="token punctuation">,</span>order_date<span class="token punctuation">,</span><span class="token keyword">status</span>)<span class="token keyword">VALUE</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">'2019-01-02'</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> order_items<span class="token keyword">VALUES</span><span class="token punctuation">(</span>LAST_INSERT_ID<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2.95</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span>LAST_INSERT_ID<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">3.95</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="创建表复制"><a href="#创建表复制" class="headerlink" title="创建表复制"></a>创建表复制</h4><p>①全复制</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> orders_archived <span class="token keyword">AS</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> orders<span class="token comment">--注意:插入时mysql会忽略主键以及递增</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>②部分复制</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--创建一个空表orders_archived</span><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> orders_archived<span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> orders<span class="token keyword">WHERE</span> order_date <span class="token operator"><</span> <span class="token string">'2019-01-01'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--例子</span><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> invoices_archived <span class="token keyword">AS</span><span class="token keyword">SELECT</span> i<span class="token punctuation">.</span>invoice_id<span class="token punctuation">,</span> i<span class="token punctuation">.</span>number<span class="token punctuation">,</span> c<span class="token punctuation">.</span>name <span class="token keyword">AS</span> client<span class="token punctuation">,</span> i<span class="token punctuation">.</span>invoice_total<span class="token punctuation">,</span> i<span class="token punctuation">.</span>payment_total<span class="token punctuation">,</span> i<span class="token punctuation">.</span>invoice_date<span class="token punctuation">,</span> i<span class="token punctuation">.</span>payment_date<span class="token punctuation">,</span> i<span class="token punctuation">.</span>due_date<span class="token keyword">FROM</span> invoices i<span class="token keyword">JOIN</span> clients c<span class="token keyword">USING</span><span class="token punctuation">(</span>client_id<span class="token punctuation">)</span><span class="token keyword">WHERE</span> payment_date <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="更新单行"><a href="#更新单行" class="headerlink" title="更新单行"></a>更新单行</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">UPDATE</span> invoices<span class="token keyword">SET</span> payment_total<span class="token operator">=</span><span class="token number">10</span><span class="token keyword">WHERE</span> invoice_id <span class="token operator">=</span> <span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="更新多行"><a href="#更新多行" class="headerlink" title="更新多行"></a>更新多行</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">UPDATE</span> invoices<span class="token keyword">SET</span> payment_total<span class="token operator">=</span><span class="token number">10</span><span class="token keyword">WHERE</span> invoice_id <span class="token operator">IN</span> <span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token comment">--所有WHERE语句都适用</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="在Updates中用子查询"><a href="#在Updates中用子查询" class="headerlink" title="在Updates中用子查询"></a>在Updates中用子查询</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">UPDATE</span> invoices<span class="token keyword">SET</span> payment_total<span class="token operator">=</span><span class="token number">10</span><span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> client_id <span class="token keyword">FROM</span> clients <span class="token keyword">WHERE</span> name <span class="token operator">=</span> <span class="token string">'Myworks'</span><span class="token punctuation">)</span><span class="token comment">--MySql中用()复合句来表示条件,含多个条件时我们用‘IN’来代替‘=’</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="删除行"><a href="#删除行" class="headerlink" title="删除行"></a>删除行</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">DELETE</span> <span class="token keyword">FROM</span> invoices<span class="token comment">//单独一个话删除整个表</span><span class="token keyword">WHERE</span> invoice_id <span class="token operator">=</span><span class="token punctuation">(</span> <span class="token keyword">SELECT</span><span class="token operator">*</span> <span class="token keyword">FROM</span> clients <span class="token keyword">WHERE</span> name <span class="token operator">=</span> <span class="token string">'Myworks'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="恢复数据库"><a href="#恢复数据库" class="headerlink" title="恢复数据库"></a>恢复数据库</h4><p>File-Open SQL script-打开-excute-refresh</p><h2 id="第四章:汇总数据"><a href="#第四章:汇总数据" class="headerlink" title="第四章:汇总数据"></a>第四章:汇总数据</h2><h4 id="聚合函数"><a href="#聚合函数" class="headerlink" title="聚合函数"></a>聚合函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--MAX()</span><span class="token comment">--MIN()</span><span class="token comment">--AVG()</span><span class="token comment">--SUM()</span><span class="token comment">--COUNT()</span><span class="token keyword">SELECT</span> <span class="token function">MAX</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token function">MIN</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token function">AVG</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token function">COUNT</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token keyword">FROM</span> invoices<span class="token keyword">WHERE</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token comment">--COUNT(invoice_total)计算的是这个列里面非空的元素,</span><span class="token comment">--COUNT(*)计算的列的总数</span><span class="token comment">--DISTINCT关键字排除了列中重复的元素</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="GROUP-BY-子句"><a href="#GROUP-BY-子句" class="headerlink" title="GROUP BY 子句"></a>GROUP BY 子句</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--单列分组</span><span class="token keyword">SELECT</span>client_id<span class="token punctuation">,</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sales<span class="token keyword">FROM</span> invoices<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> client_id<span class="token comment">--计算不同client_id总的销售额</span><span class="token comment">--GROUP BY 放在ORDER BY 前面</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--多列分组</span><span class="token keyword">SELECT</span>state<span class="token punctuation">,</span>city<span class="token punctuation">,</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sales<span class="token keyword">FROM</span> invoices i<span class="token keyword">JOIN</span> clients <span class="token keyword">USING</span> <span class="token punctuation">(</span>client_id<span class="token punctuation">)</span><span class="token keyword">GROUP</span> <span class="token keyword">BY</span> state<span class="token punctuation">,</span>city<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="HAVING子句"><a href="#HAVING子句" class="headerlink" title="HAVING子句"></a>HAVING子句</h4><p>用于分组后筛选数据,与WHERE的用法基本一致。</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span>client_id<span class="token punctuation">,</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sales<span class="token keyword">FROM</span> invoices<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> client_id<span class="token keyword">HAVING</span> total_sales <span class="token operator">></span><span class="token number">500</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>WHERE 与 HAVING 的区别:</strong>前者用于分组行前筛选,后者用于分组后</p><h4 id="ROLLUP运算符(只适用MySql)"><a href="#ROLLUP运算符(只适用MySql)" class="headerlink" title="ROLLUP运算符(只适用MySql)"></a>ROLLUP运算符(只适用MySql)</h4><p>我们再GROUP BY 后面加入WITH ROLLUP 来得到汇总的结果集</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span>client_id<span class="token punctuation">,</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sales<span class="token keyword">FROM</span> invoices<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> client_id <span class="token keyword">WITH ROLLUP</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span>state<span class="token punctuation">,</span>city<span class="token punctuation">,</span><span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sales<span class="token keyword">FROM</span> invoices<span class="token keyword">JOIN</span> clients <span class="token keyword">USING</span><span class="token punctuation">(</span>client_id<span class="token punctuation">)</span><span class="token keyword">GROUP</span> <span class="token keyword">BY</span> state<span class="token punctuation">,</span>city <span class="token keyword">WITH ROLLUP</span><span class="token comment">--多列分组运用rollup运算符时会得到每个组及整个结果集的汇总值</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="第六章:编写复杂查询"><a href="#第六章:编写复杂查询" class="headerlink" title="第六章:编写复杂查询"></a>第六章:编写复杂查询</h2><h4 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--例1:</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> products<span class="token keyword">WHERE</span> unit_price<span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> unit_price <span class="token keyword">FROM</span> products <span class="token keyword">WHERE</span> product_id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token comment">--例2:</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> employees<span class="token keyword">WHERE</span> salary<span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">AVG</span><span class="token punctuation">(</span>salary<span class="token punctuation">)</span> <span class="token keyword">FROM</span> employees<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="IN-运算符"><a href="#IN-运算符" class="headerlink" title="IN 运算符"></a>IN 运算符</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> products<span class="token keyword">WHERE</span> product_id <span class="token operator">NOT</span> <span class="token operator">IN</span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token keyword">DISTINCT</span> product_id<span class="token keyword">FROM</span> order_items <span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="子查询VS连接"><a href="#子查询VS连接" class="headerlink" title="子查询VS连接"></a>子查询VS连接</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--子查询获得购买产品id为3的顾客信息</span><span class="token keyword">SELECT</span> <span class="token keyword">DISTINCT</span> c<span class="token punctuation">.</span>customer_id<span class="token punctuation">,</span>c<span class="token punctuation">.</span>first_name<span class="token punctuation">,</span>c<span class="token punctuation">.</span>last_name<span class="token keyword">FROM</span> orders<span class="token keyword">JOIN</span> customers c <span class="token keyword">USING</span><span class="token punctuation">(</span>customer_id<span class="token punctuation">)</span><span class="token keyword">WHERE</span> order_id <span class="token operator">IN</span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> order_id<span class="token keyword">FROM</span> order_items<span class="token keyword">WHERE</span> product_id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token comment">--连接查询获得购买产品id为3的顾客信息</span><span class="token keyword">SELECT</span> <span class="token keyword">DISTINCT</span> customer_id<span class="token punctuation">,</span>first_name<span class="token punctuation">,</span>last_name<span class="token keyword">FROM</span> customers c<span class="token keyword">JOIN</span> orders o <span class="token keyword">USING</span><span class="token punctuation">(</span>customer_id<span class="token punctuation">)</span><span class="token keyword">JOIN</span> order_items oi <span class="token keyword">USING</span> <span class="token punctuation">(</span>order_id<span class="token punctuation">)</span><span class="token keyword">WHERE</span> oi<span class="token punctuation">.</span>product_id <span class="token operator">=</span> <span class="token number">3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="ALL关键字"><a href="#ALL关键字" class="headerlink" title="ALL关键字"></a>ALL关键字</h4><p><code>ALL</code>运算符是一个逻辑运算符,它将单个值与子查询返回的单列值集进行比较。</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--获得大于顾客三发票的所有发票</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> invoices<span class="token keyword">WHERE</span> invoice_total<span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">MAX</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token keyword">FROM</span> invoices<span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token comment">--等价于</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> invoices<span class="token keyword">WHERE</span> invoice_total<span class="token operator">></span> <span class="token keyword">ALL</span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> invoice_total<span class="token keyword">FROM</span> invoices<span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><table><thead><tr><th>条件</th><th>描述</th></tr></thead><tbody><tr><td><code>c > ALL(…)</code></td><td><code>c</code>列中的值必须大于要评估为<code>true</code>的集合中的最大值。</td></tr><tr><td>c >= ALL(…)</td><td><code>c</code>列中的值必须大于或等于要评估为<code>true</code>的集合中的最大值。</td></tr><tr><td>c < ALL(…)</td><td><code>c</code>列中的值必须小于要评估为<code>true</code>的集合中的最小值。</td></tr><tr><td>c <= ALL(…)</td><td><code>c</code>列中的值必须小于或等于要评估为<code>true</code>的集合中的最小值。</td></tr><tr><td>c <> ALL(…)</td><td><code>c</code>列中的值不得等于要评估为<code>true</code>的集合中的任何值。</td></tr><tr><td>c = ALL(…)</td><td><code>c</code>列中的值必须等于要评估为<code>true</code>的集合中的任何值。</td></tr></tbody></table><h4 id="ANY关键字"><a href="#ANY关键字" class="headerlink" title="ANY关键字"></a>ANY关键字</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--选择至少有两张发票的客户</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> clients<span class="token keyword">WHERE</span> client_id <span class="token operator">IN</span> <span class="token punctuation">(</span> <span class="token keyword">SELECT</span> client_id<span class="token punctuation">,</span><span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> number<span class="token keyword">FROM</span> invoices<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> client_id<span class="token keyword">HAVING</span> number <span class="token operator">>=</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> clients<span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> <span class="token keyword">ANY</span> <span class="token punctuation">(</span> <span class="token keyword">SELECT</span> client_id<span class="token punctuation">,</span><span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> number<span class="token keyword">FROM</span> invoices<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> client_id<span class="token keyword">HAVING</span> number <span class="token operator">>=</span><span class="token number">2</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>注意:=ANY 等价于IN。</p><h4 id="相关子查询"><a href="#相关子查询" class="headerlink" title="相关子查询"></a>相关子查询</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--在所有员工中,计算他们办公室的平均工资并返回工资大于平均工资的员工</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> employees e<span class="token keyword">WHERE</span> salary <span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">AVG</span><span class="token punctuation">(</span>salary<span class="token punctuation">)</span> <span class="token keyword">FROM</span> employees <span class="token keyword">WHERE</span> office_id <span class="token operator">=</span> e<span class="token punctuation">.</span>office_id<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>相关子查询与非相关子查询的区别:</p><p><strong>非相关子查询</strong>的执行不依赖与外部的查询。<br>执行过程:<br>(1)执行子查询,其结果不被显示,而是传递给外部查询,作为外部查询的条件使用。<br>(2)执行外部查询,并显示整个结果。 <br>非相关子查询一般可以分为:返回单值的子查询和返回一个列表的子查询。</p><p><strong>相关子查询</strong>的执行依赖于外部查询。多数情况下是子查询的WHERE子句中引用了外部查询的表。<br>执行过程:<br>(1)从外层查询中取出一个元组,将元组相关列的值传给内层查询。<br>(2)执行内层查询,得到子查询操作的值。<br>(3)外查询根据子查询返回的结果或结果集得到满足条件的行。<br>(4)然后外层查询取出下一个元组重复做步骤1-3,直到外层的元组全部处理完毕。 </p><p><strong>总结:</strong></p><p>非相关子查询是独立于外部查询的子查询,子查询总共执行一次,执行完毕后将值传递给外部查询。</p><p>相关子查询的执行依赖于外部查询的数据,外部查询执行一行,子查询就执行一次。<br>故非相关子查询比相关子查询效率高。</p><h4 id="EXISTS运算符"><a href="#EXISTS运算符" class="headerlink" title="EXISTS运算符"></a>EXISTS运算符</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--选择有发票的客户</span><span class="token comment">--方法1:</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> clients C<span class="token keyword">WHERE</span> client_id <span class="token operator">IN</span><span class="token punctuation">(</span> <span class="token keyword">SELECT</span> <span class="token keyword">DISTINCT</span> client_id <span class="token keyword">FROM</span> invoices<span class="token punctuation">)</span><span class="token comment">--方法2:</span><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span> clients C<span class="token keyword">WHERE</span> <span class="token keyword">EXISTS</span><span class="token punctuation">(</span> <span class="token keyword">SELECT</span> client_id <span class="token keyword">FROM</span> invoices <span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> c<span class="token punctuation">.</span>client_id<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>EXISTS 没有返回的结果只返回TRUE/FALSE,而IN有返回的列表,故当返回的数据较大时,IN的效率没有EXISTS高。</p><h4 id="SELECT子句中的子查询"><a href="#SELECT子句中的子查询" class="headerlink" title="SELECT子句中的子查询"></a>SELECT子句中的子查询</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> invoice_id<span class="token punctuation">,</span> invoice_total<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">AVG</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token keyword">FROM</span> invoices<span class="token punctuation">)</span> <span class="token keyword">AS</span> invoice_average<span class="token punctuation">,</span>invoice_total<span class="token operator">-</span>invoice_average<span class="token keyword">FROM</span> invoices<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="FROM子句中的子查询"><a href="#FROM子句中的子查询" class="headerlink" title="FROM子句中的子查询"></a>FROM子句中的子查询</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><span class="token keyword">FROM</span><span class="token punctuation">(</span> <span class="token keyword">SELECT</span>client_id<span class="token punctuation">,</span>name<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">SUM</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span> <span class="token keyword">FROM</span> invoices <span class="token keyword">WHERE</span> client_id <span class="token operator">=</span> c<span class="token punctuation">.</span>client_id<span class="token punctuation">)</span> <span class="token keyword">AS</span> total_sale<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token function">AVG</span><span class="token punctuation">(</span>invoice_total<span class="token punctuation">)</span><span class="token keyword">FROM</span> invoices<span class="token punctuation">)</span> <span class="token keyword">AS</span> average<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> total_sale <span class="token operator">-</span>average<span class="token punctuation">)</span> <span class="token keyword">AS</span> differences<span class="token keyword">FROM</span> clients c <span class="token punctuation">)</span><span class="token keyword">AS</span> sales_summary<span class="token keyword">WHERE</span> total_sales <span class="token operator">IS</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="第七章:MySql基本函数"><a href="#第七章:MySql基本函数" class="headerlink" title="第七章:MySql基本函数"></a>第七章:MySql基本函数</h2><h4 id="数值函数"><a href="#数值函数" class="headerlink" title="数值函数"></a>数值函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token function">ROUND</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span>b<span class="token punctuation">)</span> <span class="token comment">--四舍五入小数a,保留b位</span>CEILING<span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token comment">--返回大于或等于c的最小整数</span>FLOOR<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token comment">--返回小于或等于d的最大整数</span>ABS<span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token comment">--计算e的绝对值</span>RAND<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">--返回0-1直接的随机浮点数</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql">LENGTH(<span class="token string">''</span>) <span class="token comment">--返回字符串的长度</span>UPPER<span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">--返回字符串的大写形式</span>LOWER<span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">--返回字符串的小写形式</span>LTRIM<span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">--移除字符串左侧的空白字符或其他预定义字符</span>RTRIM<span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">--移除字符串右侧的空白字符或其他预定义字符</span>TRIM<span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">--删除所有前导或者尾随空格</span><span class="token keyword">LEFT</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">,</span>a<span class="token punctuation">)</span> <span class="token comment">--返回字符串从左数a位</span><span class="token keyword">LEFT</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">,</span>a,b<span class="token punctuation">)</span> <span class="token comment">--返回字符串从第a位开始数b位</span><span class="token keyword">RIGHT</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">,</span>a<span class="token punctuation">)</span> <span class="token comment">--返回字符串从右数a位</span>LOCATE<span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">)</span><span class="token comment">--从1开始,返回字符b中第一个出现a的位置,没有则返回0</span><span class="token keyword">REPLACE</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">,</span><span class="token string">'c'</span><span class="token punctuation">)</span><span class="token comment">--将字符串a中的b元素换成c元素</span>CONCAT<span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">)</span> <span class="token comment">--连接a,b两个字符串</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token function">NOW</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span>CURDATE<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span>CURTIME<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">--返回当前日期+时间/当前日期/当前时间</span><span class="token keyword">YEAR</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token keyword">MONTH</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token keyword">DAY</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token keyword">MINUTE</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">--返回时间的年份/月份...</span>YEARNAME<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span>MONTHNAME<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">--返回年/月...的字符串形式</span>EXTRACT<span class="token punctuation">(</span>timea <span class="token keyword">FROM</span> timeb<span class="token punctuation">)</span> <span class="token comment">--返回从时间b中提取时间a</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="格式化日期和时间"><a href="#格式化日期和时间" class="headerlink" title="格式化日期和时间"></a>格式化日期和时间</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql">DATE_FORMAT<span class="token punctuation">(</span><span class="token keyword">time</span><span class="token punctuation">,</span><span class="token string">'%a%b'</span><span class="token punctuation">)</span> <span class="token comment">--格式化时间%a缩写星期名</span>TIME_FORMAT<span class="token punctuation">(</span><span class="token keyword">time</span><span class="token punctuation">,</span><span class="token string">'%a%b'</span><span class="token punctuation">)</span><span class="token operator">%</span>b缩写月名<span class="token operator">%</span>c月,数值<span class="token operator">%</span>D带有英文前缀的月中的天<span class="token operator">%</span>d月的天,数值<span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">31</span><span class="token punctuation">)</span><span class="token operator">%</span>e月的天,数值<span class="token punctuation">(</span><span class="token number">0</span><span class="token operator">-</span><span class="token number">31</span><span class="token punctuation">)</span><span class="token operator">%</span>f微秒<span class="token operator">%</span>H小时 <span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">23</span><span class="token punctuation">)</span><span class="token operator">%</span>h小时 <span class="token punctuation">(</span><span class="token number">01</span><span class="token operator">-</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token operator">%</span>I小时 <span class="token punctuation">(</span><span class="token number">01</span><span class="token operator">-</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token operator">%</span>i分钟,数值<span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">59</span><span class="token punctuation">)</span><span class="token operator">%</span>j年的天 <span class="token punctuation">(</span><span class="token number">001</span><span class="token operator">-</span><span class="token number">366</span><span class="token punctuation">)</span><span class="token operator">%</span>k小时 <span class="token punctuation">(</span><span class="token number">0</span><span class="token operator">-</span><span class="token number">23</span><span class="token punctuation">)</span><span class="token operator">%</span>l小时 <span class="token punctuation">(</span><span class="token number">1</span><span class="token operator">-</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token operator">%</span>M月名<span class="token operator">%</span>m月,数值<span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token operator">%</span>pAM 或 PM<span class="token operator">%</span>r时间,<span class="token number">12</span><span class="token operator">-</span>小时(hh:mm:ss AM 或 PM)<span class="token operator">%</span>S秒<span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">59</span><span class="token punctuation">)</span><span class="token operator">%</span>s秒<span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">59</span><span class="token punctuation">)</span><span class="token operator">%</span>T时间<span class="token punctuation">,</span> <span class="token number">24</span><span class="token operator">-</span>小时 <span class="token punctuation">(</span>hh:mm:ss<span class="token punctuation">)</span><span class="token operator">%</span>U周 <span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">53</span><span class="token punctuation">)</span> 星期日是一周的第一天<span class="token operator">%</span>u周 <span class="token punctuation">(</span><span class="token number">00</span><span class="token operator">-</span><span class="token number">53</span><span class="token punctuation">)</span> 星期一是一周的第一天<span class="token operator">%</span>V周 <span class="token punctuation">(</span><span class="token number">01</span><span class="token operator">-</span><span class="token number">53</span><span class="token punctuation">)</span> 星期日是一周的第一天,与 <span class="token operator">%</span>X 使用<span class="token operator">%</span>v周 <span class="token punctuation">(</span><span class="token number">01</span><span class="token operator">-</span><span class="token number">53</span><span class="token punctuation">)</span> 星期一是一周的第一天,与 <span class="token operator">%</span>x 使用<span class="token operator">%</span>W星期名<span class="token operator">%</span>w周的天 (<span class="token number">0</span><span class="token operator">=</span>星期日<span class="token punctuation">,</span> <span class="token number">6</span><span class="token operator">=</span>星期六)<span class="token operator">%</span>X年,其中的星期日是周的第一天,<span class="token number">4</span> 位,与 <span class="token operator">%</span>V 使用<span class="token operator">%</span>x年,其中的星期一是周的第一天,<span class="token number">4</span> 位,与 <span class="token operator">%</span>v 使用<span class="token operator">%</span>Y年,<span class="token number">4</span> 位<span class="token operator">%</span>y年,<span class="token number">2</span> 位<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="计算时间和日期"><a href="#计算时间和日期" class="headerlink" title="计算时间和日期"></a>计算时间和日期</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql">DATE_ADD<span class="token punctuation">(</span><span class="token keyword">time</span><span class="token punctuation">,</span><span class="token keyword">INTERVAL</span> <span class="token number">1</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span> <span class="token comment">--增加日期</span>DATE_SUB<span class="token punctuation">(</span><span class="token keyword">time</span><span class="token punctuation">,</span><span class="token keyword">INTERVAL</span> <span class="token number">1</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span> <span class="token comment">--减去时间</span>DATEDIFF<span class="token punctuation">(</span>timea<span class="token punctuation">,</span>timeb<span class="token punctuation">)</span> <span class="token comment">--返回时间a-时间b的天数(存在负值)</span>TIME_TO_SEC<span class="token punctuation">(</span><span class="token keyword">time</span><span class="token punctuation">)</span> <span class="token comment">--返回从0点开始计时的秒数</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="IFNULL函数和COALESCE函数"><a href="#IFNULL函数和COALESCE函数" class="headerlink" title="IFNULL函数和COALESCE函数"></a>IFNULL函数和COALESCE函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--IFNULL函数:当括号中第一个元素为空则替换成第二个元素</span><span class="token keyword">SELECT</span> order_id<span class="token punctuation">,</span>IFNULL<span class="token punctuation">(</span>shipper_id<span class="token punctuation">,</span><span class="token string">'asd'</span><span class="token punctuation">)</span><span class="token keyword">FROM</span> orders<span class="token comment">--COALESC函数:当括号第一个元素为空则替换成第二个,如果第二个为空则替换成第三个</span><span class="token keyword">SELECT</span> order_id<span class="token punctuation">,</span>COALESC<span class="token punctuation">(</span>shipper_id<span class="token punctuation">,</span><span class="token keyword">comment</span><span class="token punctuation">,</span><span class="token string">'...'</span><span class="token punctuation">)</span><span class="token keyword">FROM</span> orders<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="IF函数"><a href="#IF函数" class="headerlink" title="IF函数"></a>IF函数</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--IF(expression,first,second) IF表达式为真返回第一个,为假返回第二个</span><span class="token keyword">SELECT</span>order_id<span class="token punctuation">,</span>order_date<span class="token punctuation">,</span><span class="token keyword">IF</span><span class="token punctuation">(</span><span class="token keyword">YEAR</span><span class="token punctuation">(</span>order_date<span class="token punctuation">)</span><span class="token operator">=</span><span class="token keyword">YEAR</span><span class="token punctuation">(</span><span class="token function">NOW</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">'Active'</span><span class="token punctuation">,</span><span class="token string">'Archived'</span><span class="token punctuation">)</span><span class="token keyword">FROM</span> orders<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="CASE运算符"><a href="#CASE运算符" class="headerlink" title="CASE运算符"></a>CASE运算符</h4><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">--在有多个测试表达式且想要针对每个表达式返回不同值的时候可以使用CASE运算符</span><span class="token keyword">SELECT</span>order_id<span class="token punctuation">,</span><span class="token keyword">CASE</span><span class="token keyword">WHEN</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span>order_date<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span><span class="token function">NOW</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">THEN</span> <span class="token string">'Active'</span><span class="token keyword">WHEN</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span>order_date<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span><span class="token function">NOW</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token number">1</span> <span class="token keyword">THEN</span> <span class="token string">'Last Year'</span><span class="token keyword">WHEN</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span>order_date<span class="token punctuation">)</span> <span class="token operator"><</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span><span class="token function">NOW</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token number">1</span> <span class="token keyword">THEN</span> <span class="token string">'Archived'</span><span class="token keyword">ELSE</span> <span class="token string">'Future'</span><span class="token keyword">END</span> <span class="token keyword">AS</span> category<span class="token keyword">FROM</span> orders<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> MySQL数据库学习 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> MySQL </tag>
</tags>
</entry>
<entry>
<title>LeetCode01--twoSum</title>
<link href="2021/04/30/LeetCode/LeetCode01%20%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C/"/>
<url>2021/04/30/LeetCode/LeetCode01%20%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C/</url>
<content type="html"><![CDATA[<h1 id="题目01:两数之和(简单)"><a href="#题目01:两数之和(简单)" class="headerlink" title="题目01:两数之和(简单)"></a>题目01:两数之和(简单)</h1><h3 id="题目描述:"><a href="#题目描述:" class="headerlink" title="题目描述:"></a>题目描述:</h3><p>给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。</p><p>你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。</p><p>你可以按任意顺序返回答案。</p><p>示例 1:</p><pre class="line-numbers language-none"><code class="language-none">输入:nums = [2,7,11,15], target = 9输出:[0,1]解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>示例 2:</p><pre class="line-numbers language-none"><code class="language-none">输入:nums = [3,2,4], target = 6输出:[1,2]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>示例 3:</p><pre class="line-numbers language-none"><code class="language-none">输入:nums = [3,3], target = 6输出:[0,1]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="解法一(循环暴力解):"><a href="#解法一(循环暴力解):" class="headerlink" title="解法一(循环暴力解):"></a>解法一(循环暴力解):</h3><p>思路:双重循环遍历出所有情况,输出符合条件的情况</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">twoSum</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> nums<span class="token punctuation">,</span> <span class="token keyword">int</span> target<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> ans<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>nums<span class="token punctuation">.</span>length <span class="token operator"><</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">;</span> i <span class="token operator"><</span> nums<span class="token punctuation">.</span>length <span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> i <span class="token operator">+</span> <span class="token number">1</span> <span class="token punctuation">;</span> j <span class="token operator"><</span> nums<span class="token punctuation">.</span>length <span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">+</span> nums<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">==</span> target<span class="token punctuation">)</span><span class="token punctuation">{</span> ans<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> i<span class="token punctuation">;</span> ans<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> j<span class="token punctuation">;</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> ans<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:双层for循环为O(n^2)</p><p>空间复杂度:O(1)</p><h3 id="解法二(HashMap优化):"><a href="#解法二(HashMap优化):" class="headerlink" title="解法二(HashMap优化):"></a>解法二(HashMap优化):</h3><p>思路:我们可以将第二个循环换个方式理解如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>j <span class="token operator"><</span> nums<span class="token punctuation">.</span>length<span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> sub <span class="token operator">=</span> target <span class="token operator">-</span> nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>nums<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token operator">==</span> sub<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>我们无非是在第二次遍历中找到sub,时间复杂度为O(n)。</p><p><strong>但是</strong>:如果我们使用hash table,将每个元素保存为hash的key,下标对应hash的value,那么我们只需要判断sub在不在hash里面即可,此时的时间复杂度为O(1).</p><p><strong>此外:</strong>由于题目中提到一个元素只能用一次,我们还得进行一次判断去重。代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">twoSum</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> nums<span class="token punctuation">,</span> <span class="token keyword">int</span> target<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">,</span><span class="token class-name">Integer</span> <span class="token punctuation">></span></span> hash <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>i <span class="token operator"><</span> nums<span class="token punctuation">.</span>length<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> hash<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">;</span> i <span class="token operator"><</span> nums<span class="token punctuation">.</span>length <span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> sub <span class="token operator">=</span> target <span class="token operator">-</span> nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>hash<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>sub<span class="token punctuation">)</span> <span class="token operator">&&</span> hash<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>sub<span class="token punctuation">)</span><span class="token operator">!=</span>i<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>i<span class="token punctuation">,</span>hash<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>sub<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>时间复杂度:O(n)</p><p>空间复杂度:使用了HashMap,复杂度变为O(n)</p><h3 id="解法三(解法二优化):"><a href="#解法三(解法二优化):" class="headerlink" title="解法三(解法二优化):"></a>解法三(解法二优化):</h3><p>是的,没错,我们可以再进行一次优化,我们可以把解法二的两个for循环合并起来,时间复杂度没有发生变化,但是不需要判断是不是当前元素了,因为是后面添加进HashMap的。代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">twoSum</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> nums<span class="token punctuation">,</span> <span class="token keyword">int</span> target<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Integer</span><span class="token punctuation">,</span><span class="token class-name">Integer</span> <span class="token punctuation">></span></span> hash <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">;</span> i <span class="token operator"><</span> nums<span class="token punctuation">.</span>length <span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">int</span> sub <span class="token operator">=</span> target <span class="token operator">-</span> nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>hash<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span>sub<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span>i<span class="token punctuation">,</span>hash<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>sub<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> hash<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>nums<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h3><p>今天是第一天,万事开头难!之前都是用python刷的的力扣,对于Java语法的掌握不是很理想,很多方法都不会表达,我相信一切都会好起来的,越努力越幸运,加油!!!</p>]]></content>
<categories>
<category> LeetCode算法 </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 力扣算法 </tag>
</tags>
</entry>
</search>