-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
2690 lines (2444 loc) · 380 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
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Git使用子模块</title>
<url>/posts/2367184701/</url>
<content><![CDATA[<h3 id="我"><a href="#我" class="headerlink" title="我"></a>我</h3><p>git如何创建子模块关联远程仓库,然后进行拉取和提交</p>
<h3 id="ChatGPT"><a href="#ChatGPT" class="headerlink" title="ChatGPT"></a>ChatGPT</h3><ol>
<li>创建子模块并关联远程仓库</li>
</ol>
<p>在父仓库中执行以下命令:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule add <远程仓库地址> <子模块目录></span><br></pre></td></tr></table></figure>
<p>例如:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule add https://github.com/user/repo.git submodules/repo</span><br></pre></td></tr></table></figure>
<p>这将在父仓库中创建一个子模块,并将其关联到远程仓库的地址。</p>
<ol start="2">
<li>拉取子模块</li>
</ol>
<p>在父仓库中执行以下命令:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure>
<p>这将拉取子模块的代码,并将其更新到父仓库中。</p>
<ol start="3">
<li>提交子模块</li>
</ol>
<p>在父仓库中进行修改后,需要将子模块的修改提交到子模块的仓库中。可以进入子模块目录,使用普通的git命令进行提交。</p>
<p>例如:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd submodules/repo</span><br><span class="line">git add .</span><br><span class="line">git commit -m "修改了子模块"</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<p>完成子模块的提交后,返回到父仓库,使用普通的git命令提交父仓库的修改即可。</p>
<p>例如:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd ..</span><br><span class="line">git add .</span><br><span class="line">git commit -m "修改了父仓库和子模块"</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<h3 id="我-1"><a href="#我-1" class="headerlink" title="我"></a>我</h3><p>可以直接进入子模块目录进行pull吗</p>
<h3 id="ChatGPT-1"><a href="#ChatGPT-1" class="headerlink" title="ChatGPT"></a>ChatGPT</h3><p>是的,可以进入子模块目录进行 pull 操作。但是需要注意的是,在父仓库中执行 <code>git submodule update</code> 命令时,可能会出现子模块版本不一致的情况,因为子模块的版本是被固定在父仓库中的。所以在进行 pull 操作之前,最好先进入父仓库中,执行 <code>git submodule update</code> 命令,确保子模块版本和父仓库中的版本一致,再进入子模块目录进行 pull 操作。</p>
<h3 id="我-2"><a href="#我-2" class="headerlink" title="我"></a>我</h3><p>完整的流程</p>
<h3 id="ChatGPT-2"><a href="#ChatGPT-2" class="headerlink" title="ChatGPT"></a>ChatGPT</h3><p>下面是使用子模块的完整流程:</p>
<ol>
<li>在父仓库中添加子模块并关联远程仓库:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule add <远程仓库地址> <子模块目录></span><br></pre></td></tr></table></figure>
<p>例如:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule add https://github.com/user/repo.git submodules/repo</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>提交父仓库的修改:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m "添加子模块"</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>拉取子模块:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>进入子模块目录并拉取子模块的代码:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd submodules/repo</span><br><span class="line">git pull origin master</span><br></pre></td></tr></table></figure>
<ol start="5">
<li>提交子模块的修改:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m "修改子模块"</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<ol start="6">
<li>返回父仓库,提交父仓库和子模块的修改:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd ..</span><br><span class="line">git add .</span><br><span class="line">git commit -m "修改父仓库和子模块"</span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<p>这样就完成了使用子模块的流程。在以后的开发中,如果需要更新子模块的代码,只需要进入子模块目录,执行 <code>git pull</code> 命令即可。</p>
]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>git</tag>
<tag>chatgpt</tag>
</tags>
</entry>
<entry>
<title>Git删除远程已提交文件,保留本地副本</title>
<url>/posts/1579996637/</url>
<content><![CDATA[<h1 id="不想推送文件"><a href="#不想推送文件" class="headerlink" title="不想推送文件"></a>不想推送文件</h1><h2 id="来自你的消息"><a href="#来自你的消息" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git commit的文件不想push到远程库,怎么解决</p>
<h2 id="来自-ChatGPT-的消息"><a href="#来自-ChatGPT-的消息" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>可以使用git的忽略文件机制来解决这个问题。</p>
<ol>
<li>在仓库根目录下创建一个名为.gitignore的文件。</li>
<li>在该文件中列出您想忽略的文件和/或文件夹的名称。例如,如果您想忽略文件“temp.txt”,则可以这样编写:</li>
</ol>
<p>temp.txt</p>
<p>如果您想忽略整个文件夹“logs”,则可以这样编写:</p>
<p>logs/</p>
<ol start="3">
<li>执行git add和git commit命令以将.gitignore文件添加到本地仓库中。</li>
<li>提交更改后,git将不会跟踪列在.gitignore文件中的文件和文件夹。</li>
</ol>
<p>请注意,如果.gitignore文件中列出的文件/文件夹已经被git跟踪,那么您需要使用git rm –cached命令将它们从git跟踪列表中删除,然后再添加到.gitignore文件中。</p>
<h2 id="来自你的消息-1"><a href="#来自你的消息-1" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git rm –cached 怎么删除指定的文件</p>
<h2 id="来自-ChatGPT-的消息-1"><a href="#来自-ChatGPT-的消息-1" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>可以使用以下命令删除指定文件:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">rm</span> --cached <filename></span><br></pre></td></tr></table></figure>
<p>其中,<code><filename></code> 指定要从Git缓存中删除的文件名。</p>
<p>注意,使用 <code>--cached</code> 参数只会从缓存中删除文件,而不会从工作目录中删除文件。如果您想同时从缓存和工作目录中删除一个文件,需要去掉 <code>--cached</code> 参数,并在执行 <code>git rm</code> 命令后再执行 <code>rm</code> 命令,例如:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">rm</span> <filename></span><br><span class="line"><span class="built_in">rm</span> <filename></span><br></pre></td></tr></table></figure>
<p>这将从缓存和工作目录中删除指定的文件。</p>
<p>在执行 <code>git rm</code> 命令后还需要执行 <code>git commit</code> 命令提交更改,以使指定文件从仓库中删除。</p>
<h2 id="来自你的消息-2"><a href="#来自你的消息-2" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git push 的时候提示push rejected</p>
<h2 id="来自-ChatGPT-的消息-2"><a href="#来自-ChatGPT-的消息-2" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当执行 <code>git push</code> 命令时,如果远程库拒绝了push操作,通常会出现 “push rejected” 的提示信息。 这种情况通常有以下几种原因:</p>
<ol>
<li>您没有权限向该仓库推送代码。这种情况可能是因为您没有通过认证或您没有被授权访问该仓库。您可以联系仓库管理员来解决这个问题。</li>
<li>您试图推送到一个保护分支,但没有被授权。 对于受保护的分支,只有授权用户才能推送更改。您可以查看仓库的分支保护规则以了解更多信息。</li>
<li>远程仓库的更新使您的推送不再是快进合并。 这种情况下,你需要先将远程库的更新pull到本地仓库,然后再push您的更改。 您可以使用 <code>git pull</code> 命令或 <code>git pull --rebase</code> 命令拉取远程库的更改,然后再执行您的推送操作。</li>
<li>您正在尝试推送一个空的分支。 Git默认情况下不会推送空分支。 请确保您的本地分支中有更改并且已经提交。 或者,您可以使用 <code>git push --force</code> 命令强制推送一个空分支。</li>
<li>远程分支已被删除。 在此情况下,您需要使用 <code>git fetch --prune</code> 命令清理您的本地分支,然后重新拉取新的分支。</li>
</ol>
<p>以上是常见的几种 push rejected 的原因。您可以根据具体情况进行调整。</p>
<h2 id="来自你的消息-3"><a href="#来自你的消息-3" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git pull 提示app/xiaoke/utils/XiaokeTest.java</p>
<h2 id="来自-ChatGPT-的消息-3"><a href="#来自-ChatGPT-的消息-3" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当执行 <code>git pull</code> 命令时,如果提示某个文件被修改,这通常是由于您的本地分支和远程分支之间存在冲突而引起的。</p>
<p>如果提示的文件是 <code>app/xiaoke/utils/XiaokeTest.java</code>,则说明该文件在远程分支上发生了变化,并且和您当前的本地分支发生了冲突。</p>
<p>在这种情况下,需要进行合并操作,合并操作的具体步骤如下:</p>
<ol>
<li>在合并之前,您需要先保存当前分支的更改,可以使用以下命令进行提交:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m <span class="string">"save the local changes"</span></span><br></pre></td></tr></table></figure>
<ol start="2">
<li>然后,拉取远程分支:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git pull</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>如果有冲突,则需要您手动解决冲突,并再次提交:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m <span class="string">"resolve the conflict"</span></span><br></pre></td></tr></table></figure>
<ol start="4">
<li>最后,您可以将更改推送到远程分支:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git push</span><br></pre></td></tr></table></figure>
<p>在合并冲突时,请确保仔细查看每个更改以确保不会丢失任何重要更改。</p>
<h2 id="来自你的消息-4"><a href="#来自你的消息-4" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git pull提示refusing to merge unrelated histories</p>
<h2 id="来自-ChatGPT-的消息-4"><a href="#来自-ChatGPT-的消息-4" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当执行 <code>git pull</code> 命令时,如果提示 “refusing to merge unrelated histories”,这表示您的本地分支和远程分支没有共同的祖先。它们是两个独立的仓库,不能合并。</p>
<p>这种情况通常出现在您使用了 <code>git init</code> 命令创建了一个新的本地仓库,然后尝试将其与远程仓库进行合并的情况下。</p>
<p>要解决这个问题,您可以尝试以下几种方法:</p>
<ol>
<li>如果您的本地仓库不需要保留历史记录,可以使用以下命令强制合并:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git pull origin master --allow-unrelated-histories</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>如果您希望保留本地仓库中的所有更改,而不是采用远程仓库的版本,则可以使用以下命令将本地分支推送到远程分支:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git push --set-upstream origin master</span><br></pre></td></tr></table></figure>
<p>这将创建一个新的远程分支,它的历史记录将从您的本地分支开始。</p>
<ol start="3">
<li>如果您想要通过合并来保留您的本地更改并获得远程版本的更新,则可以使用以下命令:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git fetch origin</span><br><span class="line">git merge origin/master</span><br></pre></td></tr></table></figure>
<p>请注意,在这种情况下,如果您的本地分支和远程分支有任何冲突,您需要手动解决冲突。</p>
<p>希望以上方法有助于解决您的问题。</p>
<h2 id="来自你的消息-5"><a href="#来自你的消息-5" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>提示fatal: refusing to merge unrelated histories</p>
<h2 id="来自-ChatGPT-的消息-5"><a href="#来自-ChatGPT-的消息-5" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当执行 <code>git merge</code> 命令或 <code>git pull</code> 命令时,如果提示 “fatal: refusing to merge unrelated histories”,这表示您的本地分支和远程分支没有共同的祖先,它们是两个独立的仓库,不能合并。</p>
<p>这种情况通常出现在您使用了 <code>git init</code> 命令创建了一个新的本地仓库,然后尝试将其与远程仓库进行合并的情况下。</p>
<p>要解决这个问题,您可以尝试以下几种方法:</p>
<ol>
<li>如果您的本地仓库不需要保留历史记录,可以使用以下命令强制合并:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git merge origin/master --allow-unrelated-histories</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>如果您希望保留本地仓库中的所有更改,而不是采用远程仓库的版本,则可以使用以下命令将本地分支推送到远程分支:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git push --set-upstream origin master</span><br></pre></td></tr></table></figure>
<p>这将创建一个新的远程分支,它的历史记录将从您的本地分支开始。</p>
<ol start="3">
<li>如果您想要通过合并来同时保留您的本地更改和远程版本的更新,则可以使用以下命令:</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git fetch origin</span><br><span class="line">git merge origin/master</span><br></pre></td></tr></table></figure>
<p>请注意,在这种情况下,如果您的本地分支和远程分支有任何冲突,您需要手动解决冲突。</p>
<p>希望以上方法有助于解决您的问题。</p>
<h2 id="来自你的消息-6"><a href="#来自你的消息-6" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git merge origin/master 提示refusing to merge unrelated histories</p>
<h2 id="来自-ChatGPT-的消息-6"><a href="#来自-ChatGPT-的消息-6" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当您执行以下命令时:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git merge origin/master</span><br></pre></td></tr></table></figure>
<p>并且提示 “refusing to merge unrelated histories”,这通常表示您的本地分支和远程分支是两个不相关的历史记录。这种情况经常出现在您从远程仓库中拉取了新的代码,而您的本地仓库中也有独立的更改(可能是使用 <code>git init</code> 命令创建了新的本地仓库)。</p>
<p>如果您想要通过合并来同时保留您的本地更改和远程版本的更新,可以使用以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git pull origin master --allow-unrelated-histories</span><br></pre></td></tr></table></figure>
<p>这将拉取远程分支并将其合并到您的本地分支,同时保留两者的历史记录。在执行此命令时,您需要手动解决任何冲突。</p>
<p>如果您希望保留本地仓库中的所有更改,则可以使用以下命令将本地分支推送到远程分支:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git push --set-upstream origin master</span><br></pre></td></tr></table></figure>
<p>这将创建一个新的远程分支,它的历史记录将从您的本地分支开始。</p>
<p>希望以上方法有助于解决您的问题。</p>
<h2 id="来自你的消息-7"><a href="#来自你的消息-7" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>执行git pull origin master –allow-unrelated-histories 提示Automatic merge failed; fix conflicts and then commit the result</p>
<h2 id="来自-ChatGPT-的消息-7"><a href="#来自-ChatGPT-的消息-7" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>当您执行以下命令时:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git pull origin master --allow-unrelated-histories</span><br></pre></td></tr></table></figure>
<p>并且提示 “Automatic merge failed; fix conflicts and then commit the result”,这通常表示拉取远程分支并将其合并到您的本地分支时出现了冲突。</p>
<p>要解决这个问题,您需要手动解决冲突。通常,Git 将使用合并工具将冲突标记为 <code><<<<<<<</code>,<code>=======</code> 和 <code>>>>>>>></code>,您需要编辑文件以手动解决冲突。</p>
<p>一旦您解决了冲突,您需要使用以下命令将更改的文件添加到暂存区:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git add <file></span><br></pre></td></tr></table></figure>
<p>然后,使用以下命令提交更改:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git commit -m <span class="string">"Resolved conflict"</span></span><br></pre></td></tr></table></figure>
<p>在提交更改后,您可以使用以下命令将更改推送到远程仓库:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git push origin master</span><br></pre></td></tr></table></figure>
<p>希望这可以帮助您解决问题,如果您需要更多帮助,请告诉我。</p>
<h2 id="来自你的消息-8"><a href="#来自你的消息-8" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>git rm –cached 整个文件夹</p>
<h2 id="来自-ChatGPT-的消息-8"><a href="#来自-ChatGPT-的消息-8" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>如果您想要从 Git 中删除整个文件夹(包括它下面的所有文件和子文件夹),需要使用以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">rm</span> -r --cached <folder></span><br></pre></td></tr></table></figure>
<p>其中,<code>-r</code> 选项表示要递归地删除文件夹及其所有子文件夹和文件,<code>--cached</code> 选项表示只从 Git 中删除该文件夹的跟踪,而不会删除在本地已经存在的文件。</p>
<p>请注意,这只会从 Git 中删除该文件夹的跟踪。如果文件夹之前已经被提交并被其他人克隆了该仓库,则不会删除他们本地的文件夹。如果您要在所有克隆该仓库的计算机中删除该文件夹,您需要通知每个克隆库的用户,让他们手动删除该文件夹。</p>
<p>希望这可以回答您的问题。</p>
<h1 id="去除远程-svn文件夹"><a href="#去除远程-svn文件夹" class="headerlink" title="去除远程.svn文件夹"></a>去除远程.svn文件夹</h1><h2 id="来自你的消息-9"><a href="#来自你的消息-9" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>不小心.svn文件夹提交 push到了远程,怎么处理去掉.svn文件夹</p>
<h2 id="来自-ChatGPT-的消息-9"><a href="#来自-ChatGPT-的消息-9" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><ol>
<li>使用svn命令从远程仓库中删除.svn文件夹:</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">svn delete <repository_url>/.svn --force</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>从本地文件夹中删除.svn文件夹:</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cd path/to/local/folder</span><br><span class="line">rm -rf .svn</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>将修改提交到远程仓库:</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m "remove .svn folder"</span><br><span class="line">git push</span><br></pre></td></tr></table></figure>
<p>注意:上述方法会从远程仓库中删除所有.svn文件夹,所以请确保其它人员不需要这些文件夹。同时,由于.svn文件夹可能包含重要的版本控制信息,因此请谨慎处理。</p>
<h2 id="来自你的消息-10"><a href="#来自你的消息-10" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>如何从远程里面删除,本地不删除</p>
<h2 id="来自-ChatGPT-的消息-10"><a href="#来自-ChatGPT-的消息-10" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>如果你想在远程仓库中删除<code>.svn</code>文件夹而不删除本地副本中的<code>.svn</code>文件夹,可以使用以下命令:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git rm -r --cached <repository_url>/.svn</span><br><span class="line">git commit -m "Remove .svn folder from remote repository"</span><br><span class="line">git push</span><br></pre></td></tr></table></figure>
<p>这将从远程仓库中删除<code>.svn</code>文件夹,并在提交更改后将其重新推送到远程仓库中。在这个过程中,本地副本中的<code>.svn</code>文件夹将不会被修改或删除。</p>
<p>请注意,如果<code>.svn</code>文件夹包含有关远程仓库中文件的更改历史信息,则删除它可能会影响版本控制。因此,请先做好备份,谨慎操作。</p>
<h2 id="来自你的消息-11"><a href="#来自你的消息-11" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>删除后,不想把.svn文件夹纳入git跟踪,需要怎么处理</p>
<h2 id="来自-ChatGPT-的消息-11"><a href="#来自-ChatGPT-的消息-11" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>如果你不想将<code>.svn</code>文件夹纳入Git的跟踪中,可以将其添加到<code>.gitignore</code>文件中,以便Git忽略这些文件夹的更改和提交。</p>
<ol>
<li>在你的项目根目录下,创建一个<code>.gitignore</code>文件(如果你的项目已经有了<code>.gitignore</code>文件,则可以跳过此步骤)。</li>
<li>用文本编辑器打开<code>.gitignore</code>文件,并添加以下一行:</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">.svn/</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>保存<code>.gitignore</code>文件,并将其推送到远程仓库。此时,Git将忽略任何位于<code>.svn</code>文件夹中的更改和提交。</li>
</ol>
<p>注意,添加<code>.svn/</code>到<code>.gitignore</code>文件只是不让Git跟踪<code>.svn</code>文件夹的更改,但是Git会继续跟踪其他未被忽略的文件。如果你希望停止跟踪整个文件夹,可以使用以下命令:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git rm -r --cached .svn/</span><br></pre></td></tr></table></figure>
<p>这将从Git中删除<code>.svn</code>文件夹的所有内容,并停止跟踪它的更改和提交。接下来,将<code>.svn</code>文件夹添加到<code>.gitignore</code>文件中,以确保以后Git不会再次开始跟踪它。</p>
<h2 id="来自你的消息-12"><a href="#来自你的消息-12" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>删除.svn文件夹提示fatal: pathspec ‘.svn/‘ did not match any files</p>
<h2 id="来自-ChatGPT-的消息-12"><a href="#来自-ChatGPT-的消息-12" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>如果你尝试从 Git 跟踪中移除 <code>.svn</code> 文件夹时,提示 <code>fatal: pathspec '.svn/' did not match any files</code>,可能是因为该文件夹已经被提交了,但是在本地找不到对应的目录。</p>
<p>在这种情况下,你需要使用 <code>git ls-files</code> 命令来查看规范化后的 Git 文件列表,确认一下 <code>.svn</code> 文件夹是否包含在内。</p>
<p>如果确认<code>.svn</code>文件夹被跟踪了,可以使用以下命令将其从 Git 跟踪中删除,并且保留本地 <code>.svn</code> 文件夹:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git rm -r --cached .svn</span><br><span class="line">git commit -m "Removed .svn from Git tracking"</span><br><span class="line">git push</span><br></pre></td></tr></table></figure>
<p>如果仅仅是想在以后的 Git 提交中忽略 <code>.svn</code>,可以在项目根目录下的 <code>.gitignore</code> 文件中添加以下一行:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">.svn/</span><br></pre></td></tr></table></figure>
<p>保存 <code>.gitignore</code> 文件,然后将其推送到 Git 仓库中。现在,Git 将忽略 <code>.svn</code> 文件夹的任何更改或提交。</p>
<h2 id="来自你的消息-13"><a href="#来自你的消息-13" class="headerlink" title="来自你的消息:"></a>来自你的消息:</h2><p>已经add. 但是没有commit的文件,怎么加入到忽略文件里,后面都不进行提交</p>
<h2 id="来自-ChatGPT-的消息-13"><a href="#来自-ChatGPT-的消息-13" class="headerlink" title="来自 ChatGPT 的消息:"></a>来自 ChatGPT 的消息:</h2><p>如果文件已经 add 进 Git 缓存区,但是还没有 commit,可以使用以下命令将其忽略:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git update-index --assume-unchanged <file></span><br></pre></td></tr></table></figure>
<p>其中 <code><file></code> 是你想要忽略的文件。</p>
<p>如果你想重新跟踪这个文件,可以使用以下命令:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git update-index --no-assume-unchanged <file></span><br></pre></td></tr></table></figure>
<p>但是请注意,在这种情况下,这个文件将重新被添加进 Git 缓存区。因此,在以后的提交中,它将再次被包含。如果你想要永久忽略这个文件,你应该将其添加到 <code>.gitignore</code> 文件中。这将确保在以后的提交中都不被包含。</p>
<p>如果你已经将文件添加到 <code>.gitignore</code> 文件中,并希望将其从 Git 缓存区中删除,可以使用以下命令:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git rm --cached <file></span><br></pre></td></tr></table></figure>
<p>这将从 Git 缓存区中删除指定的文件,但不会将其从文件系统中删除。这使得你可以在本地保留副本,但在以后的提交中不包含它。</p>
]]></content>
<categories>
<category>原创</category>
</categories>
<tags>
<tag>git</tag>
<tag>chatgpt</tag>
</tags>
</entry>
<entry>
<title>JAVA学习</title>
<url>/posts/969553277/</url>
<content><![CDATA[<h1 id="annotation"><a href="#annotation" class="headerlink" title="annotation"></a>annotation</h1><h2 id="元注解:"><a href="#元注解:" class="headerlink" title="元注解:"></a>元注解:</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1. @Target</span><br><span class="line">2. @Document</span><br><span class="line">3. @Retention</span><br><span class="line">4. @Inherited</span><br></pre></td></tr></table></figure>
<h2 id="Target,注解的修饰的范围"><a href="#Target,注解的修饰的范围" class="headerlink" title="@Target,注解的修饰的范围"></a>@Target,注解的修饰的范围</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1. ElementType.CONSTRUCTOR 构造器</span><br><span class="line">2. ElementType.METHOD 方法</span><br><span class="line">3. ElementType.FIELD 属性</span><br><span class="line">4. ElementType.TYPE 接口或者类</span><br><span class="line">5. ElementType.PARAMETER 参数</span><br><span class="line">6. ElementType.PACKAGE 包</span><br></pre></td></tr></table></figure>
<h2 id="Retention-注释修饰的时间"><a href="#Retention-注释修饰的时间" class="headerlink" title="Retention 注释修饰的时间"></a>Retention 注释修饰的时间</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1. RetentionPolicy.RESOURCE 源文件保留</span><br><span class="line">2. RetentionPolicy.RUNTIME 运行时保留</span><br><span class="line">3. RetentionPolicy.CLASS class文件保留</span><br></pre></td></tr></table></figure>
<h2 id="Inherited-元注解的一个标记注解,被标注的类型是被继承的。"><a href="#Inherited-元注解的一个标记注解,被标注的类型是被继承的。" class="headerlink" title="@Inherited 元注解的一个标记注解,被标注的类型是被继承的。"></a>@Inherited 元注解的一个标记注解,被标注的类型是被继承的。</h2><h1 id="cdn"><a href="#cdn" class="headerlink" title="cdn"></a>cdn</h1><h2 id="CDN简介:"><a href="#CDN简介:" class="headerlink" title="CDN简介:"></a>CDN简介:</h2><p>CDN就是内容分发网络 <code>Content Delivery NetWork</code>,构建在Internet上一种先进的流量分配网络。在现有的Internet<br>中添加一层新的网络架构,将网络内容分发到离用户最近的网络边缘,使用户可以就近取得所需的内容。</p>
<h2 id="CDN缓存内容:"><a href="#CDN缓存内容:" class="headerlink" title="CDN缓存内容:"></a>CDN缓存内容:</h2><p>目前CDN都以缓存网站中静态数据为主,如CSS. JS. 图片和静态页面等数据。用户在主站服务器请求到动态内容后,再从CDN上下载<br>这些静态数据,从而加速网站数据内容的下载速度。</p>
<h2 id="CDN要达到的目标:"><a href="#CDN要达到的目标:" class="headerlink" title="CDN要达到的目标:"></a>CDN要达到的目标:</h2><ol>
<li>可扩展性. 2. 安全性. 3. 可靠性. 响应和执行</li>
</ol>
<h2 id="负载均衡:"><a href="#负载均衡:" class="headerlink" title="负载均衡:"></a>负载均衡:</h2><ol>
<li>链路负载均衡:DNS解析成不同IP</li>
<li>集群负载均衡:由硬件和软件负载均衡</li>
<li>系统负载均衡:利用操作系统级别的软中断和硬中断来达到负载均衡,例如可以设置多列网卡。</li>
</ol>
<h2 id="CDN动态加速"><a href="#CDN动态加速" class="headerlink" title="CDN动态加速"></a>CDN动态加速</h2><ol>
<li>CDN动态加速原理;在CDN的DNS解析中,通过动态的链路探测来寻找回源最好的一条路径,然后将DNS调度将所有请求调度到选定<br>的这条路径上回源,从而加速用户的访问效率。</li>
<li>如何选择源站链路最好的路径让用户走,一个简单的原则就是在每个CDN节点上,从源站下载一个一定大小的文件,看哪个路径耗时最短,<br>可以构成一个链路列表,绑定到DNS解析上。</li>
</ol>
<h1 id="class"><a href="#class" class="headerlink" title="class"></a>class</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>1:任何一个Class文件都对应唯一一个类或者接口的定义信息。Class文件是一组8位字节码为基础单位的二进制流,各个项目岩哥按照顺序紧凑的排列在Class文件中,中间没有添加任何的分隔符,Class文件存储的几乎是全部的必要数据。<br>2:Class文件格式采用二中类型数据:无符号数和表。无符号数:无符号数属于基本数据类型,用来描述数字. 索引引用. 数量值或者按照UTF-8编码组成的字符串。表:是由多个无符号数或者其他表作为数据项构成的符合数据类型,所有的表都习惯性的以”_info”结尾。<br>3:每个Class文件的头4个字节成为”魔数”,它的唯一作用就是确定这个文件是否为一个能够被虚拟机接受的Class文件,很多文件存储标准中都使用魔数来进行身份识别。<br>4:Class文件的魔数值为oxCAFEBABE</p>
<h2 id="类的加载时机"><a href="#类的加载时机" class="headerlink" title="类的加载时机"></a>类的加载时机</h2><ol>
<li>类从加载到虚拟机内存开始,到卸载出内存位置。它的生命周期包括:加载. 连接. 初始化. 使用. 卸载。连接:验证. 准备. 解析。</li>
</ol>
<h2 id="类的初始化"><a href="#类的初始化" class="headerlink" title="类的初始化"></a>类的初始化</h2><ol>
<li>遇到new. getstatic. putstatic或invokestatic这4条字节码指令的时候,如果类没有初始化,则需要初始化。new创建一个对象,获取或者设置静态属性. 调用静态方法时,需要初始化。</li>
<li>使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。</li>
<li>当初始化一个类的时候,发现其父类没有初始化,则需要先触发其父类的初始化。</li>
<li>当使用jdk7动态语言支持时,有些方法句柄对应的类,没有进行初始化,需要触发其初始化。</li>
</ol>
<h2 id="类的加载过程"><a href="#类的加载过程" class="headerlink" title="类的加载过程"></a>类的加载过程</h2><ol>
<li>在加载阶段,虚拟机需要完成一下3件事情,1:通过一个类的权限名来获取定义此类的二进制字节流。<br>2:将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构。3:在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。</li>
</ol>
<h2 id="类加载过程:"><a href="#类加载过程:" class="headerlink" title="类加载过程:"></a>类加载过程:</h2><ol>
<li>加载:查找和导入class文件</li>
<li>连接:<br> 2.1:验证:检查载入Class文件是否符合JVM规范。<br> 2.2:准备:将静态变量分配存储空间。<br> 2.3:解析:将符号引用,转换成直接引用。</li>
<li>初始化:对类的静态变量,静态代码块初始化操作。</li>
</ol>
<h2 id="类的加载过程:"><a href="#类的加载过程:" class="headerlink" title="类的加载过程:"></a>类的加载过程:</h2><ol>
<li>类的主动引用(一定会发生类的初始化)<br>1.1 new一个类的对象<br>1.2 调用一个类的静态成员或者静态方法<br>1.3 使用反射方法对类进行反射调用<br>1.4 启动main方法所在的类<br>1.5 当初始化一个类时,其父类如果没有被初始化,则先初始化父类</li>
<li>类的被动引用(不会发生类的初始化)<br>2.1 当访问一个静态域时,只有真正声明的这个类才会被初始化<br> 通过子类引用父类的静态变量,子类不会被初始化<br>2.2 通过数组定义引用类,不会触发类的初始化<br>2.3 引用常量不会触发此类的初始化</li>
</ol>
<h2 id="类加载器的代理模式:"><a href="#类加载器的代理模式:" class="headerlink" title="类加载器的代理模式:"></a>类加载器的代理模式:</h2><ol>
<li>代理模式:交给其他加载器来加载指定的类</li>
<li>双亲委托机制:在加载类时,首先将加载任务交给父类加载器加载。双亲委托机制是为了保证java核心类库的类型安全。</li>
</ol>
<h2 id="类加载器的层次结构:"><a href="#类加载器的层次结构:" class="headerlink" title="类加载器的层次结构:"></a>类加载器的层次结构:</h2><ol>
<li>引导类加载器(根加载器)</li>
<li>扩展类加载器</li>
<li>应用程序加载器</li>
<li>自定义加载器</li>
</ol>
<h2 id="线程上下文加载器:"><a href="#线程上下文加载器:" class="headerlink" title="线程上下文加载器:"></a>线程上下文加载器:</h2><ol>
<li>当需要动态加载资源的时候,你至少有三个classloader可以选择:<br>1.1系统类加载器叫做应用加载器<br>1.2当前类加载器<br>1.3当前线程加载器<br>1.4线程类加载器是为了抛弃双亲委派加载链模式<br>每个线程都有一个关联的上下文类加载器。如果使用new Thread方式生成新的线程,新线程将继承父线程的上下文加载器</li>
</ol>
<h1 id="concurrent"><a href="#concurrent" class="headerlink" title="concurrent"></a>concurrent</h1><ul>
<li>jdk1.5以后,推出了java.util.concurrent包</li>
<li>Executor:具体的Runnable的执行者</li>
<li>ExecutorService:一个线程池的管理者,其实现类有很多,可以把Runnable. Callable提交到线程池中调用。</li>
<li>Semaphore:信号量。</li>
<li>ReentrantLock:可重入的互斥锁。</li>
<li>Future:与Runnable. Callable接口进行交互。</li>
<li>BlockingQueue:阻塞队列。</li>
<li>CompletionService:ExecutorService的扩展,可以活得最先执行完线程的结果,底层实现是通过阻塞队列。</li>
<li>CountDownLatch:一个同步辅助类,完成一组正在其他线程中执行的操作之前,允许一个或者多个线程一直等待。</li>
<li>CyclicBarrier:一个同步辅助类,它允许一组线程互相等待,直到达到某个屏障点。</li>
<li>CopyOnWriteArrayList:</li>
</ul>
<ol>
<li>在CopyOnWriteArrayList在处理写(包括add. set. remove)操作的时候,先将原始数据通过Array.copyOf来生成一个新的数组,<br>在新的数据对象上写,写完后再将原来的引用指向当前的数据对象,并加锁。</li>
<li>读操作在引用的当前引用的对象上进行读,不存在加锁和阻塞。</li>
<li>因为每次使用CopyOnWriteArrayList.add都要引起数组拷贝,所以应该避免在循环中使用。可以在初始化完成之后设置到CopyeOnWriteArrayList中,或者使用CopyOnWriteArrayList.addAll方法。</li>
<li>CopyOnWriteArrayList采用“写入时复制”策略,对容器的写操作将导致的容器中基本数据的复制,性能开销较大。所以在有写操作的情况下,CopyOnWriteArayList性能不佳,而且如果容器容量较大的话容易造成溢出。</li>
</ol>
<ul>
<li><p>线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。<br>说明:Executors 返回的线程池对象的弊端如下:<br>1)FixedThreadPool 和 SingleThreadPool:<br>允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。<br>2)CachedThreadPool 和 ScheduledThreadPool:<br>允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。</p>
</li>
<li><p>SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。</p>
</li>
</ul>
<p>正例:注意线程安全,使用 DateUtils。亦推荐如下处理:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override</span><br><span class="line">protected DateFormat initialValue() {</span><br><span class="line">return new SimpleDateFormat("yyyy-MM-dd");</span><br><span class="line"> };}</span><br></pre></td></tr></table></figure>
<ul>
<li><p>高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。</p>
</li>
<li><p>对多个资源. 数据库表. 对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。<br>说明:线程一需要对表 A. B. C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序 也必须是 A. B. C,否则可能出现死锁。</p>
</li>
<li><p>并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在 数据库层使用乐观锁,使用 version 作为更新依据。<br>说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。</p>
</li>
<li><p>多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。</p>
</li>
<li><p>使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,<br>确保 countDown 方法可以执行,避免主线程无法执行 至 countDown 方法,直到超时才返回结果。 说明:注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。</p>
</li>
<li><p>避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。<br>说明:Random 实例包括 java.util.Random 的实例或者 Math.random()实例。<br>正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom,在 JDK7 之前,可以做到每个 线程一个实例。</p>
</li>
<li><p>volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题, 但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现:<br>AtomicInteger count = new AtomicInteger(); count.addAndGet(1);如果是 JDK8,推 荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。</p>
</li>
<li><p>HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在 开发过程中注意规避此风险</p>
</li>
<li><p>ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static 修饰。<br>这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享 此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只 要是这个线程内定义的)都可以操控这个变量。</p>
</li>
</ul>
<h1 id="core"><a href="#core" class="headerlink" title="core"></a>core</h1><h2 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h2><ol>
<li>volatile修饰的变量,可以保证从主内存加载到线程内存的值是最新的。</li>
<li>volatile可以防止指令重排序。</li>
</ol>
<h2 id="单例模式失效"><a href="#单例模式失效" class="headerlink" title="单例模式失效"></a>单例模式失效</h2><p>双重锁检测,可能会失效。原因在于:初始化Singleton和将地址写入到instance字段的顺序是不确定的。在某个线程new Singleton()时,在构造方法之前,就为<br>该对象分配了内存空间并将对象的字段设置成默认值。此时就可以将分配的内存地址赋值给instance字段了,然而改对象可能还没有初始化;此时若另外一个线程来调用<br>getInstance,渠道的状态就是不正确的对象。</p>
<h2 id="一致性Hash算法"><a href="#一致性Hash算法" class="headerlink" title="一致性Hash算法"></a>一致性Hash算法</h2><ol>
<li>一个分布式系统,要将数据存储到具体的节点。如果普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点,如果有一个机器加入<br>或者退出这个集群,则所有的数据映射都无效,如果是持久化存储则要做数据迁移,如果是分布式存储,则其他存储就失效了。</li>
<li>把数据用户hash函数(如MD5),映射到一个很大的空间。数据的存储时,先得到一个hash值,对应到这个环上的每个位置。数据沿着顺时针找到一个机器节点,将数据<br>存储到这个机器上。</li>
<li>当其中一个机器节点挂掉后,另外一个节点会承担挂掉节点的数据,另外一个节点很容易宕机。</li>
<li>引入”虚拟节点”的概念:即把想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点。由于这些<br>虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。</li>
</ol>
<h2 id="equals-和-hashcode"><a href="#equals-和-hashcode" class="headerlink" title="equals 和 hashcode"></a>equals 和 hashcode</h2><p>在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。<br>equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。<br>hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。<br>若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值<br>Oject类中的equals方法用来比较两个引用值,hashCode用来返回引用的内地址的十六进制数值。在Set以及Map集合中,判断两个元素是否重复时,往往需要使用这两个方法。这两个方法往往被子类覆盖</p>
<ol>
<li><p>如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 </p>
</li>
<li><p>如果两个对象不equals,他们的hashcode有可能相等。 </p>
</li>
<li><p>如果两个对象hashcode相等,他们不一定equals。 </p>
</li>
<li><p>如果两个对象hashcode不相等,他们一定不equals。<br>for testsdsdsd</p>
</li>
<li><p>如果两个对象hashcode相等,他们不一定equals。</p>
</li>
<li><p>如果两个对象hashcode不相等,他们一定不equals。</p>
</li>
</ol>
<h1 id="design"><a href="#design" class="headerlink" title="design"></a>design</h1><h2 id="设计模式的六大原则:"><a href="#设计模式的六大原则:" class="headerlink" title="设计模式的六大原则:"></a>设计模式的六大原则:</h2><ol>
<li>开闭原则:对扩展开放,对修改关闭。为了程序可扩展. 易于维护和升级,需要使用接口和抽象类。</li>
<li>里氏替换原则:任何基类出现的地方,子类一定可以出现。</li>
<li>依赖反转原则:针对接口编程,依赖抽象,不依赖具体。</li>
<li>接口隔离原则:使用多个隔离的接口,比使用单个接口要好。</li>
<li>迪米特法则(最少知道原则):一个实体应该尽量少的和其他实体之间发生交互。</li>
<li>合成复用原则:尽量使用聚合,合成方式,而不是使用继承。</li>
</ol>
<h2 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h2><ol>
<li>创建型模式:工厂模式. 抽象工厂模式. 单例模式. 建造者模式,原型模式。</li>
<li>结构型模式:适配器. 装饰器. 代理. 外观. 桥接. 组合. 享元模式</li>
<li>行为型模式:策略模式. 模板方法. 观察者模式. 迭代子模式. 责任链模式. 命令模式. 备忘录模式. 状态模式. 访问者模式. 中介模式. 解析器模式。</li>
<li>其他2种:并发型模式. 线程池模式。</li>
</ol>
<h2 id="常见设计模式:"><a href="#常见设计模式:" class="headerlink" title="常见设计模式:"></a>常见设计模式:</h2><ol>
<li>策略模式:定义了一系列算法,并将每个算法封装起来,使他们可以互相替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为<br>一系列实现类提供方法,多个实现类实现该接口。</li>
<li>模板方法:准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式展现,然后声明一些抽象方法来迫使子类实现剩余逻辑。不同的子类可以以不同<br>的逻辑来实现。</li>
<li>工厂方法:建立一个工厂类,对实现了同一个接口的一些类做实例化。</li>
<li>适配器模式:适配器模式将某个类的接口转换成希望的另外一个接口表示,目的是为了消除由于接口不匹配造成的兼容性问题。主要包括三类,类的适配器.<br>对象的适配模式. 接口的适配模式。<br>类的适配器:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里。<br>对象的适配器:基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。<br>接口的适配器:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,<br>有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,<br>只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行</li>
<li>装饰模式:装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例</li>
<li>代理模式:代理模式就是多一个代理类出来,替原对象进行一些操作</li>
</ol>
<h2 id="面向对象的五大基本原则-solid"><a href="#面向对象的五大基本原则-solid" class="headerlink" title="面向对象的五大基本原则(solid)"></a>面向对象的五大基本原则(solid)</h2><ol>
<li>S单一职责SRP:Single-Responsibility Principle 一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合,高内聚在面向对象原则的引申,将职责定义为引起变化的原因,以提高内聚性减少引起变化的原因。</li>
<li>O开放封闭原则OCP:Open-Closed Principle 软件实体应该是可扩展的,而不是可修改的。对扩展开放,对修改封闭</li>
<li>L里氏替换原则LSP:Liskov-Substitution Principle 子类必须能够替换其基类。这一思想表现为对继承机制的约束规范,只有子类能够替换其基类时,才能够保证系统在运行期内识别子类,这是保证继承复用的基础。</li>
<li>I接口隔离原则ISP:Interface-Segregation Principle 使用多个小的接口,而不是一个大的总接口</li>
<li>D依赖倒置原则DIP:Dependency-Inversion Principle 依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者共同依赖于抽象。抽象不依赖于具体,具体依赖于抽象。</li>
</ol>
<h1 id="EasyCoding"><a href="#EasyCoding" class="headerlink" title="EasyCoding"></a>EasyCoding</h1><h2 id="Cpu与内存"><a href="#Cpu与内存" class="headerlink" title="Cpu与内存"></a>Cpu与内存</h2><ol>
<li>Cpu是一块超大规模集成电路板,是计算机的核心部件。CPU包括:控制器. 运算器. 寄存器;</li>
<li>控制器组成:控制单元. 指令译码器. 指令寄存器组成;控制单元是CPU的大脑,由时序控制器和指令控制器组成;指令译码器是在控制单元的协调下完成指令读取,<br>分析并交由运算器执行;指令寄存器是存成指令集,当前流行的指令集包括:X86,SSE,MMX等。</li>
<li>运算器:核心是算术逻辑运算单元,ALU,能执行算术运算或逻辑运算等各种指令。运算单元会从寄存器中提取或者存储数据。</li>
<li>寄存器:最著名的寄存器是CPU的高速缓存L1. L2,缓存容量是在组装计算机逼问的CPU性能问题之一;CPU缓存部分指令和数据,以提升性能;</li>
<li>L0寄存器. L1一级缓存. L2二级缓存. 内存. 本地磁盘. 分布式存储. 云端存储;</li>
</ol>
<h2 id="TCP-IP协议"><a href="#TCP-IP协议" class="headerlink" title="TCP. IP协议"></a>TCP. IP协议</h2><ol>
<li>传输控制协议/因特网互联协议。是当前流行的网络传输协议框架。</li>
<li>OSI模型:物理层. 数据链路层. 网络层. 传输层. 会话层. 表示层. 应用层;</li>
</ol>
<h2 id="影响RPC性能的因素如下:"><a href="#影响RPC性能的因素如下:" class="headerlink" title="影响RPC性能的因素如下:"></a>影响RPC性能的因素如下:</h2><ol>
<li>序列化。常用的RPC序列化协议包括:Thrift. Protobuf. Avro. Kryo. MsgPack. Hessian. Jackson。</li>
<li>传输协议。常用的传输协议包括:HTTP. Socket. TCP. UDP等;</li>
<li>连接。连接包括:长连接. 短链接;</li>
<li>IO模型。常用的网络IO模型:同步阻塞IO. 同步非阻塞IO. IO多路复用. 异步IO;<br>注释:stub一般翻译成存根,即在本地存在一个和远程一样的方法;</li>
</ol>
<h2 id="Protobuf"><a href="#Protobuf" class="headerlink" title="Protobuf"></a>Protobuf</h2><p>protobuf是一个高性能. 易扩展的序列化框架,通常是rpc调用追求高性能的首选。结合Netty可以非常便捷的实现RPC调用。Protobuf比JSON. XML更快. 更轻. 更小<br>,并且可以跨平台。Protobuf首先要编写proto文件,即IDL文件,后缀为“.proto”的文件。然后通过客户端生成Java相关类进行序列化. 反序列化。</p>
<h2 id="Rest"><a href="#Rest" class="headerlink" title="Rest"></a>Rest</h2><p>Rest是Representaitional State Transfer的缩写,通常翻译成“表现层状态转换”;</p>
<ol>
<li>协议:API基于HTTP协议;</li>
<li>域名:需要一个域名例如:<a href="http://api.xx.com/">http://api.xx.com</a>;</li>
<li>版本:需要版本信息,例如<a href="http://api.xx.com/v1/">http://api.xx.com/v1/</a></li>
<li>路径:rest开发又被称作是面向资源的开发。例如<a href="http://api.xx.com/v1/user/">http://api.xx.com/v1/user/</a></li>
<li>方法:一般包含的方法包括:get(获取资源,一个或者多个);post(创建资源);put(修改资源,客户端提供修改后的完整资源);<br>patch(对已知资源进行全局更新,客户端需要提供改变的属性);delete(删除,回收资源);head(获取资源的元数据);option(读取对资源的访问权限);<br>注释:SLA:需要提供相应时间. 吞吐量. 可用性等关键指标;</li>
</ol>
<h2 id="HTTP-x2F-2协议"><a href="#HTTP-x2F-2协议" class="headerlink" title="HTTP/2协议"></a>HTTP/2协议</h2><p>HTTP/2对比HTTP/1.x进行了大量简化,使得性能大幅提升;HTTP/2是基于二进制协议的。<br>HTTP/1.x的语义只支持客户端发起请求,服务端响应数据。HTTP/2改变了这种模式,只需要客户端发送一次请求,服务端便把所有的资源都推送到客户端。</p>
<h2 id="Cloud-Native属性总结"><a href="#Cloud-Native属性总结" class="headerlink" title="Cloud Native属性总结"></a>Cloud Native属性总结</h2><p>分布式. 弹性. 多租户. 自服务. 按需计量和计费. 增量部署和测试;</p>
<h2 id="gRPC"><a href="#gRPC" class="headerlink" title="gRPC"></a>gRPC</h2><ol>
<li>gRPC默认使用Protobuf进行序列化和反序列化。</li>
<li>gRPC默认采用HTTP/2进行传输。HTTP/2支持流(streaming),在批量发送数据的场景下使用流可以显著提升性能;</li>
<li>gRPC流可以分为三类:客户端流式发送. 服务端流式返回. 客户端/服务端同时流式处理;</li>
<li>gRPC并非完美,相比非IDL描述的RPC(Hession. Kyro)方式,定义proto文件是一个比较麻烦的事情。另外HTTP/2相比于基于TCP的通信协议,心梗也有显著的差异;</li>
</ol>
<h2 id="微服务框架"><a href="#微服务框架" class="headerlink" title="微服务框架"></a>微服务框架</h2><p>服务治理. 容量规划. 高效通信(需要微服务框架实现高效的序列化. 反序列化. 支持并行. 异步. 非阻塞转换以及多语言支持). 负载均衡(微服务框架需要支持常用<br>负载均衡. 故障转移. 支持自由的流量切换);</p>
<h2 id="Dubbo"><a href="#Dubbo" class="headerlink" title="Dubbo"></a>Dubbo</h2><ol>
<li>Dubbot中的角色:Provider提供者,Consumer消费者. Registry注册中心. Montiro监控中心。</li>
<li>消费者启动后,会查询注册中心,注册中心返回提供者地址列表给消费者。如果有变更,注册中心将以长连接推送变更数据给消费者。</li>
</ol>
<h2 id="Etcd"><a href="#Etcd" class="headerlink" title="Etcd"></a>Etcd</h2><p>Etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。它使用Go语言编写,并通过Raft一致性算法处理日志复制以保障强一致性。Etcd并不是强一致的。</p>
<h2 id="微服务部署策略"><a href="#微服务部署策略" class="headerlink" title="微服务部署策略"></a>微服务部署策略</h2><p>服务独享数据库. 服务独享虚拟机/容器;</p>
<h2 id="容器VS虚拟机"><a href="#容器VS虚拟机" class="headerlink" title="容器VS虚拟机"></a>容器VS虚拟机</h2><p>虚拟机是在硬件的基础上进行虚拟化,隔离性更高,而容器是在操作系统上进行的虚拟化。容器更像软件中的集装箱,能够把环境. 配置. 依赖. 软件等封装起来。<br>Docker可以让开发者大宝应用及依赖包到一个轻量级. 可移植的容器中,然后发布到任何安装了Docker的物理机或者虚拟机上,而不必担心是否安装了依赖项,<br>不必考虑编译器或其他任何需要支持的基础设施;</p>
<h2 id="分布式消息中间件"><a href="#分布式消息中间件" class="headerlink" title="分布式消息中间件"></a>分布式消息中间件</h2><p>通过分布式消息中间件解耦。系统之间可以进行可靠的异步通信,从而降低系统之间的耦合度,系统能够得到更好的扩展性和可用性。通过分布式消息中间件降低响应<br>时间。通过分布式消息中间件提升吞吐率。</p>
<h2 id="Kafka的设计原理"><a href="#Kafka的设计原理" class="headerlink" title="Kafka的设计原理"></a>Kafka的设计原理</h2><p>在Kafka中,消息被持久化到磁盘,Kafka需要依赖于ZooKeeper管理元数据。<br>Borker:Kafka的服务端,负责接收数据,并持久化数据,Broker可以有多个,每隔Broker可以包含多个Topic,Broker并不保存Offset数据,由Consumer自己<br>负责保存,默认保存在Zookeeper中。<br>Producer:生产者,生成数据发送到Broker存储数据,Producer将会和Topic下所有Partition Leader保持连接。<br>Consumer:消费者,每个Partition只能被一个消费者订阅,一个消费者可以订阅多个Partition,消费者挂掉后会从新进行负载均衡。<br>Topic:主题,每个Topic包含多个Partition,所有的元数据都存储在ZooKeeper中。<br>Partition:分区,Kafka为了扩展性,可以将一个Topic拆分为多个分区,每个分区可以独立放到一个Broker上。</p>
<h1 id="generic"><a href="#generic" class="headerlink" title="generic"></a>generic</h1><h2 id="概念:"><a href="#概念:" class="headerlink" title="概念:"></a>概念:</h2><ol>
<li>泛型是参数化类型,使用广泛的类型。</li>
<li>起因:数据类型不明确,装入的类型被当做Object对待,从而”丢失”自己的实际类型。获取数据的时候往往需要转型,效率低,容易产生错误。</li>
<li>作用:3.1安全,在编译的时候检查类型安全。3.2省心,所有强制类型转换都是自动和隐式的,提高代码的重用率。</li>
</ol>
<h2 id="定义:"><a href="#定义:" class="headerlink" title="定义:"></a>定义:</h2><p>class 类名 <字母列表> {<br> 修饰符 字母 属性<br> 修饰符 构造器{<br> }<br> 修饰符 返回类型 方法(){<br> }<br>}<br>泛型常见的字母列表:<br>T type 表示类型<br>K V 分别表示键中的key 和 value的值。<br>E 代表Element<br>? 代表不确定的类型</p>
<h2 id="泛型使用需注意问题:"><a href="#泛型使用需注意问题:" class="headerlink" title="泛型使用需注意问题:"></a>泛型使用需注意问题:</h2><ol>
<li>不能用在静态属性. 静态方法上</li>
<li>泛型使用不能指定基本类型</li>
<li>接口中泛型只能使用在接口方法中,不能使用在全局变量中</li>
</ol>
<h1 id="http"><a href="#http" class="headerlink" title="http"></a>http</h1><h2 id="HTTP协议特点:"><a href="#HTTP协议特点:" class="headerlink" title="HTTP协议特点:"></a>HTTP协议特点:</h2><ol>
<li>支持客户端和服务端</li>
<li>简单:客户向服务器发送请求时,只需要传送请求方法和路径。</li>
<li>灵活:HTTP允许传输任意类型的数据对象</li>
<li>无连接:无连接限制每次连接只处理一次请求。服务器处理完客户的请求后,并收到客户的应答后,立即断开连接,采用这种方式节省时间</li>
<li>无状态:HTTP协议时无状态的,对于事务处理没有记忆能力,缺少状态意味着如果后续处理需要前面的信息,则必须重传</li>
</ol>
<h2 id="Cookie和Session"><a href="#Cookie和Session" class="headerlink" title="Cookie和Session"></a>Cookie和Session</h2><ol>
<li>Cookie数据存放到客户端的浏览器上,sessions数据存放在服务器上。</li>
<li>cookie不是很安全,别人可以分析本地的cookie并进行cookie欺骗。</li>
<li>单个cookie保存的数据不能超过4k,很多浏览器限制一个站点最多保存20个cookie</li>
<li>Cookie 客户端需要每次将Cookie的值传送给服务端,如果Cookie的值很大,无形中增加了客户端与服务端数据传输的数量</li>
<li>Session 同一个客户端和服务端交互,不需要每次都回传所有的cookie值,而是只需要传递一个JssionId</li>
</ol>
<h2 id="HTTP介绍"><a href="#HTTP介绍" class="headerlink" title="HTTP介绍"></a>HTTP介绍</h2><ol>
<li>大多数传统的c/s互联网应用采用长连接方式</li>
<li>http采用无状态的端连接,采用这种方式的目的是未来同时服务更多的用户。</li>
<li>互联网所有的资源都需要用一个URL标识,URL是统一资源定位符</li>
<li>发起一个HTTP请求就是建立一个Socket的过程。</li>
<li>Http Header是控制着互联网用户数据的传输,控制着浏览器的渲染行为和服务器的执行逻辑。</li>
<li>http请求头,包括accept-charset 指定客户端接受的字符集。Accept-Encoding 用于接受可接受的内容编码。Accept-Language,<br>用于指定一种自然语言。</li>
</ol>
<h2 id="DNS"><a href="#DNS" class="headerlink" title="DNS"></a>DNS</h2><ol>
<li>将域名解析成IP</li>
<li>DNS解析过程<br>2.1 检查缓存中是否有域名对应的IP,如果有直接使用,缓存大小,缓存时间都有限制。缓存时间太长一旦域名对应的IP有变化,这段时间会有部分用户无法访问。<br>时间太短,每次访问都需要重新解析。<br>2.2 如果浏览器没有缓存,操作系统也可以存储。windows在host中存储,linux在etc/host中存储。</li>
<li>几种域名解析方式<br>3.1:A记录,A代表Address,用来指定域名对应的IP地址。A记录可以将多个域名解析到一个IP地址。但是不能将多个域名解析到多个IP地址。<br>3.2:CNAME记录,全称(别名解析),所谓的别名解析就是将一个域名设置一个或者多个别名。</li>
</ol>
<h2 id="Http和Https的区别"><a href="#Http和Https的区别" class="headerlink" title="Http和Https的区别"></a>Http和Https的区别</h2><ol>
<li>Https即Secure Hypertext Transfer Protocol,即安全超文本传输协议,它是一个安全通信信道,基于Http开发,用于在客户机和服务器间交换信息。它使用安全套接字层SSL进行信息交换,是Http的安全版。</li>
<li>Https协议需要到CA申请证书,一般免费证书很少,需要交费。</li>
<li>Http是超文本传输协议,信息是明文传输,https则是具有安全性的tls/ssl加密传输协议。</li>
<li>http是80端口,https是443端口</li>
</ol>
<h2 id="浏览器输入一个URL的过程"><a href="#浏览器输入一个URL的过程" class="headerlink" title="浏览器输入一个URL的过程"></a>浏览器输入一个URL的过程</h2><ol>
<li>浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址</li>
<li>解析出IP地址后,根据IP地址和默认端口80和服务器建立TCP连接</li>
<li>浏览器发出Http请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器</li>
<li>服务器做出响应,把对应的请求资源发送给浏览器</li>
<li>释放TCP连接</li>
<li>浏览器解析并显示内容</li>
</ol>
<h2 id="LinuxIO模型"><a href="#LinuxIO模型" class="headerlink" title="LinuxIO模型"></a>LinuxIO模型</h2><ol>
<li>阻塞IO模型 以socket为例,在进程空间调用recvfrom,其系统调用知道数据包到达且被复制到应用进程的缓冲区或者发生错误才返回,在此期间一直等待,进程从调用recvfrom开始到它返回的整段时间内都是被阻塞的,因此称为阻塞IO</li>
<li>非阻塞IO模型 应用进程调用recvfrom,如果缓冲区没有数据直接返回EWOULDBLOCK错误。一般对非阻塞IO进行轮询,以确定是否有数据到来。</li>
<li>IO多路复用模型<br>Linux提供select/poll,通过将一个或多个fd传递给select或poll系统调用,阻塞在select上。select/poll顺序扫描fd是否就绪。</li>
<li>信号驱动IO<br>开启套接字接口信号驱动IO功能,并通过系统调用sigaction执行信号处理函数。当数据准备就绪时,为该进程生成SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主函数处理数据。</li>
<li>异步IO<br>告知内核启动某个操作,并让内核在整个操作完成后通知我们。它与信号驱动IO的区别在于信号驱动IO由内核通知我们何时可以开始IO操作。而异步IO模型由内核通知我们IO操作已经完成。</li>
</ol>
<h1 id="instrumentation"><a href="#instrumentation" class="headerlink" title="instrumentation"></a>instrumentation</h1><h2 id="Instrumentation"><a href="#Instrumentation" class="headerlink" title="Instrumentation"></a>Instrumentation</h2><p>Instrumentation类为JVM上运行时的程序提供测量手段。很多工具通过Instrumentation修改方法字节码,实现数据收集的目的。<br>这些通过Instrumentation搜集数据的工具不会改变程序的状态和行为。这些良好的工作包括,monitoring agents, profilers, coverage analyzers, 和event loggers。</p>
<h2 id="获取Instrumentation接口的方式"><a href="#获取Instrumentation接口的方式" class="headerlink" title="获取Instrumentation接口的方式"></a>获取Instrumentation接口的方式</h2><ol>
<li>启动JVM时指定agent类,这种方式,instrumentation的实例通过agent class的premain方法被传入。</li>
<li>JVM提供了一种当JVM启动完成后启动agent机制,这种情况下,Instrumentation通过agent代码中的agentmain方式传入。<br>java agent 在JDK package specification中解释:一个agent 是被作为Jar 文件形式来部署的。在Jar文件中manifest中指定哪个类作为agent类。</li>
</ol>
<h1 id="Interview"><a href="#Interview" class="headerlink" title="Interview"></a>Interview</h1><h2 id="导致服务器频繁fullGC的原因有那些?"><a href="#导致服务器频繁fullGC的原因有那些?" class="headerlink" title="导致服务器频繁fullGC的原因有那些?"></a>导致服务器频繁fullGC的原因有那些?</h2><h2 id="限流算法"><a href="#限流算法" class="headerlink" title="限流算法"></a>限流算法</h2><ol>
<li>在高并发的系统中,缓存. 降级. 限流是三把利器。</li>
<li>限流就是限制流量,很好的控制系统的qps,从而达到保护系统的作用。</li>
<li>限流的方法<br>1)计数器:计数器是最简单最容易实现的算法。设置一个计数器counter,没当一个请求过来的时候,counter加1,如果counter的值大于100,并且<br>请求与第一个请求的间隔时间在1分钟以内,那么说明请求数过多。算法简单,最容易出现的是临界值问题。<br>2)滑动窗口:滑动窗口为了解决临界值问题,很多个矩形框组成时间窗口,每个时间窗口是1分钟,将滑动窗口划分为6个格子,每个格代表10秒钟,没过<br>10秒钟,格子向右滑动一格,每个各自有自己的counter。滑动窗口划分的格子越多,滑动窗口的滚动就越平滑,限流的统计就越精确。</li>
</ol>
<p>滑动窗口指收发两端分别维护一个发送窗口和接收窗口,发送窗口有一个窗口值Wt,窗口值Wt代表在没有收到对方确认的情况下最多可以发送的帧的数目。<br>当发送的帧的序号被接收窗口正确收下后,接收端向前滑动并向发送端发去确认,发送端收到确认后,发送窗口向前滑动。收发两端按规律向前推进.<br>滑动窗口指接收和发送两端的窗口按规律不断向前推进,是一种流量控制的策略.</p>
<p>3)漏桶算法:有个固定容量的桶,有水流进来,也有水流出去,对于进来的水,无法估量有多少,水流的速度也无法估计,但是对于流出去的水来说,可以固定流出的频率。而且<br>当桶满后,多余的水会流出去。<br>我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。当使用了漏桶算法,我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。<br>4)令牌桶算法:令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以 一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。<br>每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。<br>我们会发现我们默认从桶里移除令牌是不需要耗费时间的。如果给移除令牌设置一个延时时间,那么实际上又采用了漏桶算法的思路。Google的guava库下的SmoothWarmingUp类就采用了这个思路。</p>
<p>Java实现<br>我们可以使用Guava 的 RateLimiter 来实现基于令牌桶的流控,RateLimiter 令牌桶算法是单桶实现。RateLimiter 对简单的令牌桶算法做了一些工程上的优化,具体的实现是 SmoothBursty。<br>需要注意的是,RateLimiter 的另一个实现SmoothWarmingUp,就不是令牌桶了,而是漏桶算法。也许是出于简单起见,RateLimiter 中的时间窗口能且仅能为 1s。<br>SmoothBursty 有一个可以放 N 个时间窗口产生的令牌的桶,系统空闲的时候令牌就一直攒着,最好情况下可以扛 N 倍于限流值的高峰而不影响后续请求。<br>RateLimite允许某次请求拿走超出剩余令牌数的令牌,但是下一次请求将为此付出代价,一直等到令牌亏空补上,并且桶中有足够本次请求使用的令牌为止。<br>当某次请求不能得到所需要的令牌时,这时涉及到一个权衡,是让前一次请求干等到令牌够用才走掉呢,还是让它先走掉后面的请求等一等呢?Guava 的设计者选择的是后者,先把眼前的活干了,后面的事后面再说。</p>
<h2 id="线程池排队策略"><a href="#线程池排队策略" class="headerlink" title="线程池排队策略"></a>线程池排队策略</h2><ol>
<li>直接提交:工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。<br>当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。</li>
<li>无界队列:使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。<br>(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;</li>
<li>有界队列:有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:<br>使用大型队列和小型池可以最大限度地降低 CPU 使用率. 操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。<br>四种预定义的处理程序策略:<br>在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时RejectedExecutionException。<br>在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。<br>在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。<br>在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。</li>
</ol>
<ul>
<li>除了kv类型的数据存储外,redis适合的业务场景</li>
<li>如果解决写操作跨库的数据一致性问题</li>
<li>spring aop如何实现,两种的区别,描述一下java动态代理的实现</li>
<li>什么是乐观锁,乐观锁的机制是什么,<br>乐观锁实现的机制就是CAS操作</li>
</ul>
<h2 id="线程通信"><a href="#线程通信" class="headerlink" title="线程通信"></a>线程通信</h2><p>线程间通信和同步的方式主要有锁. 信号. 信号量<br>进程间的通信:通信机制主要有:管道. 有名管道. 消息队列. 信号量. 共享空间. 信号. 套接字(socket)<br>java线程通信 wait. notify. notifyAll. concurrent包lock下的condition里的await. signal. signalALl方法</p>
<h1 id="io"><a href="#io" class="headerlink" title="io"></a>io</h1><h2 id="字节流:"><a href="#字节流:" class="headerlink" title="字节流:"></a>字节流:</h2><ol>
<li>字节流可以处理一切(文本. 音频. 视频)</li>
<li>输入流 InputStream FileInputStream ByteArrayInputStream ObjectInputStream BufferedInputStream FilterInputStream DataInputStream</li>
<li>操作read字节数组<br>中间容器 byte[] flush = new byte[长度],接收长度 int len = 0,循环读取while(-1!=(len=流.read(flush)))。输出拷贝</li>
<li>输出流 OutputStream FileOutputStream ByteArrayOutputStream ObjectOutputStream BufferedOutputStream DataOutputStream</li>
<li>操作write(字节数组,0,长度)</li>
</ol>
<h2 id="字符流"><a href="#字符流" class="headerlink" title="字符流"></a>字符流</h2><ol>
<li>字符流,只能处理纯文本</li>
<li>输入流 Reader FileReader</li>
<li>操作read(字符数组)</li>
<li>中间容器 byte[] flush = new byte[长度],接收长度 int len = 0,循环读取while(-1!=(len=流.read(flush)))。输出拷贝</li>
<li>输出流:Writer FileWriter</li>
<li>操作write(字节数组,0,长度)</li>
</ol>
<h2 id="字节流和字符流转换"><a href="#字节流和字符流转换" class="headerlink" title="字节流和字符流转换"></a>字节流和字符流转换</h2><ol>
<li>输入流 InputStreamReader 解码</li>
<li>输出流 OutputStreamWriter 编码</li>
</ol>
<h2 id="缓冲流"><a href="#缓冲流" class="headerlink" title="缓冲流"></a>缓冲流</h2><ol>
<li>输入流:BufferedInputStream BufferedReader</li>
<li>输出流:BufferedOutputStream BufferedWriter</li>
</ol>
<h2 id="处理数据-类型"><a href="#处理数据-类型" class="headerlink" title="处理数据+类型"></a>处理数据+类型</h2><ol>
<li>输入流:DataInputStream readXxx</li>
<li>输出流:DataOutputStream writeXxx</li>
<li>引用类型<br>3.1 反序列化 ObjectInputStream readObject<br>3.2 序列化 ObjectOutputStream wirteObject</li>
</ol>
<h2 id="打印流:PrintStream"><a href="#打印流:PrintStream" class="headerlink" title="打印流:PrintStream"></a>打印流:PrintStream</h2><h2 id="IO流分类"><a href="#IO流分类" class="headerlink" title="IO流分类"></a>IO流分类</h2><ol>
<li>基于字节操作的IO接口:InputStream. OutputStream</li>
<li>基于字符操作的IO接口:Reader. Writer</li>
<li>基于磁盘操作的IO接口:File</li>
<li>基于网络操作的IO接口:Socket</li>
</ol>
<h2 id="字节和字符转换"><a href="#字节和字符转换" class="headerlink" title="字节和字符转换"></a>字节和字符转换</h2><ol>
<li>InputStreamReader:从字节流转换成字符流,转换是需要指定编码格式,否则很容易出现乱码,StreamDecoder正是完成字节到字符的解码实现类。</li>
<li>OutputStreamWriter:从字符流转换成字节流,由StreamEncoder完成编码过程。</li>
</ol>
<h2 id="磁盘IO工作机制:"><a href="#磁盘IO工作机制:" class="headerlink" title="磁盘IO工作机制:"></a>磁盘IO工作机制:</h2><p>9.1几种访问文件的方式:</p>
<ol>
<li>标准访问文件方式:<br>read接口,首先读取用户地址空间的缓存是否存在,如果存在直接返回,如果不存在,读取内核空间的高速页缓存,如果没有则读取磁盘空间。<br>write接口,首先将数据从用户地址空间复制到内核地址空间的缓存中,这时对用户来说操作已经完成,至于什么时候写入磁盘由操作系统决定,也可以调用sync同步完成。</li>
<li>直接IO访问方式:就是应用程序直接访问磁盘,不经过操作系统内核数据缓存区,目的为了减少一次从内核缓冲区到用户缓冲区数据的复制。例如数据库管理系统。操作系统很难知道哪些是热点数据,操作系统<br>只是简单的缓存最近一次从磁盘读取的数据。直接IO访问数据,如果不在应用程序缓存中,那么每次直接从磁盘加载,会非常慢。<br>read接口:首先读取用户地址空间的缓存,如果有直接返回,没有则直接访问磁盘空间。<br>write接口:首先写入应用地址空间的缓存,然后再写入磁盘空间。</li>
<li>同步访问文件方式:同步访问文件的方式就是数据的读取和写入都是同步操作的,与标准访问文件方式不同的是,只有当数据被成功写入到磁盘时,才返回给应用程序成功的标识。<br>这种访问方式性能比较差,只有在一些对数据安全要求比较高的场景才使用。</li>
<li>异步访问文件方式:当访问数据的线程发出请求后,线程会继续处理其他事情,而不是阻塞等待,当请求的数据返回后继续处理下面的操作。这种访问方式明显提高应用程序的效率,但是不会改变访问文件的效率。</li>
<li>内存映射的方式:内存映射是操作系统将内存中的某一个区域与磁盘中的文件关联起来,当要访问内存中的一段数据时,转换成访问文件的某一段数据,这种方式的目的是同样减少数据从内核空间缓存到用户空间的数据<br>复制操作,因为这两个空间的数据是共享的。<br>9.2java访问磁盘文件:<br>java中通常的File并不代表是一个真是存在的文件对象,当你指定一个路径描述符时,它就会返回一个代表这个路径的虚拟对象,可能是一个真是存在的文件或者是一个包含多个文件的目录。FileDescriptor对象,就是<br>真正代表一个存在文件对象的描述。<br>9.3 JAVA序列化:</li>
<li>概念:将一个对象转换成一串二进制表示的字节数组,通过保存或者转意这些字节数据来达到持久化的目的,需要持久化就必须实现java.io.Serializable接口。反序列化时,必须有原始类作为模板,才能将对象还原。</li>
<li>java序列化的一些总结<br>2.1:当父类序列化时,所有子类都可以被序列化。<br>2.2:子类实现Serializable接口,父类没有,父类中的属性不会序列化(不报错,数据会丢失)<br>2.3:如果序列化的属性是对象,则这个对象必须实现Serializable接口,否则会报错。<br>2.4:在反序列化是,如果对象的属性有修改或者删除,则修改的部分属性会丢失,但是不会报错。<br>2.5:在反序列化时,如果serialVersionUID被修改,那么反序列化时会失败。</li>
</ol>
<h2 id="影响网络传输的因素:"><a href="#影响网络传输的因素:" class="headerlink" title="影响网络传输的因素:"></a>影响网络传输的因素:</h2><ol>
<li>网络带宽:一条物理链路上在1s内能够传输的最大比特数。</li>
<li>传输距离:也就是数据在光纤中要走的距离,数据在光纤中移动并不是直线,所以有一个折射率,大概是光的2/3,这段时间也就是我们常说的网络延迟。</li>
<li>TCP拥塞控制:</li>
</ol>
<h2 id="BIO-NIO-AIO"><a href="#BIO-NIO-AIO" class="headerlink" title="BIO. NIO. AIO"></a>BIO. NIO. AIO</h2><ol>
<li>IO的方式通常分为几种,同步阻塞的BIO. 同步非阻塞的NIO. 异步非阻塞的AIO</li>
<li>同步阻塞IO(JAVA BIO):<br>同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,<br>当然可以通过线程池机制改善。</li>
<li>同步非阻塞IO(Java NIO) :<br>同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。</li>
<li>异步阻塞IO(Java NIO):<br>此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,<br>那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select属于同步操作。<br>因为select之后,进程还需要读写数据),从而提高系统的并发性!</li>
<li>Java AIO(NIO.2))异步非阻塞IO:<br>在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,<br>因为真正的IO读取或者写入操作已经由内核完成了。</li>
</ol>
<h2 id="BIO-NIO-AIO适用场景分析"><a href="#BIO-NIO-AIO适用场景分析" class="headerlink" title="BIO. NIO. AIO适用场景分析:"></a>BIO. NIO. AIO适用场景分析:</h2><ol>
<li>BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 </li>
<li>NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 </li>
<li>AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。</li>
</ol>
<h1 id="jvm"><a href="#jvm" class="headerlink" title="jvm"></a>jvm</h1><h2 id="判断对象是否已死?"><a href="#判断对象是否已死?" class="headerlink" title="判断对象是否已死?"></a>判断对象是否已死?</h2><ol>
<li>引用计数法:给对象添加一个引用计数器,当对象被引用,加1,引用失效,减1,计数器的值为0,那么可以回收。缺陷:当2个对象相互引用时,导致2个计数器的值都不为0。</li>
<li>可达性分析:Gc roots。Gc roots包括的对象有 1 虚拟机栈中引用的对象。方法区中静态属性引用的对象,方法区中常量方法引用的对象。本地方法栈中,引用的对象。</li>
<li>引用包括,强引用. 软引用. 弱引用. 虚引用。强引用:对象被引用不能被回收。软引用:用来描述一些还有用但是并非必须的对象。当系统将要发生内存溢出的时候,将会把这些对象进行第二次回收。弱引用:弱引用用来描述非必须对系那个,但是它的强度比软引用更弱一些,被弱引用的对象只能生存到下一次垃圾搜集发生之前。无论内存是否足够都会被回收。虚引用:虚引用也被称为幽灵引用或者幻影引用,它是最弱的一种引用关系。为对象设置一个虚引用唯一的目的就是能在这个对象被回收时收到一个系统通知。</li>
</ol>
<h2 id="java的类被回收的条件:"><a href="#java的类被回收的条件:" class="headerlink" title="java的类被回收的条件:"></a>java的类被回收的条件:</h2><ol>
<li>该类所有的实例都已经被回收,java堆中不存在该类的任何实例。</li>
<li>加载该类的classloader已经被回收。</li>
<li>该类对应的java.lang.class对象没有任何地方被引用。</li>
</ol>
<h2 id="基本回收算法"><a href="#基本回收算法" class="headerlink" title="基本回收算法"></a>基本回收算法</h2><p> 1:引用计数:比较古老的回收算法。原理是此对象有一个引用,即增加一个技术,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法时致命的是无法处理循环引用的问题。<br> 2:标记-清除:此算法执行分为两个阶段。第一个阶段从引用根节点开始标记所有被引用的对象;第二个阶段便利整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。<br> 3:复制:此算法把内存空间划分为两个相同的区域,每次只是用其中的一个区域。垃圾回收时,便利当前使用区域,把正在使用中的对象复制到另外一个区域。此算法每次只处理正在使用中的对象,因此复制成本比较小,同事复制过去以后还能进行相应的内存整理,不过会出现碎片问题。当然,此算法的缺点也是很明显的,就是需要两倍的内存。<br> 4:标记-整理:此算法结合了“标记-清除”和”复制”两个算法的有点。也是分两个阶段,第一个阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象”压缩”到堆的其中一块,按顺序排放。此算法避免了”标记-清除”碎片问题,同时也避免了复制算法的空间问题。<br> 5:增量收集:实施垃圾回收算法,即应用进行的同时进行垃圾回收。<br> 6:分代算法:基于对象生命周期分析后得出的垃圾回收算法。把对象分为年轻代. 年老带. 持久代,对不同生命周期的对象使用不同的算法进行回收。</p>
<h2 id="SavePoint安全点和安全区"><a href="#SavePoint安全点和安全区" class="headerlink" title="SavePoint安全点和安全区"></a>SavePoint安全点和安全区</h2><ol>
<li>程序执行时并非所有的地方都能停顿下来开始GC,只有到达安全点才能停顿。SafePoint的选定既不讷讷个太少导致让GC等待时间太长,也不能过于频繁已至过分增加运行的负荷。安全点的选定基本上是已”是否具有让程序长时间执行的特征”为标准进行选定的。</li>
<li>让所有的线程到达安全的方案有2中:抢先式终端,和主动式中断。抢先式中断:不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现有的线程不在安全点上,就恢复线程,让它跑到安全点。主动式中断:当GC需要中断时,仅仅简单地这只一个标识,各个线程执行时主动去轮训这个标志。发现终端标志为真时,就将自己挂起。</li>
<li>安全区:在一段代码片段中,引用关系不会发生变化,在这个区域的任意地方开始GC都是安全的。例如,有些时候是程序不执行的时候,当线程处于Sleep状态或者Blocked状态,无法响应jvm的中段请求。当线程进入安全区以后,会标识自己已经进入安全区,当jvm要发起gc时,就不用管标识自己为safe region状态的线程了。</li>
</ol>
<h2 id="垃圾收集器"><a href="#垃圾收集器" class="headerlink" title="垃圾收集器"></a>垃圾收集器</h2><ol>
<li>Serial收集器<br>Serial收集器是一个单线程收集器,它在进行垃圾回收时,需要stop the world。</li>
<li>ParNew收集器<br>ParNew收集器是Serial的多线程版本。是Server模式下新生代默认的收集器,目前除了Serial收集器外只有它能与CMS收集器配合使用。</li>
<li>Parallel Scavenge收集器<br>是一款新生代收集器,它采用赋值算法,又是并行的多线程收集器。主要关注的是吞吐量。吞吐量=用户代码执行时间/用户代码执行时间+垃圾回收时间。Parallel Scavenge收集器提供了两个参数用于青雀控制吞吐量,分别是控制最大垃圾手机停顿时间和直接设置吞吐量大小的参数。</li>
<li>Serial Old收集器<br>Serial Old是Serial的老年代版本,同样是单线程收集器。主要作用是作为CMS收集器的备用方案。</li>
<li>Parallel Old收集器<br>Parallel Old收集器是 Parallel Scavenge的老年代版本,使用的是多线程和标记-整理算法。</li>
<li>CMS收集器<br>CMS:Concurrent Mark Sweep 收集器是一种以获取最短回收停顿时间为目的的收集器。CMS收集器是给予标记-清除算法实现的整个过程包括4个步骤:1初始标记2. 并发标记. 3. 重新标记. 4并发清除。CMS的有点是并发收集. 低停顿。</li>
<li>G1收集器<br>分代概念在G1中仍然得已保留。G1从整体来看是给予标记整理算法实现的收集器. 从局部上来看是给予复制算法实现的。G1在运作期间不会出现内存空间碎片,收集后能提供规整的可用内存。有利于程序长时间运行,分配大对象。<br>可预测的停顿,这是G1与CMS的另一大优势。降低停顿时间是G1和CMS共同关注点。G1除了追求低停顿外,还能建立可预测的停顿时间模型。能够让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾回收的时间不能超过N秒。G1跟踪各个Region里面的垃圾堆积的价值大小,在后台维护一个优先级列表。根据每次允许的收集时间,优先回收价值最大的Resion。G1收集器运作的步骤包括:初始标记. 并发标记. 最终标记. 筛选回收。</li>
</ol>
<h2 id="JVM中并行与并发的区别"><a href="#JVM中并行与并发的区别" class="headerlink" title="JVM中并行与并发的区别"></a>JVM中并行与并发的区别</h2><ol>
<li>并行:指多条垃圾收集线程并行狗牯脑做,但此时用户线程仍然处于等待状态。</li>
<li>并发:指用户线程与垃圾搜集线程同事执行,单不一定是并行,可能是交替执行。</li>
</ol>
<h2 id="基本回收算法-1"><a href="#基本回收算法-1" class="headerlink" title="基本回收算法"></a>基本回收算法</h2><p> 1:引用计数:比较古老的回收算法。原理是此对象有一个引用,即增加一个技术,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法时致命的是无法处理循环引用的问题。<br> 2:标记-清除:此算法执行分为两个阶段。第一个阶段从引用根节点开始标记所有被引用的对象;第二个阶段便利整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。<br> 3:复制:此算法把内存空间划分为两个相同的区域,每次只是用其中的一个区域。垃圾回收时,便利当前使用区域,把正在使用中的对象复制到另外一个区域。此算法每次只处理正在使用中的对象,因此复制成本比较小,同事复制过去以后还能进行相应的内存整理,不过会出现碎片问题。当然,此算法的缺点也是很明显的,就是需要两倍的内存。<br> 4:标记-整理:此算法结合了“标记-清除”和”复制”两个算法的有点。也是分两个阶段,第一个阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象”压缩”到堆的其中一块,按顺序排放。此算法避免了”标记-清除”碎片问题,同时也避免了复制算法的空间问题。<br> 5:增量收集:实施垃圾回收算法,即应用进行的同时进行垃圾回收。<br> 6:分代算法:基于对象生命周期分析后得出的垃圾回收算法。把对象分为年轻代. 年老带. 持久代,对不同生命周期的对象使用不同的算法进行回收。</p>
<h2 id="基于分代算法"><a href="#基于分代算法" class="headerlink" title="基于分代算法"></a>基于分代算法</h2><p> 1. 年轻代:年轻代分为三个区。一个Eden区,两个Survivor区。大部分对象都是在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象被复制到另外一个Survivor区,当这个Survivor区也满了时,会从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到老年代。需要注意的是Survivor区是对称的,没有先后关系,所以同一个区中可能同时存在从Eden复制过来的对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor过来的对象。而且Survivor总有一个是空的。<br> 2. 年老代:年老代存放从年轻带存货的对象。一般来说年老代都是存放生命周期较长的对象。<br> 3. 持久代:用于存放静态文件,例如java类. 方法等。持久代对垃圾回收没有显著的影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程总新增的类。持久代大小通过-XX:MAXPermSize进行设置。</p>
<h2 id="java运行时数据区域:"><a href="#java运行时数据区域:" class="headerlink" title="java运行时数据区域:"></a>java运行时数据区域:</h2><ol>
<li>程序计数器:当前线程所执行的字节码的行号指示器。</li>
<li>虚拟机栈:是线程私有的,它的生命周期和线程相同,每个方法在执行的时候会创建一个栈帧,用于存储变量表. 操作数栈. 方法出口. 动态连接等。</li>
<li>本地方法区:本地方法栈是为虚拟机使用到的native方法服务。</li>
<li>堆:是java虚拟机所管理的内存中最大的一块。java堆是所有线程共享的一块内存区域,目的是为了存放对象实例。</li>
<li>方法区:方法区也是各个线程共享的一块区域,用于存储虚拟机加载的类信息. 常量. 静态变量. 即时编译后的代码等。</li>
</ol>
<h1 id="kafka"><a href="#kafka" class="headerlink" title="kafka"></a>kafka</h1><ul>
<li>Kafka是一个高性能. 高可用. 可持久化的,为分布式设计的消息中间件。Kafka主要包括,consumer. broker. producer。</li>
<li>每一种消息的分类叫做topic,一个消息中间可以包括多个topic。为了分布式设计,每个主题又可以分成多个分区。每个分区都是一个顺序写入<br>,且不可变的文件。每个新的消息总是追加到文件末尾。每个主题可以多个分区,每个分区又可以分布到不同的机器上,所以实现了分布式系统的功能。<br>Kafka通过zk实现注册中心,将消息生产者,消费者,消息中介元数据存储到zk上,kafka也会将分区分配到不同机器。</li>
<li>对于消息发送到哪个分区,默认是轮训调度的方式,也可以根据自己的业务场景,进行定制实现消息分区的逻辑。</li>
<li>每个分区的消费都是顺序的,但是跨分区的消息不保证顺序性。kafka的消费者提供了GroupId的功能,同时只能拥有同样的GroupId的消费者,<br>消费一个分区。消息中间件分为queue和topic两种模式,如果每个消费者拥有相同的groupId,那么这个主题就被当做为queue方式消费,因为不会<br>有消费者消费相同的消息。如果每个消费者拥有不同的GroupId,那么这个主题会被当做Topic模式消费,每个消费者都会消费一遍这个消息。</li>
<li>消息的消费者使用标准的推送push模型,将消息推送到消息中介,但是消费者不同,消费者使用拉取pull模型,主动的将消息从消息中介拉取<br>到客户端,这样消息中介不需要维护消息的状态。为了提升吞吐量可以批量拉取。</li>
<li>kafka的消息中介不会存储消息状态,而是把消费到哪里的偏移量存储到客户端,并且同步到zk,可以选择批量同步,这样可以提升吞吐量,风险<br>是如果消费者崩溃,未同步的消息偏移量将导致重复消费。系统提供了3种可能消费的传递保障方式,至多一次,至少一次,仅仅一次。至多一次,无论<br>消费者是否成功,消息不会在重新推送。至少一次,消息被消费时,如果由于网络原因导致重复消费,需要业务实现幂等性,无论消费多少次都是相同<br>的结果。仅仅一次,如果消息已经被消费,再次消费消息的时候会回滚。Kafka由于设计的理念不同,只支持至少一次。</li>
<li>kafka的存储方式,kafka直接使用磁盘进行存储,没有使用缓存,由于操作系统本身就有缓存,先将数据放入缓存,经过一定时间再刷盘。<br>Kafka最大限度的利用了操作系统的缓存,所以如果想提升kafka的性能,最好使用固态硬盘。Kafka是追加数据顺序读,不会随机读写,所以及时<br>直接使用磁盘效率也很高。如果不想丢弃消息,可以修改kafka的配置参数,每发送一条消息就同步一次磁盘,用降低性能的方式提高安全性。kafka<br>会自动清理过期的消息,默认保留1个星期。</li>
<li>kafka收到消息后会向主节点和从节点发送消息。主节点用于接收和消费消息,从节点用于同步消息,kafka的主从节点不是以服务器为粒度,而<br>是以topic主体为粒度,这样每台机器都会有主从分区,最大限度的利用了机器资源。</li>
</ul>
<h1 id="lock"><a href="#lock" class="headerlink" title="lock"></a>lock</h1><ol>
<li>Lock能完成几乎所有synchronized的功能,并有一些后者不具备的功能,如锁投票. 定时锁等候. 可中断锁等候等。</li>
<li>synchronized 是Java 语言层面的,是内置的关键字;Lock 则是JDK 5中出现的一个包,在使用时,synchronized 同步的代码块可以由JVM自动释放;<br>Lock 需要程序员在finally块中手工释放,如果不释放,可能会引起难以预料的后果</li>
</ol>
<h2 id="主内存和工作内存:"><a href="#主内存和工作内存:" class="headerlink" title="主内存和工作内存:"></a>主内存和工作内存:</h2><ol>
<li>Java内存模型中规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存,线程的工作内存,保存了该线程使用到的主内存副本拷贝,线程对变量的所有操作必须在工作内存中,而不能直接读写主内存中的变量,<br>不同线程之间无法直接访问对方工作内存中的变量,线程间值的传递均需要通过主内存来完成。</li>
</ol>
<h2 id="什么是自旋锁:"><a href="#什么是自旋锁:" class="headerlink" title="什么是自旋锁:"></a>什么是自旋锁:</h2><p>自旋锁jdk1.6后默认自动开启,基于之前的观察,共享数据的锁定状态只会持续很短的时间,为了这一段时间而去挂起和恢复线程有点浪费,然后面请求锁的那个线程稍等一会,但是不放弃处理器的执行时间,看看只有线程的锁<br>是否能很快释放。为了让线程等待,所以需要让线程执行一个忙循环自旋操作。</p>
<h2 id="锁消除"><a href="#锁消除" class="headerlink" title="锁消除"></a>锁消除</h2><p>虚拟机即使编译器在运行时,对于代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。如果判断一段代码,在堆上的数据都不会逃逸,被其他线程访问到,那么认为是线程私有的,同步加锁也就没有必要了。</p>
<h2 id="锁粗化"><a href="#锁粗化" class="headerlink" title="锁粗化"></a>锁粗化</h2><p>原则上,在编写代码时,推荐将同步块的作用范围限制的尽量小,仅仅在共享数据的实际作用域才进行同步,这样目的是使得需要同步的操作尽可能小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。但是如果一系列的连续<br>操作都对同一个对象反复加锁和解锁,甚至锁出现在循环体内,及时没有线程竞争,频繁的进行互斥操作也会带来性能损耗。</p>
<h2 id="偏向锁"><a href="#偏向锁" class="headerlink" title="偏向锁"></a>偏向锁</h2><p>偏向锁意思是这个锁会偏向第一个获取它的线程,如果在接下来的过程中,该锁没有被其他线程获取,则只有偏向锁的线程将永远不需要进行同步,偏向锁可以提高带有同步但是没有竞争的程序的性能。偏向锁也不一定总是对程序<br>有利的,如果程序中的锁大部分都是被多个不同的线程访问,那么偏向模式就是多余的。</p>
<h2 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a>轻量级锁</h2><p>轻量级锁能提升程序同步性能的依据是”对于绝大部分的锁,在整个同步周期内都是不存在竞争”,这是一个经验值。如果没有竞争,轻量级锁使用CAS操作避免了使用互斥所的开销,但如果存在竞争,除了互斥锁的开销外,还额外<br>发生了CAS操作,一次在竞争的情况下,轻量级锁会比传统重量级锁更慢。</p>
<h2 id="volatile变量"><a href="#volatile变量" class="headerlink" title="volatile变量"></a>volatile变量</h2><p>volatile能够实现可见性,但是不能保证原子性。</p>
<h2 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h2><p>CAS包括三个操作数,内存位置,预期旧值,新值。如果内存位置的值和预期的旧值相同,那么使用新值替换旧值。</p>
<h2 id="如何实现互斥同步?"><a href="#如何实现互斥同步?" class="headerlink" title="如何实现互斥同步?"></a>如何实现互斥同步?</h2><p>java中最基本的互斥就是synchronized关键字,synchronized在经过编译后,会在同步块的前后分别形成monitorenter和moitorexit这两个字节码指令。在执行monitorenter指令时,首先要去尝试获取对象的锁,<br>如果这个对象没有被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的,在执行monitorexit指令时会把锁计数器减1,当计数器为0时,锁就被释放了。如果获取对象的锁失败,当当前线程就要阻塞等待,<br>知道对象的锁被另一个线程释放为止。synchronized对于同一个线程来说是可重入的,不会出现自己把自己锁死的问题。除了synchronized指望,JUC中的Lock也能实现互斥同步,ReentrantLock,写法上更加可见,<br>lock和unlock配合try/finally来配合完成,ReentrantLock比synchronized有几个高级的特性。</p>
<h2 id="ReentrantLock的高级特性有那几个?"><a href="#ReentrantLock的高级特性有那几个?" class="headerlink" title="ReentrantLock的高级特性有那几个?"></a>ReentrantLock的高级特性有那几个?</h2><ol>
<li>等待可中断,当持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,改为处理其他事情;</li>
<li>可以实现公平锁,公平锁指多个线程在等待同一个锁时,必须按照申请锁的顺序依次获得锁,synchronized是非公平锁,ReentrantLock默认也是非公平的,只不过可以通过构造函数来制定实现公平锁;</li>
<li>锁绑定多个条件,ReentrantLock对象可以同时绑定多个Condition对象,在synchronized中,锁对象的wait/notify/notifyall方法可以实现一个隐含的条件,如果要多一个条件关联的时候,就需要额外的增加一个锁;</li>
</ol>
<h2 id="关于sunchronized的几个注意点?"><a href="#关于sunchronized的几个注意点?" class="headerlink" title="关于sunchronized的几个注意点?"></a>关于sunchronized的几个注意点?</h2><ol>
<li>当一个线程访问object的一个synchronized(this)同步代码块时, 另一个线程仍然可以访问该object中的非synchronized(this)同步代码块;</li>
<li>当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时, 一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块;</li>
<li>尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时, 其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞;</li>
<li>Java中的每一个对象都可以作为锁,对于同步方法,锁是当前实例对象,对于静态同步方法,锁是当前对象的Class对象,对于同步方法块,锁是Synchonized括号里配置的对象;</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">## JAVA concurrent包下的类:</span><br><span class="line">Executors</span><br><span class="line"> Executor</span><br><span class="line"> ExecutorService</span><br><span class="line"> ScheduledExecutorService</span><br><span class="line"> Callable</span><br><span class="line"> Future</span><br><span class="line"> ScheduledFuture</span><br><span class="line"> Delayed</span><br><span class="line"> CompletionService</span><br><span class="line"> ThreadPoolExecutor</span><br><span class="line"> ScheduledThreadPoolExecutor</span><br><span class="line"> AbstractExecutorService</span><br><span class="line"> Executors</span><br><span class="line"> FutureTask</span><br><span class="line"> ExecutorCompletionService</span><br><span class="line">Queues</span><br><span class="line"> BlockingQueue</span><br><span class="line"> ConcurrentLinkedQueue</span><br><span class="line"> LinkedBlockingQueue</span><br><span class="line"> ArrayBlockingQueue</span><br><span class="line"> SynchronousQueue</span><br><span class="line"> PriorityBlockingQueue</span><br><span class="line"> DelayQueue</span><br><span class="line">Concurrent Collections</span><br><span class="line"> ConcurrentMap</span><br><span class="line"> ConcurrentHashMap</span><br><span class="line"> CopyOnWriteArray{List,Set</span><br><span class="line">Synchronizers</span><br><span class="line"> CountDownLatch</span><br><span class="line"> Semaphore</span><br><span class="line"> Exchanger</span><br><span class="line"> CyclicBarrier</span><br><span class="line">Timing</span><br><span class="line"> TimeUnit</span><br><span class="line">Locks</span><br><span class="line"> Lock</span><br><span class="line"> Condition</span><br><span class="line"> ReadWriteLock</span><br><span class="line"> AbstractQueuedSynchronizer</span><br><span class="line"> LockSupport</span><br><span class="line"> ReentrantLock</span><br><span class="line"> ReentrantReadWriteLock</span><br><span class="line">Atomics</span><br><span class="line"> Atomic[Type], Atomic[Type]Array</span><br><span class="line"> Atomic[Type], FieldUpdater</span><br><span class="line"> Atomic{Markable, Stampable}Reference}</span><br></pre></td></tr></table></figure>
<h2 id="原子性-可见性-有序性"><a href="#原子性-可见性-有序性" class="headerlink" title="原子性. 可见性. 有序性"></a>原子性. 可见性. 有序性</h2><ol>
<li>原子性:由java内存模型来直接保证原子性变量操作包括:lock. unlock. read. load. assign. use. store和write 。</li>
<li>可见性:当一个线程修改了共享变量的值,其他线程能够立即知道这个修改。java内存模型是通过在变量修改后将新值同步回<br>主内存,在变量读取之前需要从主内存刷新变量值这种依赖猪内存作为传递媒介的方式来实现的可见性。除了volatitle关键字外,<br>还有2个synchronized. final。</li>
<li>有序性:java提供了volatitle和synchronized关键字来保证线程之间操作的有序性。</li>
</ol>
<h1 id="mongodb"><a href="#mongodb" class="headerlink" title="mongodb"></a>mongodb</h1><h2 id="MongoDB简介"><a href="#MongoDB简介" class="headerlink" title="MongoDB简介"></a>MongoDB简介</h2><ol>
<li>MongoDB是一个开源的. 面向文档的存储的NoSql数据库。</li>
<li>模式自由. 支持动态查询. 完全索引. 可以轻易查询文档中内嵌的对象及数组。</li>
<li>面向文档存储,易存储对象类型的数据。</li>
<li>高效的文档存储,支持二进制数据及大型对象。</li>
<li>支持复制和故障恢复</li>
<li>自动分片支持云级别的伸缩性,支持水平的数据库集群。</li>
</ol>
<h2 id="特点"><a href="#特点" class="headerlink" title="特点:"></a>特点:</h2><p>高性能. 易部署. 易使用,存储数据非常方便。主要功能特性有:<br>面向集合存储,易存储对象类型的数据。<br>模式自由。<br>支持动态查询。<br>支持完全索引,包含内部对象。<br>支持查询。<br>支持复制和故障恢复。<br>使用高效的二进制数据存储,包括大型对象(如视频等)。<br>自动处理碎片,以支持云计算层次的扩展性<br>支持Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++语言的驱动程序,社区中也提供了对Erlang及.NET等平台的驱动程序。<br>文件存储格式为BSON(一种JSON的扩展)。<br>可通过网络访问。</p>
<h2 id="功能"><a href="#功能" class="headerlink" title="功能:"></a>功能:</h2><p>面向集合的存储:适合存储对象及JSON形式的数据。<br>动态查询:Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。<br>完整的索引支持:包括文档内嵌对象及数组。Mongo的查询优化器会分析查询表达式,并生成一个高效的查询计划。<br>查询监视:Mongo包含一个监视工具用于分析数据库操作的性能。<br>复制及自动故障转移:Mongo数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移。<br>高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)<br>自动分片以支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器。</p>
<h2 id="适用场合"><a href="#适用场合" class="headerlink" title="适用场合:"></a>适用场合:</h2><p>网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。<br>缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源 过载。<br>大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。<br>高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。<br>用于对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询。</p>
<h2 id="与传统数据库对比"><a href="#与传统数据库对比" class="headerlink" title="与传统数据库对比"></a>与传统数据库对比</h2><p>MongoDb是由数据库databse. 集合collection. 文档对象document三个层次组成。</p>
<h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><p>MongoDB提供了多样性的索引支持,索引信息被保存在sytem.indexes中,且默认为_id创建索引,它的索引使用基本和mysql一样。</p>
<h1 id="mysql"><a href="#mysql" class="headerlink" title="mysql"></a>mysql</h1><h2 id="Mysql主要存储引擎包括InnoDB-MyISAM"><a href="#Mysql主要存储引擎包括InnoDB-MyISAM" class="headerlink" title="Mysql主要存储引擎包括InnoDB. MyISAM"></a>Mysql主要存储引擎包括InnoDB. MyISAM</h2><h2 id="InnoDB和MyISAM的区别:"><a href="#InnoDB和MyISAM的区别:" class="headerlink" title="InnoDB和MyISAM的区别:"></a>InnoDB和MyISAM的区别:</h2><ol>
<li>MyISAM不支持事物,表锁,支持全文检索,不支持外键,表空间相对比较小,关注的主要是查询的性能。</li>
<li>InnoDB支持事物,行锁,不支持全文检索,支持外检,表空间相对比较大,关注的是事务。</li>
<li>MyISAM的读性能比InnoDB强。</li>
</ol>
<h2 id="数值类型:"><a href="#数值类型:" class="headerlink" title="数值类型:"></a>数值类型:</h2><ol>
<li>tinyint 1个字节</li>
<li>smallint 2个字节</li>
<li>mediumint 3个字节</li>
<li>int 4个字节</li>
<li>bigint 8个字节</li>
<li>float 4个字节</li>
<li>double 8个字节</li>
<li>bit 1-8个字节</li>
</ol>
<h2 id="日期类型"><a href="#日期类型" class="headerlink" title="日期类型"></a>日期类型</h2><ol>
<li>如果表示年月日用date</li>
<li>如果标示年月日时分秒,用datetime和timestamp</li>
<li>如果表示十分秒,用time</li>
<li>如果表示年,用year</li>
</ol>
<h2 id="datetime和timestamp的区别:"><a href="#datetime和timestamp的区别:" class="headerlink" title="datetime和timestamp的区别:"></a>datetime和timestamp的区别:</h2><ol>
<li>timestamp支持的时间范围比较小,从1970年到2038年的某个时间。</li>
<li>timestamp的查询和插入受当地时区的影响。</li>
</ol>
<h2 id="char-varchar-binary-varbinary-blob-text-enum和set"><a href="#char-varchar-binary-varbinary-blob-text-enum和set" class="headerlink" title="char. varchar. binary. varbinary. blob. text. enum和set"></a>char. varchar. binary. varbinary. blob. text. enum和set</h2><ol>
<li>char的固定长度是0-255,且会去掉末尾的空格</li>
<li>varchar是非固定的,会保留末尾的空格</li>
</ol>
<h2 id="数据库隔离级别"><a href="#数据库隔离级别" class="headerlink" title="数据库隔离级别"></a>数据库隔离级别</h2><ol>
<li>数据库隔离级别包括:读未提交. 读提交. 重复读. 序列化。</li>
<li>数据库各种隔离级别出现的问题:脏读. 不可重复度. 幻读</li>
<li>读未提交:脏读. 不可重复读. 幻读</li>
<li>读提交:不可重复读. 幻读</li>
<li>重复度:幻读</li>
<li>序列化:不会出现问题</li>
</ol>
<h2 id="各种隔离级别出现的问题"><a href="#各种隔离级别出现的问题" class="headerlink" title="各种隔离级别出现的问题"></a>各种隔离级别出现的问题</h2><ol>
<li>脏读:一个事务读取了另外一个事务未提交的数据,而这个数据有可能回滚。</li>
<li>不可重复读:在访问数据库时,一个事务范围内的两次查询,返回了不同的结果。这是由于查询时系统中其他事务修改的提交而引起的。</li>
<li>幻读:是指事务不是独立执行时发生的一种现象。,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样.</li>
</ol>
<ul>
<li><p>小数类型为 decimal,禁止使用 float 和 double。<br>说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。</p>
</li>
<li><p>如果存储的字符串长度几乎相等,使用 char 定长字符串类型。<br>varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长 度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。</p>
</li>
<li><p>字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循:<br>1)不是频繁修改的字段。<br>2)不是 varchar 超长字段,更不能是 text 字段。</p>
</li>
<li><p>单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。</p>
</li>
<li><p>合适的字符存储长度,不但节约数据库表空间. 节约索引存储,更重要的是提升检 索速度。</p>
</li>
<li><p>业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。<br>说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明 显的;另外,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,根据墨菲定律, 必然有脏数据产生。</p>
</li>
<li><p>超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询 时,保证被关联的字段需要有索引。<br>说明:即使双表 join 也要注意表索引. SQL 性能。</p>
</li>
<li><p>在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据 实际文本区分度决定索引长度。<br>说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分 度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度 来确定。</p>
</li>
<li><p>页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索 引。</p>
</li>
<li><p>如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合 索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。<br>正例:where a=? and b=? order by c; 索引:a_b_c 反例:索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。</p>
</li>
<li><p>利用覆盖索引来进行查询操作,来避免回表操作。<br>说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览 一下就好,这个目录就是起到覆盖索引的作用。<br>正例:能够建立索引的种类:主键索引. 唯一索引. 普通索引,而覆盖索引是一种查询的一种 效果,用explain的结果,extra列会出现:using index</p>
</li>
<li><p>利用延迟关联或者子查询优化超多分页场景。<br>说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过 特定阈值的页数进行 SQL 改写。<br>正例:先快速定位需要获取的 id 段,然后再关联:<br>SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id</p>
</li>
<li><p>SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。<br>说明:<br>1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 2)ref 指的是使用普通的索引(normal index)。<br>3)range 对索引进行范围检索。<br>反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级 别比较 range 还低,与全表扫描是小巫见大巫。</p>
</li>
<li><p>建组合索引的时候,区分度最高的在最左边。<br>正例:如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即 可。<br>说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where a>? and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。</p>
</li>
<li><p>创建索引时避免有如下极端误解:<br>1)误认为一个查询就需要建一个索引。 2)误认为索引会消耗空间. 严重拖慢更新和新增速度。 3)误认为唯一索引一律需要在应用层通过“先查后插”方式解决。</p>
</li>
<li><p>count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行</p>
</li>
<li><p>当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题<br>正例:可以使用如下方式来避免sum的NPE问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;</p>
</li>
<li><p>使用 ISNULL()来判断是否为 NULL 值。注意:NULL 与任何值的直接比较都为 NULL。 说明:</p>
</li>
</ul>
<ol>
<li>NULL<>NULL的返回结果是NULL,而不是false。 2) NULL=NULL的返回结果是NULL,而不是true。 3) NULL<>1的返回结果是NULL,而不是true。</li>
</ol>
<h1 id="netty"><a href="#netty" class="headerlink" title="netty"></a>netty</h1><h2 id="为什么使用Netty"><a href="#为什么使用Netty" class="headerlink" title="为什么使用Netty?"></a>为什么使用Netty?</h2><ol>
<li>Netty提供了这样的一个间接的解决方法。Netty提供了高层次的抽象来简化TCP和UDP服务器的编程,但是你仍然可以使用底层地API。</li>
<li>Netty成功的提供了易于开发,高性能和高稳定性,以及较强的扩展性</li>
</ol>
<h2 id="Design-设计"><a href="#Design-设计" class="headerlink" title="Design(设计)"></a>Design(设计)</h2><p>各种传输类型,阻塞和非阻塞套接字统一的API<br>使用灵活<br>简单但功能强大的线程模型<br>无连接的DatagramSocket支持<br>链逻辑,易于重用</p>
<h2 id="Ease-of-Use-易于使-用"><a href="#Ease-of-Use-易于使-用" class="headerlink" title="Ease of Use(易于使 用)"></a>Ease of Use(易于使 用)</h2><p>提供大量的文档和例子<br>除了依赖jdk1.6+,没有额外的依赖关系。某些功能依赖jdk1.7+,其他特性可能有相 关依赖,但都是可选的。</p>
<h2 id="Performance-性能"><a href="#Performance-性能" class="headerlink" title="Performance(性能)"></a>Performance(性能)</h2><p>比Java APIS更好的吞吐量和更低的延迟<br>因为线程池和重用所有消耗较少的资源<br>尽量减少不必要的内存拷贝</p>
<h2 id="Robustness-鲁棒性"><a href="#Robustness-鲁棒性" class="headerlink" title="Robustness(鲁棒性)"></a>Robustness(鲁棒性)</h2><p>链接快或慢或超载不会导致更多的OutOfMemoryError<br>在高速的网络程序中不会有不公平的read/write</p>
<h2 id="Security-安全性"><a href="#Security-安全性" class="headerlink" title="Security(安全性)"></a>Security(安全性)</h2><p>完整的SSL/TLS和StartTLS支持<br>可以在如Applet或OSGI这些受限制的环境中运行</p>
<h2 id="Community-社区"><a href="#Community-社区" class="headerlink" title="Community(社区)"></a>Community(社区)</h2><p>版本发布频繁<br>社区活跃</p>
<h1 id="network"><a href="#network" class="headerlink" title="network"></a>network</h1><h2 id="Netty-解决TCP粘包-x2F-拆包问题"><a href="#Netty-解决TCP粘包-x2F-拆包问题" class="headerlink" title="Netty 解决TCP粘包/拆包问题"></a>Netty 解决TCP粘包/拆包问题</h2><p>业务上一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。<br>对于这类问题,netty提供了LineBasedFrameDecoder和StringDecoder进行完美解决。如下是相关代码。</p>
<ol>
<li>LineBasedFrameDecoder<br>工作原理是依次遍历ByteBuf中的可读字节,判断是否有换行符,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。</li>
<li>StringDecoder<br>功能就是将接受到的对象转换为字符串,然后继续调用后面的handler。<br>LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器。</li>
<li>其他解码器<br>DelimiterBasedFrameDecoder可以自定义分隔符<br>FixedLengthFrameDecoder定长解码器。</li>
</ol>
<h1 id="nginx"><a href="#nginx" class="headerlink" title="nginx"></a>nginx</h1><h2 id="Nginx能做什么?"><a href="#Nginx能做什么?" class="headerlink" title="Nginx能做什么?"></a>Nginx能做什么?</h2><ol>
<li>反向代理</li>
<li>负载均衡</li>
<li>HTTP服务器(包含动静分离)</li>
<li>正向代理</li>
</ol>
<h2 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h2><p>反向代理是指代理服务器接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器得到的结果返回给internet上请求连接的客户端,此时代理服务器<br>对外表现为一个反向代理服务器。简单的说就是真是的服务器不能直接被外部网络访问,需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真是服务器在同一个网络环境。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">server { </span><br><span class="line"> listen 80; </span><br><span class="line"> server_name localhost; </span><br><span class="line"> client_max_body_size 1024M;</span><br><span class="line"> </span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://localhost:8080;</span><br><span class="line"> proxy_set_header Host :;</span><br><span class="line"> </span><br><span class="line">}}</span><br></pre></td></tr></table></figure>
<p>保存配置文件后启动Nginx,这样当我们访问localhost的时候,就相当于访问localhost:8080了</p>
<h2 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h2><ol>
<li>RR 默认<br>每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">upstream test {</span><br><span class="line"> server localhost:8080;</span><br><span class="line"> server localhost:8081;</span><br><span class="line"> </span><br><span class="line"> server {</span><br><span class="line"> listen 81; </span><br><span class="line"> server_name localhost; </span><br><span class="line"> client_max_body_size 1024M;</span><br><span class="line"> </span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://test;</span><br><span class="line"> proxy_set_header Host :;</span><br><span class="line"> }</span><br><span class="line">}}</span><br></pre></td></tr></table></figure>
<p>负载均衡的核心代码为</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">upstream test {</span><br><span class="line"> server localhost:8080;</span><br><span class="line"> server localhost:8081;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>这里我配置了2台服务器,当然实际上是一台,只是端口不一样而已,而8081的服务器是不存在的,也就是说访问不到,但是我们访问<a href="http://localhost/">http://localhost</a> 的时候,也不会有问题,会默认跳转到<a href="http://localhost:8080/">http://localhost:8080</a><br>具体是因为Nginx会自动判断服务器的状态,如果服务器处于不能访问(服务器挂了),就不会跳转到这台服务器,所以也避免了一台服务器挂了影响使用的情况,由于Nginx默认是RR策略,所以我们不需要其他更多的设置。</p>
<ol start="2">
<li>权重<br>指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 例如</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">upstream test {</span><br><span class="line"> server localhost:8080 weight=9;</span><br><span class="line"> server localhost:8081 weight=1;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>那么10次一般只会有1次会访问到8081,而有9次会访问到8080</p>
<ol start="3">
<li>ip_hash<br>上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">upstream test {</span><br><span class="line"> ip_hash;</span><br><span class="line"> server localhost:8080;</span><br><span class="line"> server localhost:8081;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>fair(第三方)<br>按后端服务器的响应时间来分配请求,响应时间短的优先分配。</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">upstream backend {</span><br><span class="line"> fair;</span><br><span class="line"> server localhost:8080;</span><br><span class="line"> server localhost:8081;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<ol start="5">
<li>url_hash(第三方)<br>按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">upstream backend {</span><br><span class="line"> hash ;</span><br><span class="line"> hash_method crc32;</span><br><span class="line"> server localhost:8080;</span><br><span class="line"> server localhost:8081;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>以上5种负载均衡各自适用不同情况下使用,所以可以根据实际情况选择使用哪种策略模式,不过fair和url_hash需要安装第三方模块才能使用,由于本文主要介绍Nginx能做的事情,所以Nginx安装第三方模块不会再本文介绍</p>
<h2 id="HTTP服务器"><a href="#HTTP服务器" class="headerlink" title="HTTP服务器"></a>HTTP服务器</h2><p>Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,<br>首先看看Nginx做静态资源服务器</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 80; </span><br><span class="line"> server_name localhost; </span><br><span class="line"> client_max_body_size 1024M;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> location / {</span><br><span class="line"> root e:wwwroot;</span><br><span class="line"> index index.html;</span><br><span class="line"> </span><br><span class="line"> }}</span><br></pre></td></tr></table></figure>
<p>这样如果访问<a href="http://localhost/">http://localhost</a> 就会默认访问到E盘wwwroot目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。</p>
<h2 id="动静分离"><a href="#动静分离" class="headerlink" title="动静分离"></a>动静分离</h2><p>动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">upstream test{ </span><br><span class="line"> server localhost:8080; </span><br><span class="line"> server localhost:8081; </span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> server { </span><br><span class="line"> listen 80; </span><br><span class="line"> server_name localhost; </span><br><span class="line"> </span><br><span class="line"> location / { </span><br><span class="line"> root e:wwwroot; </span><br><span class="line"> index index.html; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> # 所有静态请求都由nginx处理,存放目录为html </span><br><span class="line"> location ~ .(gif|jpg|jpeg|png|bmp|swf|css|js)$ { </span><br><span class="line"> root e:wwwroot; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> # 所有动态请求都转发给tomcat处理 </span><br><span class="line"> location ~ .(jsp|do)$ { </span><br><span class="line"> proxy_pass http://test; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> error_page 500 502 503 504 /50x.html; </span><br><span class="line"> location = /50x.html { </span><br><span class="line"> root e:wwwroot; </span><br><span class="line"> } </span><br><span class="line"> } }</span><br></pre></td></tr></table></figure>
<p>这样我们就可以吧HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求,例如当我们后缀为gif的时候,<br>Nginx默认会从wwwroot获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活</p>
<h2 id="正向代理"><a href="#正向代理" class="headerlink" title="正向代理"></a>正向代理</h2><p>正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。当你需要把你的服务器作为代理服务器的时候,可以用Nginx来实现正向代理,但是目前Nginx有一个问题,那么就是不支持HTTPS,虽然我百度到过配置HTTPS的正向代理,但是到最后发现还是代理不了,当然可能是我配置的不对,所以也希望有知道正确方法的同志们留言说明一下。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">resolver 114.114.114.114 8.8.8.8;</span><br><span class="line">server {</span><br><span class="line"> </span><br><span class="line"> resolver_timeout 5s;</span><br><span class="line"> </span><br><span class="line"> listen 81;</span><br><span class="line"> </span><br><span class="line"> access_log e:wwwrootproxy.access.log;</span><br><span class="line"> error_log e:wwwrootproxy.error.log;</span><br><span class="line"> </span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://;</span><br><span class="line"> </span><br><span class="line">}}</span><br></pre></td></tr></table></figure>
<p>resolver是配置正向代理的DNS服务器,listen 是正向代理的端口,配置好了就可以在ie上面或者其他代理插件上面使用服务器ip+端口号进行代理了。</p>
<h2 id="Nginx热部署"><a href="#Nginx热部署" class="headerlink" title="Nginx热部署"></a>Nginx热部署</h2><p>Nginx是支持热启动的,也就是说当我们修改配置文件后,不用关闭Nginx,就可以实现让配置生效,当然我并不知道多少人知道这个,反正我一开始并不知道,导致经常杀死了Nginx线程再来启动。。。Nginx从新读取配置的命令是<br>nginx -s reload<br>windows下面就是<br>nginx.exe -s reload </p>
<h1 id="redis"><a href="#redis" class="headerlink" title="redis"></a>redis</h1><h2 id="redis数据类型"><a href="#redis数据类型" class="headerlink" title="redis数据类型"></a>redis数据类型</h2><ol>
<li>string. list. set. zset. hash</li>
<li>String是最简单的类型,一个key对应一个value</li>
<li>list是一个链表,主要包括push. pop,获取一定范围内的所有值,操作中key为链表的名称</li>
<li>set是集合,对集合的操作包括,新增. 删除. 合并求交集等,操作中的key为集合的名称。</li>
<li>zSet是set的一个升级版本,在set的基础上增加了顺序的功能,这个属性在添加删除的时候可以指定,每次指定后,zSet会自动重新按照<br>新的值调整顺序。</li>
<li>Hash数据类型允许用户使用redis对象类型,当你存储的数据对象只有很少几个key值时,数据存储的消耗会很小。</li>
</ol>
<h2 id="Redis的持久化方式:"><a href="#Redis的持久化方式:" class="headerlink" title="Redis的持久化方式:"></a>Redis的持久化方式:</h2><ol>
<li>RDB方式:默认redis会以快照的形式存储数据持久化到硬盘中,在配置文件中的格式是 save N M,标识在N秒的时间内,redis至少发生<br>M次修改,则将redis抓去快照到磁盘。工作原理:当redis需要做持久化时,只需要fork一个子进程,子进程将数据写入到磁盘上的一个临时<br>文件RDB中,当子进程完成后,将原来的RDB文件替换掉,这样的好处是copy-on-write。</li>
<li>AOF方式:append only file,文件日志追加,当开启后,redis每执行一次修改数据的命令后,都会把它添加到aof文件中,当redis<br>重启时,进行”重放”,以恢复redis关闭前的最后时刻。AOF的三中方式:每提交一个修改命令都刷新到aof中,非常慢,但是很安全。每秒中<br>刷盘一次,很快,但是可能会丢失一秒中的数据。第三种是依赖操作系统的缓存进行刷新,最快,但是安全性最差。</li>
</ol>
<h2 id="Redis各种特征的试用场景"><a href="#Redis各种特征的试用场景" class="headerlink" title="Redis各种特征的试用场景"></a>Redis各种特征的试用场景</h2><ol>
<li>Strings<br>Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。<br>setnx 设置可以为对应的值为String类型的value,如果key存在返回0不覆盖,不存在返回1<br>常规key-value缓存应用。<br>常规计数: 微博数, 粉丝数</li>
<li>Hashs<br>在Memcached中,我们经常将一些结构化的信息打包成hashmap,在客户端序列化后存储为一个字符串的值,比如用户的昵称. 年龄. 性别. 积分等,这时候在需要修改其中某一项时,<br>通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。<br>而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。<br>它是一个String类型的field和value的映射表,它的添加和删除都是平均的,hash特别适合用于存储对象,对于将对象存储成字符串而言,hash会占用更少的内存,<br>并且可以更方便的存取整个对象. 它和java的HashMap完全类似<br>使用场景<br>存储部分变更数据<br>如用户信息等。</li>
<li>Lists<br>Lists 就是链表,略有数据结构知识的人都应该能理解其结构。使用Lists结构,我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,<br>然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api,你可以直接查询,删除Lists中某一段的元素。<br>Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样List即可以作为栈,也可以作为队列。<br>消息队列系统<br>使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。<br>比如:将Redis用作日志收集器<br>实际上还是一个队列,多个端点将日志信息写入Redis,然后一个worker统一将所有日志写到磁盘。<br>取最新N个数据的操作<br>记录前N个最新登陆的用户Id列表,超出的范围可以从数据库中获得。<br>//把当前登录人添加到链表里<br>ret = r.lpush(“login:last_login_times”, uid)<br>//保持链表只有N位<br>ret = redis.ltrim(“login:last_login_times”, 0, N-1)<br>//获得前N个最新登陆的用户Id列表<br>last_login_list = r.lrange(“login:last_login_times”, 0, N-1)<br>比如sina微博:<br>在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。但是我们做了限制不能超过5000个ID,因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库。<br>我们的系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,而主页或第一个评论页是不会麻烦到硬盘上的数据库了。<br>Sets<br>Sets 就是一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据。<br>案例:<br>在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集. 并集. 差集等操作,可以非常方便的实现如共同关注. 共同喜好. 二度好友等功能,对上面的所有集合操作,<br>你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。<br>Set是集合,是String类型的无序集合,set是通过hashtable实现的,概念和数学中个的集合基本类似,可以交集,并集,差集等等,set中的元素是没有顺序的。</li>
</ol>
<p>Sorted Sets<br>和Sets相比,Sorted Sets增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的Sorted Sets,其集合value可以是同学的学号,而score就可以是其考试得分,<br>这样在数据插入集合的时候,就已经进行了天然的排序。<br>可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。<br>比如在线游戏的排行榜,根据得分你通常想要:</p>
<ul>
<li>列出前100名高分选手</li>
<li>列出某用户当前的全球排名</li>
</ul>
<h1 id="seckill"><a href="#seckill" class="headerlink" title="seckill"></a>seckill</h1><h2 id="典型的秒杀系统:"><a href="#典型的秒杀系统:" class="headerlink" title="典型的秒杀系统:"></a>典型的秒杀系统:</h2><ol>
<li>典型的秒杀系统由接入层. 逻辑服务层. 存储层与缓存构成。Proxy处理请求接入,Server承载主要的业务逻辑,Cache用于缓存库存数量. DB则用于数据持久化。</li>
<li>一个秒杀系统对应DB中一条库存记录,当用户秒杀商品时,系统主要逻辑在于DB中库存的操作,一般对DB操作流程主要有以下三方面,1.锁库存. 2.插入秒杀记录. 3.更新库存,锁<br>库存避免超卖情况,同时要求这三步操作需要在一个事务中完成,作为单个逻辑工作单元执行,要么全部成功,要么全部失败。</li>
<li>秒杀系统设计难点,就在这个事务的操作上,商品库存在DB中记为一行,大量用户同时秒杀同一商品,第一个到达DB的请求锁住了这个库存记录,在第一个事物完成之前,这个锁一直被<br>第一个请求占用,后面的所有请求需要排队等待。并发请求的用户越多,DB请求就越多,排队越严重。</li>
</ol>
<h2 id="秒杀系统解决高并发问题常用方案"><a href="#秒杀系统解决高并发问题常用方案" class="headerlink" title="秒杀系统解决高并发问题常用方案"></a>秒杀系统解决高并发问题常用方案</h2><ol>
<li>方案一,使用内存操作替代实时DB事务操作。将实时扣库存的行为上移到内存Cache中操作,内存Cache操作成功直接给Server返回成功,然后异步落DB持久化。<br>优点:用内存操作替换磁盘操作,提高了并发性能。<br>缺点:在内存操作成功,但是DB持久化失败,或者内存Cache故障的情况下,DB持久化会丢数据。</li>
<li>方案二 使用乐观锁代替悲观锁:<br>悲观锁:关系数据库管理系统中一种并发控制的方法。<br>乐观锁:它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,会检查该事务读取的数据后,有没有其他事务对数据<br>进行了修改,如果有修改,正在提交的事务会回滚。<br>商品秒杀系统中,乐观锁的具体应用方法,是在DB的库存记录中维护一个版本号,在更新库存之前,先去DB获取当前版本号,在更新库存的事务提交时,检查版本号是否已经被其他事务修改,如果没有修改,<br>则提交事务,且版本号+1,如果版本号已经被其他事务修改,那么回滚事务,并报错。<br>优点:提高了DB并发处理的能力<br>缺点:采用乐观锁的方式,会带来大数据量的无效更新,事务回滚,给DB造成不必要的压力。用户体验也十分不好。</li>
</ol>
<h2 id="秒杀系统的挑战"><a href="#秒杀系统的挑战" class="headerlink" title="秒杀系统的挑战"></a>秒杀系统的挑战</h2><ol>
<li>对现有网站业务造成冲击</li>
<li>高并发下的应用. 数据库压力</li>
<li>突然增加的网络及服务器宽带</li>
<li>直接下单,下单页面的URL,需要动态化</li>
</ol>
<h2 id="秒杀系统的应对策略"><a href="#秒杀系统的应对策略" class="headerlink" title="秒杀系统的应对策略"></a>秒杀系统的应对策略</h2><ol>
<li>秒杀系统独立部署</li>
<li>秒杀系统页面静态化,用户请求不需要经过应用服务器的业务逻辑,也不需要访问数据库。</li>
<li>租赁秒杀网络宽带,需要将秒杀商品网页缓存到CDN,同样需要和CDN服务商临时租赁新增的出口宽带。</li>
<li>动态生成下单页面的URL,为了避免用户直接访问下单页面的URL,需要将URL动态化。在下单页面的URL加入由服务端生成的随机数作为参数,在秒杀开始的时候才能得到。</li>
</ol>
<h2 id="秒杀系统架构设计"><a href="#秒杀系统架构设计" class="headerlink" title="秒杀系统架构设计"></a>秒杀系统架构设计</h2><ol>
<li>秒杀页面尽量设计的简单</li>
<li>下单页也一样,尽量简单。只有第一个提交的订单会发送到网站的订单系统,其他用户提交订单后,只能看到秒杀结束页面。</li>
</ol>
<h2 id="如何控制秒杀商品页面购买按钮的点亮"><a href="#如何控制秒杀商品页面购买按钮的点亮" class="headerlink" title="如何控制秒杀商品页面购买按钮的点亮"></a>如何控制秒杀商品页面购买按钮的点亮</h2><p>使用javascript脚本控制,在秒杀商品静态页面加入一个javaScript文件引用,该javaScript文件中加入秒杀是否开始的标志和下单页面URL的随机参数,当秒杀开始的时候,生成一个新的javaScript文件并被用户浏览器加载,控制秒杀商品页面的展示,这个javaScript文件使用随机版本号,并且不做浏览器. CDN和反向代理服务器缓存。javaScript文件非常小,及时每次浏览器刷新都访问javaScript文件服务器也不会对服务器集群和网络宽带造成太大压力。</p>
<h2 id="如何只允许第一个提交的订单被发送到订单子系统。"><a href="#如何只允许第一个提交的订单被发送到订单子系统。" class="headerlink" title="如何只允许第一个提交的订单被发送到订单子系统。"></a>如何只允许第一个提交的订单被发送到订单子系统。</h2><ol>
<li>在用户提交订单时,需要检查是否已经有订单提交了。可以控制进入下单页面的入口,只有少数用户能进入到下单页,其他用户直接进入秒杀结束页面。</li>
</ol>
<h1 id="service"><a href="#service" class="headerlink" title="service"></a>service</h1><h2 id="微服务故障"><a href="#微服务故障" class="headerlink" title="微服务故障"></a>微服务故障</h2><ol>
<li>分布式调用故障<br> 序列化. 反序列化故障<br> 分布式路由故障<br> 网络通信故障</li>
<li>第三方服务故障,微服务会依赖第三方服务,包括数据库服务. 文件存储服务. 缓存服务. 消息队列服务等<br> 网络通信类故障<br> 雪崩效应,导致的级联故障,例如服务处理缓慢导致客户端线程被阻塞<br> 第三方不可用,导致微服务处理失败</li>
<li>微服务之间的故障<br> 处理较慢的微服务会阻塞其他服务<br> 某个微服务故障蔓延,导致整个进程不可用OOM<br> 低优先级的微服务,抢占高优先级微服务的资源</li>
</ol>
<h2 id="服务故障隔离"><a href="#服务故障隔离" class="headerlink" title="服务故障隔离"></a>服务故障隔离</h2><ol>
<li>IO操作故障隔离:网络IO. 磁盘IO. 数据库</li>
<li>资源故障隔离<br> 通信链路隔离<br> 调度资源隔离:微服务之间隔离. 第三方依赖隔离<br> 进程级隔离:VM隔离. 进程级隔离</li>
<li>容错能力<br> 路由容错:失败重试. 失败回调. 快速失败<br> 服务降级:强制降级. 容错降级<br> 熔断:全部拒绝. 部分拒绝</li>
<li>流量控制</li>
</ol>
<h2 id="故障隔离技术"><a href="#故障隔离技术" class="headerlink" title="故障隔离技术"></a>故障隔离技术</h2><p>同步I/O主要弊端:<br>I/O操作同步阻塞, 受制于网络和第三方处理速度 I/O线程效率低,容易发生 线程数量膨胀. 通信队列积压 等问题<br>优化策略:<br>TCP私有协议:建议直接基于 Netty开发<br>HTTP/Restful/SOAP等:选择支 持非阻塞I/O的Web框架。可以选 择基于Netty构建的开源应用层 协议栈框架</p>
<h2 id="RPC通信链路隔离"><a href="#RPC通信链路隔离" class="headerlink" title="RPC通信链路隔离"></a>RPC通信链路隔离</h2><p>隔离策略:</p>
<ol>
<li>微服务节点之间支持配置多链路</li>
<li>微服务链路支持不同的隔离策略: 例如根据消息码流大小. 根据微服务的优先级等策略,实现链路级的隔离</li>
</ol>
<h2 id="微服务调度隔离"><a href="#微服务调度隔离" class="headerlink" title="微服务调度隔离"></a>微服务调度隔离</h2><p>关键技术点:</p>
<ol>
<li>微服务发布时支持指定线程池/线程组</li>
<li>微服务线程池支持独享和共享两种模式 微服务和线程池监控,识别故障微服务, 动态调整到故障隔离线程池中</li>
<li>支持按照微服务优先级调度微服务, 即微服务线程支持微服务优先级调度</li>
</ol>
<h2 id="第三方服务依赖隔离"><a href="#第三方服务依赖隔离" class="headerlink" title="第三方服务依赖隔离"></a>第三方服务依赖隔离</h2><p>关键技术点:</p>
<ol>
<li>第三方依赖隔离可以采用线程池 + 响应式编程(例如RxJava)的方式实现 </li>
<li>对第三方依赖进行分类,每种依赖对应一个独立的线程/线程池</li>
<li>微服务不直接调用第三方依赖的API,而是使用异步封装之后的API接口</li>
<li>异步调用第三方依赖API之后,获取Future对象。利用响应式编程框架, 可以订阅后续的事件,接收响应,针对响应进行编程</li>
</ol>
<h2 id="微服务进程隔离-Docker容器"><a href="#微服务进程隔离-Docker容器" class="headerlink" title="微服务进程隔离(Docker容器)"></a>微服务进程隔离(Docker容器)</h2><p>关键技术点:</p>
<ol>
<li>微服务独立开发. 打包和部署</li>
<li>基于Docker部署微服务,可以实现细粒度的资源隔离,实现微服务的高密度部署。<br>优势:</li>
<li>高效:微服务的启动和销毁速度非常快, 可以实现秒级弹性伸缩</li>
<li>高性能:Docker容器的性能接近裸的物理机, 综合性能损耗 < 5%</li>
<li>可移植性:“一次编写,到处运行”</li>
</ol>
<h2 id="分布式路由容错"><a href="#分布式路由容错" class="headerlink" title="分布式路由容错"></a>分布式路由容错</h2><ol>
<li>失败自动重试:微服务调用失败自动重试</li>
<li>失败自动切换:当发生服务调用异常时,重新选路,查找下一个可用的微服务提供者 </li>
<li>快速失败:对于一些非核心的服务,希望只调用一次,失败也不再重试</li>
<li>失败回调:提供异常回调接口,执行微服务消费者自定义的失败处理逻辑</li>
</ol>
<h2 id="服务降级"><a href="#服务降级" class="headerlink" title="服务降级"></a>服务降级</h2><ol>
<li>强制降级:不发起远程服务调用,执行本地降级策略,例如本地Mock方法 </li>
<li>容错降级:当非核心服务不可用时,可以对故障服务做业务逻辑放通,以保障核心服务的运行,降级策略包括异常转换. 本地放通方法调用</li>
</ol>
<h2 id="熔断机制"><a href="#熔断机制" class="headerlink" title="熔断机制"></a>熔断机制</h2><ol>
<li>熔断判断:微服务调用时, 对熔断开关状态进行判断,当熔断器开关关闭时, 请求被允许通过熔断器</li>
<li>熔断执行:当熔断器开关打 开时,微服务调用请求被禁止通过,执行失败回调接口</li>
<li>自动恢复:熔断之后,周期T 之后允许一条消息通过,如果成功,则取消熔断状态,否则继续处于熔断状态</li>
</ol>
<h1 id="spark"><a href="#spark" class="headerlink" title="spark"></a>spark</h1><h2 id="什么是Spark"><a href="#什么是Spark" class="headerlink" title="什么是Spark"></a>什么是Spark</h2><ol>
<li>Apache Spark是一个围绕速度. 易用性和复杂分析构建的大数据处理框架。<br>最初在2009年由加州大学伯克利分校的AMPLab开发,并于2010年成为Apache的开源项目之一。</li>
<li>Spark为我们提供了一个全面. 统一的框架用于管理各种有着不同性质(文本数据. 图表数据等)的数据集和数据源(批量数据或实时的流数据)的大数据处理的需求。</li>
<li>Spark可以将Hadoop集群中的应用在内存中的运行速度提升100倍,甚至能够将应用在磁盘上的运行速度提升10倍。</li>