From 33784b8a311b182ef7ed2c7e3daf01f66be7de14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=8D=A1=E7=94=B0=E8=9E=BA=E7=9A=84=E5=B0=8F=E7=94=B7?=
=?UTF-8?q?=E5=AD=A9?= <327658337@qq.com>
Date: Sun, 11 Jul 2021 21:09:56 +0800
Subject: [PATCH 01/50] Add files via upload
---
...\350\256\25615\350\277\236\351\227\256.md" | 395 ++++++++++++++++++
1 file changed, 395 insertions(+)
create mode 100644 "Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md"
diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md"
new file mode 100644
index 0000000..eae7eec
--- /dev/null
+++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md"
@@ -0,0 +1,395 @@
+### 前言
+
+TCP协议是大厂面试必问的知识点。整理了15道非常经典的TCP面试题,希望大家都找到理想的offer呀
+
+
+
+
+
+- 公众号:**捡田螺的小男孩**
+
+### 1. 讲下TCP三次握手流程
+
+
+
+开始客户端和服务器都处于CLOSED状态,然后服务端开始监听某个端口,进入LISTEN状态
+
+- 第一次握手(SYN=1, seq=x),发送完毕后,客户端进入 SYN_SEND 状态
+- 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 发送完毕后,服务器端进入 SYN_RCVD 状态。
+- 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。
+
+### 2.TCP握手为什么是三次,不能是两次?不能是四次?
+
+TCP握手为什么是三次呢?为了方便理解,我们以谈恋爱为例子:两个人能走到一起,最重要的事情就是相爱,就是**我爱你,并且我知道,你也爱我**,接下来我们以此来模拟三次握手的过程:
+
+
+
+
+
+**为什么握手不能是两次呢?**
+
+如果只有两次握手,女孩子可能就不知道,她的那句**我也爱你**,男孩子是否**收到**,恋爱关系就不能愉快展开。
+
+**为什么握手不能是四次呢?**
+
+因为握手不能是四次呢?因为三次已经够了,三次已经能让双方都知道:你爱我,我也爱你。而四次就多余了。
+
+### 3. 讲下TCP四次挥手过程
+
+
+
+1. 第一次挥手(FIN=1,seq=u),发送完毕后,客户端进入FIN_WAIT_1 状态
+2. 第二次挥手(ACK=1,ack=u+1,seq =v),发送完毕后,服务器端进入CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态
+3. 第三次挥手(FIN=1,ACK1,seq=w,ack=u+1),发送完毕后,服务器端进入LAST_ACK 状态,等待来自客户端的最后一个ACK。
+4. 第四次挥手(ACK=1,seq=u+1,ack=w+1),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,**等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后**,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
+
+### 4. TCP挥手为什么需要四次呢?
+
+举个例子吧!
+
+> 小明和小红打电话聊天,通话差不多要结束时,小红说“我没啥要说的了”,小明回答“我知道了”。但是小明可能还会有要说的话,小红不能要求小明跟着自己的节奏结束通话,于是小明可能又叽叽歪歪说了一通,最后小明说“我说完了”,小红回答“知道了”,这样通话才算结束。
+
+
+
+### 5. TIME-WAIT 状态为什么需要等待 2MSL
+
+
+
+2MSL,2 Maximum Segment Lifetime,即两个最大段生命周期
+
+> - 1个 MSL 保证四次挥手中主动关闭方最后的 ACK 报文能最终到达对端
+> - 1个 MSL 保证对端没有收到 ACK 那么进行重传的 FIN 报文能够到达
+
+### 6.TCP 和 UDP 的区别
+
+1. TCP面向连接((如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
+2. TCP要求安全性,提供可靠的服务,通过TCP连接传送的数据,不丢失、不重复、安全可靠。而UDP尽最大努力交付,即不保证可靠交付。
+3. TCP是点对点连接的,UDP一对一,一对多,多对多都可以
+4. TCP传输效率相对较低,而UDP传输效率高,它适用于对高速传输和实时性有较高的通信或广播通信。
+5. TCP适合用于网页,邮件等;UDP适合用于视频,语音广播等
+6. TCP面向字节流,UDP面向报文
+
+### 7. TCP报文首部有哪些字段,说说其作用
+
+
+
+
+- **16位端口号**:源端口号,主机该报文段是来自哪里;目标端口号,要传给哪个上层协议或应用程序
+- **32位序号**:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。
+- **32位确认号**:用作对另一方发送的tcp报文段的响应。其值是收到的TCP报文段的序号值加1。
+- **4位头部长度**:表示tcp头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。
+- **6位标志位**:URG(紧急指针是否有效),ACk(表示确认号是否有效),PSH(缓冲区尚未填满),RST(表示要求对方重新建立连接),SYN(建立连接消息标志接),FIN(表示告知对方本端要关闭连接了)
+- **16位窗口大小**:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
+- **16位校验和**:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。
+- **16位紧急指针**:一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此,确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。
+
+### 8. TCP 是如何保证可靠性的
+
+
+
+
+
+- 首先,TCP的连接是基于**三次握手**,而断开则是**四次挥手**。确保连接和断开的可靠性。
+- 其次,TCP的可靠性,还体现在**有状态**;TCP会记录哪些数据发送了,哪些数据被接受了,哪些没有被接受,并且保证数据包按序到达,保证数据传输不出差错。
+- 再次,TCP的可靠性,还体现在**可控制**。它有报文校验、ACK应答、**超时重传(发送方)**、失序数据重传(接收方)、丢弃重复数据、流量控制(滑动窗口)和拥塞控制等机制。
+
+### 9. TCP 重传机制
+
+#### 超时重传
+TCP 为了实现可靠传输,实现了重传机制。最基本的重传机制,就是**超时重传**,即在发送数据报文时,设定一个定时器,每间隔一段时间,没有收到对方的ACK确认应答报文,就会重发该报文。
+
+这个间隔时间,一般设置为多少呢?我们先来看下什么叫**RTT(Round-Trip Time,往返时间)**。
+
+
+
+RTT就是,一个数据包从发出去到回来的时间,即**数据包的一次往返时间**。超时重传时间,就是Retransmission Timeout ,简称**RTO**。
+
+**RTO设置多久呢?**
+- 如果RTO比较小,那很可能数据都没有丢失,就重发了,这会导致网络阻塞,会导致更多的超时出现。
+- 如果RTO比较大,等到花儿都谢了还是没有重发,那效果就不好了。
+
+一般情况下,RTO略大于RTT,效果是最好的。一些小伙伴会问,超时时间有没有计算公式呢?有的!有个标准方法算RTO的公式,也叫**Jacobson / Karels 算法**。我们一起来看下计算RTO的公式
+
+**1. 先计算SRTT(计算平滑的RTT)**
+
+```
+SRTT = (1 - α) * SRTT + α * RTT //求 SRTT 的加权平均
+```
+
+**2. 再计算RTTVAR (round-trip time variation)**
+
+
+```
+RTTVAR = (1 - β) * RTTVAR + β * (|RTT - SRTT|) //计算 SRTT 与真实值的差距
+```
+
+**3. 最终的RTO**
+
+```
+RTO = µ * SRTT + ∂ * RTTVAR = SRTT + 4·RTTVAR
+```
+
+其中,```α = 0.125,β = 0.25, μ = 1,∂ = 4```,这些参数都是大量结果得出的最优参数。
+
+但是,超时重传会有这些缺点:
+> - 当一个报文段丢失时,会等待一定的超时周期然后才重传分组,增加了端到端的时延。
+> - 当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况:其后的报文段已经被接收端接收但却迟迟得不到确认,发送端会认为也丢失了,从而引起不必要的重传,既浪费资源也浪费时间。
+
+并且,TCP有个策略,就是超时时间间隔会加倍。超时重传需要**等待很长时间**。因此,还可以使用**快速重传**机制。
+
+#### 快速重传
+
+**快速重传**机制,它不以时间驱动,而是以数据驱动。它基于接收端的反馈信息来引发重传。
+
+一起来看下快速重传流程:
+
+
+
+发送端发送了 1,2,3,4,5,6 份数据:
+
+- 第一份 Seq=1 先送到了,于是就 Ack 回 2;
+- 第二份 Seq=2 也送到了,假设也正常,于是ACK 回 3;
+- 第三份 Seq=3 由于网络等其他原因,没送到;
+- 第四份 Seq=4 也送到了,但是因为Seq3没收到。所以ACK回3;
+- 后面的 Seq=4,5的也送到了,但是ACK还是回复3,因为Seq=3没收到。
+- 发送端连着收到三个重复冗余ACK=3的确认(实际上是4个,但是前面一个是正常的ACK,后面三个才是重复冗余的),便知道哪个报文段在传输过程中丢失了,于是在定时器过期之前,重传该报文段。
+- 最后,接收到收到了 Seq3,此时因为 Seq=4,5,6都收到了,于是ACK回7.
+
+但**快速重传**还可能会有个问题:ACK只向发送端告知最大的有序报文段,到底是哪个报文丢失了呢?**并不确定**!那到底该重传多少个包呢?
+> 是重传 Seq3 呢?还是重传 Seq3、Seq4、Seq5、Seq6 呢?因为发送端并不清楚这三个连续的 ACK3 是谁传回来的。
+
+#### 带选择确认的重传(SACK)
+
+为了解决快速重传的问题:**应该重传多少个包**? TCP提供了**SACK方法**(带选择确认的重传,Selective Acknowledgment)。
+
+**SACK机制**就是,在快速重传的基础上,接收端返回最近收到的报文段的序列号范围,这样发送端就知道接收端哪些数据包没收到,酱紫就很清楚该重传哪些数据包啦。SACK标记是加在TCP头部**选项**字段里面的。
+
+
+
+如上图中,发送端收到了三次同样的ACK=30的确认报文,于是就会触发快速重发机制,通过SACK信息发现只有```30~39```这段数据丢失,于是重发时就只选择了这个```30~39```的TCP报文段进行重发。
+
+#### D-SACK
+
+ D-SACK,即Duplicate SACK(重复SACK),在SACK的基础上做了一些扩展,,主要用来告诉发送方,有哪些数据包自己重复接受了。DSACK的目的是帮助发送方判断,是否发生了包失序、ACK丢失、包重复或伪重传。让TCP可以更好的做网络流控。来看个图吧:
+
+
+
+### 10. 聊聊TCP的滑动窗口
+
+TCP 发送一个数据,需要收到确认应答,才会发送下一个数据。这样有个缺点,就是效率会比较低。
+> 这就好像我们面对面聊天,你说完一句,我应答后,你才会说下一句。那么,如果我在忙其他事情,没有能够及时回复你。你说完一句后,要等到我忙完回复你,你才说下句,这显然很不现实。
+
+为了解决这个问题,TCP引入了**窗口**,它是操作系统开辟的一个缓存空间。窗口大小值表示无需等待确认应答,而可以继续发送数据的最大值。
+
+TCP头部有个字段叫win,也即那个**16位的窗口大小**,它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度,从而达到**流量控制**的目的。
+> 通俗点讲,就是接受方每次收到数据包,在发送确认报文的时候,同时告诉发送方,自己的缓存区还有多少空余空间,缓冲区的空余空间,我们就称之为接受窗口大小。这就是win。
+
+TCP 滑动窗口分为两种: 发送窗口和接收窗口。**发送端的滑动窗口**包含四大部分,如下:
+- 已发送且已收到ACK确认
+- 已发送但未收到ACK确认
+- 未发送但可以发送
+- 未发送也不可以发送
+
+
+
+- 虚线矩形框,就是发送窗口。
+- SND.WND: 表示发送窗口的大小,上图虚线框的格子数就是14个。
+- SND.UNA: 一个绝对指针,它指向的是已发送但未确认的第一个字节的序列号。
+- SND.NXT:下一个发送的位置,它指向未发送但可以发送的第一个字节的序列号。
+
+接收方的滑动窗口包含三大部分,如下:
+- 已成功接收并确认
+- 未收到数据但可以接收
+- 未收到数据并不可以接收的数据
+
+
+
+- 虚线矩形框,就是接收窗口。
+- REV.WND: 表示接收窗口的大小,上图虚线框的格子就是9个。
+- REV.NXT:下一个接收的位置,它指向未收到但可以接收的第一个字节的序列号。
+
+### 11. 聊聊TCP的流量控制
+
+TCP三次握手,发送端和接收端进入到ESTABLISHED状态,它们即可以愉快地传输数据啦。
+
+但是发送端不能疯狂地向接收端发送数据,因为接收端接收不过来的话,接收方只能把处理不过来的数据存在缓存区里。如果缓存区都满了,发送方还在疯狂发送数据的话,接收方只能把收到的数据包丢掉,这就浪费了网络资源啦。
+
+> TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量,这就是**流量控制**。
+
+TCP通过滑动窗口来控制流量,我们看下流量控制的**简要流程**吧:
+
+首先双方三次握手,初始化各自的窗口大小,均为 400 个字节。
+
+
+
+
+1. 假如当前发送方给接收方发送了200个字节,那么,发送方的```SND.NXT```会右移200个字节,也就是说当前的可用窗口减少了200 个字节。
+2. 接受方收到后,放到缓冲队列里面,REV.WND =400-200=200字节,所以win=200字节返回给发送方。接收方会在 ACK 的报文首部带上缩小后的滑动窗口200字节
+3. 发送方又发送200字节过来,200字节到达,继续放到缓冲队列。不过这时候,由于大量负载的原因,接受方处理不了这么多字节,只能处理100字节,剩余的100字节继续放到缓冲队列。这时候,REV.WND = 400-200-100=100字节,即win=100返回发送方。
+4. 发送方继续干活,发送100字节过来,这时候,接受窗口win变为0。
+5. 发送方停止发送,开启一个定时任务,每隔一段时间,就去询问接受方,直到win大于0,才继续开始发送。
+
+### 12. TCP的拥塞控制
+
+拥塞控制是**作用于网络的,防止过多的数据包注入到网络中,避免出现网络负载过大的情况**。它的目标主要是最大化利用网络上瓶颈链路的带宽。它跟**流量控制**又有什么区别呢?流量控制是作用于接收者的,根据**接收端的实际接收能力控制发送速度**,防止分组丢失的。
+
+我们可以把网络链路比喻成一根水管,如果我们想最大化利用网络来传输数据,那就是尽快让水管达到最佳充满状态。
+
+
+
+发送方维护一个**拥塞窗口cwnd(congestion window)** 的变量,用来估算在一段时间内这条链路(水管)可以承载和运输的数据(水)的数量。它大小代表着网络的拥塞程度,并且是动态变化的,但是为了达到最大的传输效率,我们该如何知道这条水管的运送效率是多少呢?
+
+一个比较简单的方法就是不断增加传输的水量,直到水管快要爆裂为止(对应到网络上就是发生丢包),用 TCP 的描述就是:
+> 只要网络中没有出现拥塞,拥塞窗口的值就可以再增大一些,以便把更多的数据包发送出去,但只要网络出现拥塞,拥塞窗口的值就应该减小一些,以减少注入到网络中的数据包数。
+
+实际上,拥塞控制主要有这几种常用算法
+- 慢启动
+- 拥塞避免
+- 拥塞发生
+- 快速恢复
+
+#### 慢启动算法
+
+慢启动算法,表面意思就是,别急慢慢来。它表示TCP建立连接完成后,一开始不要发送大量的数据,而是先探测一下网络的拥塞程度。由小到大逐渐增加拥塞窗口的大小,如果没有出现丢包,**每收到一个ACK,就将拥塞窗口cwnd大小就加1(单位是MSS)**。**每轮次**发送窗口增加一倍,呈指数增长,如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。
+
+- TCP连接完成,初始化cwnd = 1,表明可以传一个MSS单位大小的数据。
+- 每当收到一个ACK,cwnd就加一;
+- 每当过了一个RTT,cwnd就增加一倍; 呈指数让升
+
+
+
+为了防止cwnd增长过大引起网络拥塞,还需设置一个**慢启动阀值ssthresh**(slow start threshold)状态变量。当```cwnd```到达该阀值后,就好像水管被关小了水龙头一样,减少拥塞状态。即当**cwnd >ssthresh**时,进入了**拥塞避免**算法。
+
+
+#### 拥塞避免算法
+
+一般来说,慢启动阀值ssthresh是65535字节,```cwnd```到达**慢启动阀值**后
+- 每收到一个ACK时,cwnd = cwnd + 1/cwnd
+- 当每过一个RTT时,cwnd = cwnd + 1
+
+显然这是一个线性上升的算法,避免过快导致网络拥塞问题。
+
+
+
+#### 拥塞发生
+
+当网络拥塞发生**丢包**时,会有两种情况:
+
+- RTO超时重传
+- 快速重传
+
+如果是发生了**RTO超时重传**,就会使用拥塞发生算法
+
+- 慢启动阀值sshthresh = cwnd /2
+- cwnd 重置为 1
+- 进入新的慢启动过程
+
+
+
+
+这真的是**辛辛苦苦几十年,一朝回到解放前**。其实还有更好的处理方式,就是**快速重传**。发送方收到3个连续重复的ACK时,就会快速地重传,不必等待**RTO超时**再重传。
+
+
+
+慢启动阀值ssthresh 和 cwnd 变化如下:
+
+- 拥塞窗口大小 cwnd = cwnd/2
+- 慢启动阀值 ssthresh = cwnd
+- 进入快速恢复算法
+
+#### 快速恢复
+
+快速重传和快速恢复算法一般同时使用。快速恢复算法认为,还有3个重复ACK收到,说明网络也没那么糟糕,所以没有必要像RTO超时那么强烈。
+
+正如前面所说,进入快速恢复之前,cwnd 和 sshthresh已被更新:
+```
+- cwnd = cwnd /2
+- sshthresh = cwnd
+```
+
+然后,真正的快速算法如下:
+
+- cwnd = sshthresh + 3
+- 重传重复的那几个ACK(即丢失的那几个数据包)
+- 如果再收到重复的 ACK,那么 cwnd = cwnd +1
+- 如果收到新数据的 ACK 后, cwnd = sshthresh。因为收到新数据的 ACK,表明恢复过程已经结束,可以再次进入了拥塞避免的算法了。
+
+
+
+### 13. 半连接队列和 SYN Flood 攻击的关系
+
+TCP进入三次握手前,服务端会从**CLOSED**状态变为**LISTEN**状态,同时在内部创建了两个队列:半连接队列(SYN队列)和全连接队列(ACCEPT队列)。
+
+什么是**半连接队列(SYN队列)** 呢? 什么是**全连接队列(ACCEPT队列)** 呢?回忆下TCP三次握手的图:
+
+
+
+- TCP三次握手时,客户端发送SYN到服务端,服务端收到之后,便回复**ACK和SYN**,状态由**LISTEN变为SYN_RCVD**,此时这个连接就被推入了**SYN队列**,即半连接队列。
+- 当客户端回复ACK, 服务端接收后,三次握手就完成了。这时连接会等待被具体的应用取走,在被取走之前,它被推入ACCEPT队列,即全连接队列。
+
+SYN Flood是一种典型的DoS (Denial of Service,拒绝服务) 攻击,它在短时间内,伪造**不存在的IP地址**,向服务器大量发起SYN报文。当服务器回复SYN+ACK报文后,不会收到ACK回应报文,导致服务器上建立大量的半连接半连接队列满了,这就无法处理正常的TCP请求啦。
+
+主要有 **syn cookie**和**SYN Proxy防火墙**等方案应对。
+
+- **syn cookie**:在收到SYN包后,服务器根据一定的方法,以数据包的源地址、端口等信息为参数计算出一个cookie值作为自己的SYNACK包的序列号,回复SYN+ACK后,服务器并不立即分配资源进行处理,等收到发送方的ACK包后,重新根据数据包的源地址、端口计算该包中的确认序列号是否正确,如果正确则建立连接,否则丢弃该包。
+
+- **SYN Proxy防火墙**:服务器防火墙会对收到的每一个SYN报文进行代理和回应,并保持半连接。等发送方将ACK包返回后,再重新构造SYN包发到服务器,建立真正的TCP连接。
+
+### 14. Nagle 算法与延迟确认
+
+#### Nagle算法
+
+如果发送端疯狂地向接收端发送很小的包,比如就1个字节,那么亲爱的小伙伴,你们觉得会有什么问题呢?
+
+> TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。**Nagle算法**就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
+
+Nagle算法的基本定义是:**任意时刻,最多只能有一个未被确认的小段**。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
+
+Nagle算法的实现规则:
+
+- 如果包长度达到MSS,则允许发送;
+- 如果该包含有FIN,则允许发送;
+- 设置了TCP_NODELAY选项,则允许发送;
+- 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
+- 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。
+
+#### 延迟确认
+
+如果接受方刚接收到发送方的数据包,在很短很短的时间内,又接收到第二个包。那么请问接收方是一个一个地回复好点,还是合并一起回复好呢?
+
+> 接收方收到数据包后,如果暂时没有数据要发给对端,它可以等一段时再确认(Linux上默认是40ms)。如果这段时间刚好有数据要传给对端,ACK就随着数据传输,而不需要单独发送一次ACK。如果超过时间还没有数据要发送,也发送ACK,避免对端以为丢包。
+
+但是有些场景不能延迟确认,比如发现了**乱序包**、**接收到了大于一个 frame 的报文,且需要调整窗口大小**等。
+
+一般情况下,**Nagle算法和延迟确认**不能一起使用,Nagle算法意味着延迟发,**延迟确认**意味着延迟接收,酱紫就会造成更大的延迟,会产生性能问题。
+
+### 15. TCP的粘包和拆包
+
+TCP是面向流,没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一**个完整的包可能会被TCP拆分成多个包进行发送**,**也有可能把多个小的包封装成一个大的数据包发送**,这就是所谓的TCP粘包和拆包问题。
+
+
+
+
+**为什么会产生粘包和拆包呢?**
+
+- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;
+- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
+- 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;
+- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。
+
+**解决方案:**
+
+- 发送端将每个数据包封装为固定长度
+- 在数据尾部增加特殊字符进行分割
+- 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小。
+
+### 参考与感谢
+- [TCP 的那些事儿(下)](https://coolshell.cn/articles/11609.html "TCP 的那些事儿(下)")
+- [面试头条你需要懂的 TCP 拥塞控制原理](https://zhuanlan.zhihu.com/p/76023663 "面试头条你需要懂的 TCP 拥塞控制原理")
+- [30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制发愁](https://zhuanlan.zhihu.com/p/133307545 "30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制发愁")
+- [TCP协议灵魂之问,巩固你的网路底层基础](https://juejin.cn/post/6844904070889603085 "TCP协议灵魂之问,巩固你的网路底层基础")
+- [TCP粘包和拆包](https://blog.csdn.net/ailunlee/article/details/95944377 "TCP粘包和拆包")
+- 百度百科
+
+
+
From 726441dc408342ae76d78f1d738457a75094e410 Mon Sep 17 00:00:00 2001
From: whx123 <327658337@qq.com>
Date: Sun, 11 Jul 2021 21:24:19 +0800
Subject: [PATCH 02/50] =?UTF-8?q?Redis=E5=BF=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...10\350\277\231\344\271\210\345\277\253.md" | 175 ++++++++++++++++++
1 file changed, 175 insertions(+)
create mode 100644 "Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md"
diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md"
new file mode 100644
index 0000000..a3f6b30
--- /dev/null
+++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md"
@@ -0,0 +1,175 @@
+
+## 前言
+
+大家好呀,我是捡田螺的小男孩。我们都知道Redis很快,它QPS可达10万(每秒请求数)。**Redis为什么这么快呢**,本文将跟大家一起学习。
+
+
+
+
+- 公众号:**捡田螺的小男孩**
+- [github地址](https://github.com/whx123/JavaHome),感谢每一颗star
+
+## 基于内存实现
+
+我们都知道内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的数据库,相对于数据存在磁盘的数据库,就省去磁盘磁盘I/O的消耗。MySQL等磁盘数据库,需要建立索引来加快查询效率,而Redis数据存放在内存,直接操作内存,所以就很快。
+
+
+
+## 高效的数据结构
+
+我们知道,MySQL索引为了提高效率,选择了B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下Redis的数据结构&内部编码图:
+
+
+
+
+
+### SDS简单动态字符串
+
+
+
+
+```
+struct sdshdr { //SDS简单动态字符串
+ int len; //记录buf中已使用的空间
+ int free; // buf中空闲空间长度
+ char buf[]; //存储的实际内容
+}
+```
+
+
+#### 字符串长度处理
+
+在C语言中,要获取```捡田螺的小男孩```这个字符串的长度,需要从头开始遍历,复杂度为O(n);
+在Redis中, 已经有一个**len**字段记录当前字符串的长度啦,直接获取即可,时间复杂度为O(1)。
+
+#### 减少内存重新分配的次数
+
+在C语言中,修改一个字符串,需要重新分配内存,修改越频繁,内存分配就越频繁,而分配内存是会**消耗性能**的。而在Redis中,SDS提供了两种优化策略:空间预分配和惰性空间释放。
+
+**空间预分配**
+
+当SDS简单动态字符串修改和空间扩充时,除了分配必需的内存空间,还会额外分配未使用的空间。分配规则是酱紫的:
+
+> - SDS修改后,len的长度小于1M,那么将额外分配与len相同长度的未使用空间。比如len=100,重新分配后,buf 的实际长度会变为100(已使用空间)+100(额外空间)+1(空字符)=201。
+> - SDS修改后, len长度大于1M,那么程序将分配1M的未使用空间。
+
+**惰性空间释放**
+
+当SDS缩短时,不是回收多余的内存空间,而是用free记录下多余的空间。后续再有修改操作,直接使用free中的空间,减少内存分配。
+
+#### 哈希
+
+Redis 作为一个K-V的内存数据库,它使用用一张全局的哈希来保存所有的键值对。这张哈希表,有多个哈希桶组成,哈希桶中的entry元素保存了```*key```和```*value```指针,其中```*key```指向了实际的键,```*value```指向了实际的值。
+
+
+
+哈希表查找速率很快的,有点类似于Java中的**HashMap**,它让我们在**O(1)** 的时间复杂度快速找到键值对。首先通过key计算哈希值,找到对应的哈希桶位置,然后定位到entry,在entry找到对应的数据。
+
+有些小伙伴可能会有疑问:你往哈希表中写入大量数据时,不是会遇到**哈希冲突**问题嘛,那效率就会降下来啦。
+> **哈希冲突:** 通过不同的key,计算出一样的哈希值,导致落在同一个哈希桶中。
+
+Redis为了解决哈希冲突,采用了**链式哈希**。链式哈希是指同一个哈希桶中,多个元素用一个链表来保存,它们之间依次用指针连接。
+
+
+
+有些小伙伴可能还会有疑问:哈希冲突链上的元素只能通过指针逐一查找再操作。当往哈希表插入数据很多,冲突也会越多,冲突链表就会越长,那查询效率就会降低了。
+
+为了保持高效,Redis 会对哈希表做**rehash操作**,也就是增加哈希桶,减少冲突。为了rehash更高效,Redis还默认使用了两个全局哈希表,一个用于当前使用,称为主哈希表,一个用于扩容,称为备用哈希表。
+
+#### 跳跃表
+
+跳跃表是Redis特有的数据结构,它其实就是在**链表的基础上,增加多级索引**,以提高查找效率。跳跃表的简单原理图如下:
+
+
+
+- 每一层都有一条有序的链表,最底层的链表包含了所有的元素。
+- 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。
+
+
+#### 压缩列表ziplist
+
+压缩列表ziplist是列表键和字典键的的底层实现之一。它是由一系列特殊编码的内存块构成的列表, 一个ziplist可以包含多个entry, 每个entry可以保存一个长度受限的字符数组或者整数,如下:
+
+
+
+
+- zlbytes :记录整个压缩列表占用的内存字节数
+- zltail: 尾节点至起始节点的偏移量
+- zllen : 记录整个压缩列表包含的节点数量
+- entryX: 压缩列表包含的各个节点
+- zlend : 特殊值0xFF(十进制255),用于标记压缩列表末端
+
+由于内存是**连续分配**的,所以遍历速度很快。。
+
+
+## 合理的数据编码
+
+Redis支持多种数据基本类型,每种基本类型对应不同的数据结构,每种数据结构对应不一样的编码。为了提高性能,Redis设计者总结出,数据结构最适合的编码搭配。
+
+Redis是使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。
+```
+//关注公众号:捡田螺的小男孩
+typedef struct redisObject{
+ //类型
+ unsigned type:4;
+ //编码
+ unsigned encoding:4;
+ //指向底层数据结构的指针
+ void *ptr;
+ //...
+ }robj;
+```
+
+redisObject中,**type** 对应的是对象类型,包含String对象、List对象、Hash对象、Set对象、zset对象。**encoding** 对应的是编码。
+
+- String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。
+- List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
+- Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。
+- Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。
+- Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码
+
+## 合理的线程模型
+
+
+### 单线程模型:避免了上下文切换
+
+Redis是单线程的,其实是指**Redis的网络IO和键值对读写**是由一个线程来完成的。但Redis的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。
+
+Redis的单线程模型,避免了**CPU不必要的上下文切换**和**竞争锁的消耗**。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的内存数据库,所以要慎用如lrange和smembers、hgetall等命令。
+
+什么是**上下文切换**?举个粟子:
+
+> - 比如你在看一本英文小说,你看到某一页,发现有个单词不会读,你加了个书签,然后去查字典。查完字典后,你回来从书签那里继续开始读,这个流程就很舒畅。
+> - 如果你一个人读这本书,肯定没啥问题。但是如果你去查字典的时候,别的小伙伴翻了一下你的书,然后溜了。你再回来看的时候,发现书不是你看的那一页了,你得花时间找到你的那一页。
+> - 一本书,你一个人怎么看怎么打标签都没事,但是人多了翻来翻去,这本书各种标记就很乱了。可能这个解释很粗糙,但是道理应该是一样的。
+
+
+
+### I/O 多路复用
+
+什么是I/O多路复用?
+- I/O :网络 I/O
+- 多路 :多个网络连接
+- 复用:复用同一个线程。
+- IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。
+
+
+
+
+
+> 多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。
+
+## 虚拟内存机制
+
+Redis直接自己构建了VM机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。
+
+**Redis的虚拟内存机制是啥呢?**
+> 虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
+
+### 参考与感谢
+
+- [Redis之VM机制](https://www.codenong.com/cs106843764/)
+- [一文揭秘单线程的Redis为什么这么快?](https://zhuanlan.zhihu.com/p/57089960)
+- [洞察|Redis是单线程的,但Redis为什么这么快?](https://zhuanlan.zhihu.com/p/42272979)
+
+
From 1b8d1314deb883bc8dcf1ba547334a82d9f0e5e7 Mon Sep 17 00:00:00 2001
From: whx123 <327658337@qq.com>
Date: Sun, 11 Jul 2021 21:26:23 +0800
Subject: [PATCH 03/50] =?UTF-8?q?Redis=E5=BF=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../order by\350\257\246\350\247\243.md" | 348 ++++++++++++++++++
1 file changed, 348 insertions(+)
create mode 100644 "Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md"
diff --git "a/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md" "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md"
new file mode 100644
index 0000000..3aeda16
--- /dev/null
+++ "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md"
@@ -0,0 +1,348 @@
+## 前言
+
+日常开发中,我们经常会使用到order by,亲爱的小伙伴,你是否知道order by 的工作原理呢?order by的优化思路是怎样的呢?使用order by有哪些注意的问题呢?本文将跟大家一起来学习,攻克order by~
+
+
+
+
+- 微信公众号:**捡田螺的小男孩**
+- [github地址,感谢每一颗star](https://github.com/whx123/JavaHome)
+- 如果觉得有收获,帮忙点赞,转发下哈,感谢感谢
+
+
+## 一个使用order by 的简单例子
+
+假设用一张员工表,表结构如下:
+
+```
+CREATE TABLE `staff` (
+`id` BIGINT ( 11 ) AUTO_INCREMENT COMMENT '主键id',
+`id_card` VARCHAR ( 20 ) NOT NULL COMMENT '身份证号码',
+`name` VARCHAR ( 64 ) NOT NULL COMMENT '姓名',
+`age` INT ( 4 ) NOT NULL COMMENT '年龄',
+`city` VARCHAR ( 64 ) NOT NULL COMMENT '城市',
+PRIMARY KEY ( `id`),
+INDEX idx_city ( `city` )
+) ENGINE = INNODB COMMENT '员工表';
+
+```
+
+表数据如下:
+
+
+
+
+我们现在有这么一个需求:**查询前10个,来自深圳员工的姓名、年龄、城市,并且按照年龄小到大排序**。对应的 SQL 语句就可以这么写:
+
+```
+select name,age,city from staff where city = '深圳' order by age limit 10;
+```
+
+这条语句的逻辑很清楚,但是它的**底层执行流程**是怎样的呢?
+
+## order by 工作原理
+
+
+
+### explain 执行计划
+
+我们先用**Explain**关键字查看一下执行计划
+
+
+
+
+- 执行计划的**key**这个字段,表示使用到索引idx_city
+- Extra 这个字段的 **Using index condition** 表示索引条件
+- Extra 这个字段的 **Using filesort**表示用到排序
+
+我们可以发现,这条SQL使用到了索引,并且也用到排序。那么它是**怎么排序**的呢?
+
+### 全字段排序
+
+MySQL 会给每个查询线程分配一块小**内存**,用于**排序**的,称为 **sort_buffer**。什么时候把字段放进去排序呢,其实是通过```idx_city```索引找到对应的数据,才把数据放进去啦。
+
+我们回顾下索引是怎么找到匹配的数据的,现在先把索引树画出来吧,**idx_city**索引树如下:
+
+
+
+
+idx_city索引树,叶子节点存储的是**主键id**。 还有一棵id主键聚族索引树,我们再画出聚族索引树图吧:
+
+
+
+
+
+**我们的查询语句是怎么找到匹配数据的呢**?先通过**idx_city**索引树,找到对应的主键id,然后再通过拿到的主键id,搜索**id主键索引树**,找到对应的行数据。
+
+加上**order by**之后,整体的执行流程就是:
+
+1. MySQL 为对应的线程初始化**sort_buffer**,放入需要查询的name、age、city字段;
+2. 从**索引树idx_city**, 找到第一个满足 city='深圳’条件的主键 id,也就是图中的id=9;
+3. 到**主键 id 索引树**拿到id=9的这一行数据, 取name、age、city三个字段的值,存到sort_buffer;
+4. 从**索引树idx_city** 拿到下一个记录的主键 id,即图中的id=13;
+5. 重复步骤 3、4 直到**city的值不等于深圳**为止;
+6. 前面5步已经查找到了所有**city为深圳**的数据,在 sort_buffer中,将所有数据根据age进行排序;
+7. 按照排序结果取前10行返回给客户端。
+
+执行示意图如下:
+
+
+
+将查询所需的字段全部读取到sort_buffer中,就是**全字段排序**。这里面,有些小伙伴可能会有个疑问,把查询的所有字段都放到sort_buffer,而sort_buffer是一块内存来的,如果数据量太大,sort_buffer放不下怎么办呢?
+
+### 磁盘临时文件辅助排序
+
+实际上,sort_buffer的大小是由一个参数控制的:**sort_buffer_size**。如果要排序的数据小于sort_buffer_size,排序在**sort_buffer** 内存中完成,如果要排序的数据大于sort_buffer_size,则**借助磁盘文件来进行排序**
+
+如何确定是否使用了磁盘文件来进行排序呢? 可以使用以下这几个命令
+
+```
+## 打开optimizer_trace,开启统计
+set optimizer_trace = "enabled=on";
+## 执行SQL语句
+select name,age,city from staff where city = '深圳' order by age limit 10;
+## 查询输出的统计信息
+select * from information_schema.optimizer_trace
+```
+
+可以从 **number_of_tmp_files** 中看出,是否使用了临时文件。
+
+
+
+
+
+**number_of_tmp_files** 表示使用来排序的磁盘临时文件数。如果number_of_tmp_files>0,则表示使用了磁盘文件来进行排序。
+
+使用了磁盘临时文件,整个排序过程又是怎样的呢?
+
+1. 从**主键Id索引树**,拿到需要的数据,并放到**sort_buffer内存**块中。当sort_buffer快要满时,就对sort_buffer中的数据排序,排完后,把数据临时放到磁盘一个小文件中。
+2. 继续回到主键 id 索引树取数据,继续放到sort_buffer内存中,排序后,也把这些数据写入到磁盘临时小文件中。
+3. 继续循环,直到取出所有满足条件的数据。最后把磁盘的临时排好序的小文件,合并成一个有序的大文件。
+
+
+**TPS:** 借助磁盘临时小文件排序,实际上使用的是**归并排序**算法。
+
+小伙伴们可能会有个疑问,既然**sort_buffer**放不下,就需要用到临时磁盘文件,这会影响排序效率。那为什么还要把排序不相关的字段(name,city)放到sort_buffer中呢?只放排序相关的age字段,它**不香**吗? 可以了解下**rowid 排序**。
+
+
+### rowid 排序
+
+rowid 排序就是,只把查询SQL**需要用于排序的字段和主键id**,放到sort_buffer中。那怎么确定走的是全字段排序还是rowid 排序排序呢?
+
+实际上有个参数控制的。这个参数就是**max_length_for_sort_data**,它表示MySQL用于排序行数据的长度的一个参数,如果单行的长度超过这个值,MySQL 就认为单行太大,就换rowid 排序。我们可以通过命令看下这个参数取值。
+
+
+```
+show variables like 'max_length_for_sort_data';
+```
+
+
+
+**max_length_for_sort_data** 默认值是1024。因为本文示例中name,age,city长度=64+4+64 =132 < 1024, 所以走的是全字段排序。我们来改下这个参数,改小一点,
+
+```
+## 修改排序数据最大单行长度为32
+set max_length_for_sort_data = 32;
+## 执行查询SQL
+select name,age,city from staff where city = '深圳' order by age limit 10;
+```
+
+使用rowid 排序的话,整个SQL执行流程又是怎样的呢?
+
+1. MySQL 为对应的线程初始化**sort_buffer**,放入需要排序的age字段,以及主键id;
+2. 从**索引树idx_city**, 找到第一个满足 city='深圳’条件的主键 id,也就是图中的id=9;
+3. 到**主键 id 索引树**拿到id=9的这一行数据, 取age和主键id的值,存到sort_buffer;
+4. 从**索引树idx_city** 拿到下一个记录的主键 id,即图中的id=13;
+5. 重复步骤 3、4 直到**city的值不等于深圳**为止;
+6. 前面5步已经查找到了所有city为深圳的数据,在 **sort_buffer**中,将所有数据根据age进行排序;
+7. 遍历排序结果,取前10行,并按照 id 的值**回到原表**中,取出city、name 和 age 三个字段返回给客户端。
+
+
+执行示意图如下:
+
+
+
+
+
+对比一下**全字段排序**的流程,rowid 排序多了一次**回表**。
+
+> 什么是回表?拿到主键再回到主键索引查询的过程,就叫做回表
+
+
+我们通过**optimizer_trace**,可以看到是否使用了rowid排序的:
+
+
+```
+## 打开optimizer_trace,开启统计
+set optimizer_trace = "enabled=on";
+## 执行SQL语句
+select name,age,city from staff where city = '深圳' order by age limit 10;
+## 查询输出的统计信息
+select * from information_schema.optimizer_trace
+
+```
+
+
+
+
+### 全字段排序与rowid排序对比
+
+
+- 全字段排序: sort_buffer内存不够的话,就需要用到磁盘临时文件,造成**磁盘访问**。
+- rowid排序: sort_buffer可以放更多数据,但是需要再回到原表去取数据,比全字段排序多一次**回表**。
+
+一般情况下,对于InnoDB存储引擎,会优先使**用全字段**排序。可以发现 **max_length_for_sort_data** 参数设置为1024,这个数比较大的。一般情况下,排序字段不会超过这个值,也就是都会走**全字段**排序。
+
+
+## order by的一些优化思路
+
+我们如何优化order by语句呢?
+
+
+- 因为数据是无序的,所以就需要排序。如果数据本身是有序的,那就不用排了。而索引数据本身是有序的,我们通过建立**联合索引**,优化order by 语句。
+- 我们还可以通过调整**max_length_for_sort_data**等参数优化;
+
+
+### 联合索引优化
+
+再回顾下示例SQL的查询计划
+
+```
+explain select name,age,city from staff where city = '深圳' order by age limit 10;
+```
+
+
+
+我们给查询条件```city```和排序字段```age```,加个联合索引**idx_city_age**。再去查看执行计划
+
+```
+alter table staff add index idx_city_age(city,age);
+explain select name,age,city from staff where city = '深圳' order by age limit 10;
+```
+
+
+
+可以发现,加上**idx_city_age**联合索引,就不需要**Using filesort**排序了。为什么呢?因为**索引本身是有序的**,我们可以看下**idx_city_age**联合索引示意图,如下:
+
+
+
+
+
+整个SQL执行流程变成酱紫:
+1. 从索引idx_city_age找到满足**city='深圳’** 的主键 id
+2. 到**主键 id索引**取出整行,拿到 name、city、age 三个字段的值,作为结果集的一部分直接返回
+3. 从索引**idx_city_age**取下一个记录主键id
+4. 重复步骤 2、3,直到查到**第10条**记录,或者是**不满足city='深圳’** 条件时循环结束。
+
+流程示意图如下:
+
+
+
+
+从示意图看来,还是有一次回表操作。针对本次示例,有没有更高效的方案呢?有的,可以使用**覆盖索引**:
+
+> 覆盖索引:在查询的数据列里面,不需要回表去查,直接从索引列就能取到想要的结果。换句话说,你SQL用到的索引列数据,覆盖了查询结果的列,就算上覆盖索引了。
+
+我们给city,name,age 组成一个联合索引,即可用到了覆盖索引,这时候SQL执行时,连回表操作都可以省去啦。
+
+### 调整参数优化
+
+我们还可以通过调整参数,去优化order by的执行。比如可以调整sort_buffer_size的值。因为sort_buffer值太小,数据量大的话,会借助磁盘临时文件排序。如果MySQL服务器配置高的话,可以使用稍微调整大点。
+
+我们还可以调整max_length_for_sort_data的值,这个值太小的话,order by会走rowid排序,会回表,降低查询性能。所以max_length_for_sort_data可以适当大一点。
+
+当然,很多时候,这些MySQL参数值,我们直接采用默认值就可以了。
+
+## 使用order by 的一些注意点
+
+### 没有where条件,order by字段需要加索引吗
+
+日常开发过程中,我们可能会遇到没有where条件的order by,那么,这时候order by后面的字段是否需要加索引呢。如有这么一个SQL,create_time是否需要加索引:
+
+```
+select * from A order by create_time;
+```
+
+无条件查询的话,即使create_time上有索引,也不会使用到。因为MySQL优化器认为走普通二级索引,再去回表成本比全表扫描排序更高。所以选择走全表扫描,然后根据全字段排序或者rowid排序来进行。
+
+如果查询SQL修改一下:
+
+```
+select * from A order by create_time limit m;
+```
+- 无条件查询,如果m值较小,是可以走索引的.因为MySQL优化器认为,根据索引有序性去回表查数据,然后得到m条数据,就可以终止循环,那么成本比全表扫描小,则选择走二级索引。
+
+
+### 分页limit过大时,会导致大量排序怎么办?
+
+假设SQL如下:
+```
+select * from A order by a limit 100000,10
+```
+
+- 可以记录上一页最后的id,下一页查询时,查询条件带上id,如: where id > 上一页最后id limit 10。
+- 也可以在业务允许的情况下,限制页数。
+
+
+### 索引存储顺序与order by不一致,如何优化?
+
+假设有联合索引 idx_age_name, 我们需求修改为这样:**查询前10个员工的姓名、年龄,并且按照年龄小到大排序,如果年龄相同,则按姓名降序排**。对应的 SQL 语句就可以这么写:
+
+```
+select name,age from staff order by age ,name desc limit 10;
+```
+我们看下执行计划,发现使用到**Using filesort**。
+
+
+
+这是因为,idx_age_name索引树中,age从小到大排序,如果**age相同,再按name从小到大排序**。而order by 中,是按age从小到大排序,如果**age相同,再按name从大到小排序**。也就是说,索引存储顺序与order by不一致。
+
+我们怎么优化呢?如果MySQL是8.0版本,支持**Descending Indexes**,可以这样修改索引:
+
+```
+CREATE TABLE `staff` (
+ `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `id_card` varchar(20) NOT NULL COMMENT '身份证号码',
+ `name` varchar(64) NOT NULL COMMENT '姓名',
+ `age` int(4) NOT NULL COMMENT '年龄',
+ `city` varchar(64) NOT NULL COMMENT '城市',
+ PRIMARY KEY (`id`),
+ KEY `idx_age_name` (`age`,`name` desc) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='员工表';
+```
+
+
+### 使用了in条件多个属性时,SQL执行是否有排序过程
+
+如果我们有**联合索引idx_city_name**,执行这个SQL的话,是不会走排序过程的,如下:
+
+```
+select * from staff where city in ('深圳') order by age limit 10;
+```
+
+
+
+
+
+但是,如果使用in条件,并且有多个条件时,就会有排序过程。
+
+```
+ explain select * from staff where city in ('深圳','上海') order by age limit 10;
+```
+
+
+
+这是因为:in有两个条件,在满足深圳时,age是排好序的,但是把满足上海的age也加进来,就不能保证满足所有的age都是排好序的。因此需要Using filesort。
+
+## 最后
+
+- 如果觉得有收获,帮忙点赞,转发下哈,感谢感谢
+- 微信搜索公众号:**捡田螺的小男孩**,加个好友,进技术交流群
+
+
+### 参考与感谢
+
+- MySQL实战45讲
+
+
+
From 46430483bac498e923f85ac4768040d32e72eff8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=8D=A1=E7=94=B0=E8=9E=BA=E7=9A=84=E5=B0=8F=E7=94=B7?=
=?UTF-8?q?=E5=AD=A9?= <327658337@qq.com>
Date: Sun, 10 Oct 2021 23:16:51 +0800
Subject: [PATCH 04/50] Add files via upload
---
...23\345\215\260\350\247\204\350\214\203.md" | 269 ++++++++++++++++++
1 file changed, 269 insertions(+)
create mode 100644 "\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md"
diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md"
new file mode 100644
index 0000000..4577332
--- /dev/null
+++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md"
@@ -0,0 +1,269 @@
+## ǰ
+
+Һã**ݵСк**־ǿٶλĺð֣**˺ƺ˦**ӡ־dzҪ**־ӡ**15ý~
+
+- ںţ**ݵСк**
+
+
+## 1. ѡǡ־
+
+־5ֱ֣errorwarninfodebugtraceճУҪѡǡ־𣬲Ҫ־Ǵӡinfo~
+
+
+
+- error־ָȽصĴҵӰ죬Ҫ**άüص**
+- warn־һĴҵӰ첻Ҫ**ע**
+- infoϢ־¼ŲĹؼϢʱ䡢εȵȣ
+- debugڿDEBUGģؼʱݣ
+- traceϸϢһЩϢֻ¼־ļС
+
+
+## 2. ־ҪӡΡ
+
+DzҪӡܶܶ־ֻҪӡ**ٶλЧ־**Ч־˦
+
+
+
+Щõ**Чؼ**־أ˵ʱӡ****Ȼأڷصʱ**ӡΣֵ**εĻһ**userIdbizSeqЩؼ**Ϣ£
+
+```
+public String testLogMethod(Document doc, Mode mode){
+ log.debug(method enter param{},userId);
+ String id = "666";
+ log.debug(method exit param{},id);
+ return id;
+}
+```
+
+
+## 3. ѡʵ־ʽ
+
+־ʽӦЩϢ統**ǰʱ**һ뾫ȷȣ**־****߳**ȵȡlogback־ôã
+
+```
+
+
+ %d{HH:mm:ss.SSS} %-5level [%thread][%logger{0}] %m%n
+
+
+```
+
+ǵ־ʽǰʱ䶼]м¼**ʱ㶼֪**
+
+
+
+
+## 4. if...else...ʱÿ֧жӡ־
+
+**if...else...switch**ʱڷ֧оʹӡ־ŲʱͿͨ־ȷĸ֧ҲŲˡ
+
+
+
+
+
+```
+if(user.isVip()){
+ log.info("ûǻԱ,Id:{},ʼԱ",user,getUserId());
+ //Ա
+}else{
+ log.info("ûǷǻԱ,Id:{},ʼǻԱ",user,getUserId())
+ //ǻԱ
+}
+```
+
+## 5.־Ƚϵʱ־ж
+
+trace/debugЩȽϵ͵־𣬱־Ŀжϡ
+
+
+```
+User user = new User(666L, "ں", "ݵСк");
+if (log.isDebugEnabled()) {
+ log.debug("userId is: {}", user.getId());
+}
+```
+
+Ϊǰµ־룺
+```
+logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
+```
+
+**õ־warn**Ļ־ӡǻִַƴӲ```symbol```Ƕ
+ִ```toString()```˷ϵͳԴִ־ȴûдӡ˽**־жϡ**
+
+## 6. ֱʹ־ϵͳLog4jLogbackе APIʹ־SLF4JеAPI
+
+SLF4J ģʽ־ܣά־ʽͳһҿڱ֤Ĵ£ܷʵֵײ־ܵĸ
+
+
+
+
+```
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+private static final Logger logger = LoggerFactory.getLogger(TianLuoBoy.class);
+```
+
+## 7. ʹòռλ{}+ƴӡ
+
+
+```
+logger.info("Processing trade with id: " + id + " and symbol: " + symbol);
+```
+
+Уʹ```+```ַƴӣһ****
+
+£
+```
+logger.info("Processing trade with id: {} and symbol : {} ", id, symbol);
+```
+ʹ˴```{}```Ϊ־еռλʹ```+```żࡣң**ڷ**ʹռλ滻Чܡ
+
+## 8. ʹ첽ķʽ־
+
+- ־ջļеģIOܻҪġ첽ͿIOܡ
+- ҪҪȻʹ첽ķʽ־logbackΪɣҪ첽ܼʹAsyncAppender
+```
+
+
+
+```
+
+## 9. Ҫʹe.printStackTrace()
+
+
+
+
+
+
+```
+try{
+ // ҵ봦
+}catch(Exception e){
+ e.printStackTrace();
+}
+```
+
+```
+try{
+ // ҵ봦
+}catch(Exception e){
+ log.error("ij쳣",e);
+}
+```
+
+**ɣ**
+
+- e.printStackTrace()ӡĶջ־ҵ־ǽһģͨŲ쳣־̫㡣
+- e.printStackTrace()ַ¼ǶջϢϢַ̫̫࣬ڵڴûпռ,ڴˣôûͿס~
+
+## 10. 쳣־Ҫֻһ룬ҪȫϢ
+
+
+
+1
+
+```
+try {
+ //ҵ봦
+} catch (Exception e) {
+ //
+ LOG.error('ij쳣');
+}
+
+```
+- 쳣eûдӡѹ֪ʲô͵쳣
+
+2
+```
+try {
+ //ҵ봦
+} catch (Exception e) {
+ //
+ LOG.error('ij쳣', e.getMessage());
+}
+```
+
+- ```e.getMessage()```¼ϸĶջ쳣Ϣֻ¼ϢŲ⡣
+
+
+
+```
+try {
+ //ҵ봦
+} catch (Exception e) {
+ //
+ LOG.error('ij쳣', e);
+}
+```
+
+## 11. ֹϻ debug
+
+ֹϻdebugһdzҪ
+
+
+Ϊһϵͳdebug־ܶ࣬ҸֿҲʹ debug־Ͽdebugÿܻ̣ӰҵϵͳС
+
+## 12.Ҫ¼쳣׳쳣
+
+
+
+
+
+£
+```
+log.error("IO exception", e);
+throw new MyException(e);
+```
+
+- ʵֵĻͨջϢӡΡΪMyException쳣ĵطٴӡһΡ
+- ־¼߰װ׳ȥҪͬʱʹã־˺Ի
+
+
+## 13.ظӡ־
+
+ظӡ־ϻ˷Ѵ̿ռ䡣Ѿһ־˼**ӡ**£
+
+```
+if(user.isVip()){
+ log.info("ûǻԱ,Id:{}",user,getUserId());
+ //࣬Ըǰ־ϲһ
+ log.info("ʼԱ,id:{}",user,getUserId());
+ //Ա
+}else{
+ //ǻԱ
+}
+```
+
+ʹlog4j־ܣ```log4j.xml``` additivity=falseΪԱظӡ־
+
+
+```
+
+```
+
+## 14.־ļ
+
+
+
+
+
+- ǿѲͬ͵־ȥaccess.logerrorerror.logԵӡһļ档
+- ȻҲԸݲͬҵģ飬ӡͬ־ļŲͳƵʱȽϷ
+
+
+## 15. Ĺģ飬ӡ־
+
+
+
+
+
+- ճУĻӵĴ룬ϸעͣԼϸ־
+- ־ҪϸأԶһ£ĺijһˣͨ־ԶλǾͿ
+
+
+
+
+
+
From 93194b68365941438a9d904fa1b3e9c258f7e28a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=8D=A1=E7=94=B0=E8=9E=BA=E7=9A=84=E5=B0=8F=E7=94=B7?=
=?UTF-8?q?=E5=AD=A9?= <327658337@qq.com>
Date: Sun, 10 Oct 2021 23:17:47 +0800
Subject: [PATCH 05/50] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6018cc3..214c8ab 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## 个人公众号
-
+微信搜:捡田螺的小男孩
- 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论哈~~
From 6cd2682a3f262f88a9db736c5402df01c6aefc20 Mon Sep 17 00:00:00 2001
From: whx123 <327658337@qq.com>
Date: Mon, 6 Jun 2022 08:23:10 +0800
Subject: [PATCH 06/50] mhouduansiwei
---
...36\344\270\252\351\224\246\345\233\212.md" | 540 ++++++++++++++++
...03\347\224\250\346\250\241\346\235\277.md" | 599 ++++++++++++++++++
2 files changed, 1139 insertions(+)
create mode 100644 "\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md"
create mode 100644 "\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md"
diff --git "a/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md" "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md"
new file mode 100644
index 0000000..f999d6b
--- /dev/null
+++ "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md"
@@ -0,0 +1,540 @@
+## ǰ
+
+ҺãǼݵСкΪ˿ʲôԣ```Java``````Go``````C++```䱳ĺ˼붼Ƶġһ˼ļרҪ˵һЩơߺ˹淶صģϣԴճа
+
+˿ʦҪǣ**ΰһӿƺ**ԣҽܣƺýӿڵ36ҡľǺ˼רĵһƪ
+
+
+
+
+- ںţݵСк
+
+
+## 1. ӿڲУ
+
+γУÿԱرĻƵĽӿڣУǷΪգγǷԤڳȡҪϰ߹ճУܶͼbugDzУµġ
+
+> ݿֶΪ```varchar(16)```,Էһ32λַ㲻У**ݿֱ쳣**
+
+Ҳǣ㶨ĽӿڱģDzΪյģĽӿڷزûУ飬ΪijЩԭֱرһ```null```ֵ
+
+
+
+## 2. Ͻӿʱעӿڵļ
+
+ܶbugΪ˶ɽӿڣȴ****µġؼDZȽصģֱӵϵͳʧܵġֳԱŶ~
+
+
+
+ԣԭӿģӿǶṩĻһҪǽӿڼݡٸӰɣdubboӿڣԭֻABһCͿԿ
+
+```
+//Ͻӿ
+void oldService(A,B){
+ //½ӿڣnullC
+ newService(A,B,null);
+}
+
+//½ӿڣʱɾϽӿڣҪݡ
+void newService(A,B,C){
+ ...
+}
+```
+
+## 3. ƽӿʱֿǽӿڵĿչ
+
+ҪʵҵƽӿڣֿǽӿڵĿչԡ
+
+ӵһûӻԱʱҪˢǷṩһԱύˢϢӿڣ˼ύˢDzͨأת˻һҪˢĻǷҪʵһӿأǵǰҵͻģ飬ӿھͺãӿڵĿչԡ
+
+ģ黮ֵĻδһֽˢĻٸһµĽӿڣֻҪö٣Ȼˢͨ̽ӿڣʵһˢIJ컯ɡ
+
+
+
+
+## 4.ӿڿǷҪش
+
+ǰظδDzǿǽӿȥش
+
+ȻDzѯʵ÷ءǸĻתģҪظˡ㣬ʹRedisظͬһʱڵͬǷˡȻתӿڣߵĻ**Ƽʹݿر****ΨһˮΪΨһ**
+
+
+
+
+## 5. صӿڣ̳߳ظ롣
+
+һЩ½ת˽סµҪӿڣ̳߳ظҵһ̳߳أЩҵbug̳߳ĻǾͱˣ**ҵӰ**˽̳߳ظ룬Ҫҵһḷ́߳ñҪҵ
+
+
+
+
+## 6. õӿҪ쳣ͳʱ
+
+õӿڣ߷ֲʽԶ̷ĵĻҪǣ
+
+- 쳣
+
+> 磬˵Ľӿڣ쳣ˣôԻǵʧܻǸ澯
+
+- ӿڳʱ
+
+> ûԤԷӿһ÷أһøʱϿʱ䣬ԱĽӿڡ**֮ǰһ**httpòóʱʱ䣬Ӧ̼һֱռ̲߳ͷţϿ̳߳ء
+
+- Դ
+> Ľӿڵʧܣ費ҪԣԼΣҪվҵϽǶ˼
+
+
+
+
+## 7. ӿʵֿ۶Ϻͽ
+
+ǰϵͳһ㶼Ƿֲʽġֲʽϵͳоijãյϵͳõ, Ϊ**ѩЧӦ**
+
+ֲʽ·```A->B->C....```ͼʾ
+
+
+
+> C⣬**ΪSQLµû**ǽBҲӳ٣ӶAҲӳ١סAռϵͳ̡߳IOԴ AķԽԽ࣬ռüԴҲԽԽ࣬ջᵼϵͳƿ֣ͬãҵϵͳ
+
+ΪӦԷѩ, **۶Ϻͽ**ǼӿؿƣϵͳʱؽٵϵͳѡÿԴ```Hystrix```
+
+## 8. ־ӡãӿڵĹؼ룬Ҫ־ݻ
+
+ؼҵεأӦ㹻־ݻ
+磺ʵתҵתȻתʧˣſͻͶߣȻ㻹ûдӡ־ˮȵ£ȴް취
+
+ôתҵҪЩ־Ϣأ٣ǰҪӡҪɣӿڵúҪһ쳣ɣͬʱӡ쳣־ɣ£
+```
+public void transfer(TransferDTO transferDTO){
+ log.info("invoke tranfer begin");
+ //ӡ
+ log.info("invoke tranfer,paramters:{}",transferDTO);
+ try {
+ res= transferService.transfer(transferDTO);
+ }catch(Exception e){
+ log.error("transfer fail,account{}",
+ transferDTO.getAccount)
+ log.error("transfer fail,exception:{}",e);
+ }
+ log.info("invoke tranfer end");
+ }
+```
+
+֮ǰдһƪӡ־15飬ҿԿ[ܽᣡ־ӡ15](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect)
+
+## 9. ӿڵĹܶҪ߱һ
+
+һָӿȽϵһרһһ½ӿڣֻУ˻룬Ȼص½ɹԼ```userId```ɡ**Ϊ˼ٽӿڽһЩעᡢһЩòѯȫŵ½ӿڣͲ̫ס**
+
+ʵҲһЩ˼룬ӿڵĹܵһȷ綩֡ƷϢصĽӿڶǻֿġĻDzǾͱȽϼ
+
+
+## 10.ӿЩʹ첽
+
+ٸӣʵһûעĽӿڡûעɹʱʼ߶ȥ֪ͨûʼ߷ţʺ첽Ϊܲһ֪ͨʧܣעʧܰɡ
+
+첽ķʽľ**̳߳**ʹϢУûעɹ߲һעɹϢעɹϢͷ֪ͨ
+
+
+
+
+еĽӿڶʺΪͬӿڡҪһת˵Ĺܣǵʵתˣǿѽӿͬûתʱͻھȴת˽ͺáתˣһһǧʣһʵģѽӿΪ첽ûתʱ־ûɹȷɹȻûʮӻʮӵת˽ͺáֻߣת˳ɹٻصϵͳ
+
+
+
+
+
+## 11. ŻӿںʱԶ̴пǸIJе
+
+һAPPҳĽӿڣҪûϢҪbannerϢҪ鵯ϢȵȡһһӿڴеDzеأ
+
+
+
+
+Ǵһһ飬ûϢ200msbannerϢ100ms鵯Ϣ50msһͺʱ```350ms```ˣϢǺʱˡֳǿԸΪеõġҲ˵ûϢbannerϢ鵯Ϣͬʱ
+
+
+
+
+Javaи첽```CompletableFuture```ͿԺܺʵܡȤСԿ֮ǰ¹[CompletableFuture](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=1260947715&lang=zh_CN#rd)
+
+## 12. ӿںϲ˵˼
+
+ݿԶ̵ʱͲҪforѭá
+
+
+һӣƽʱһбϸݲݿʱҪforѭһһ룬һμ롣ͬԶ̵Ҳ뷨ѯӪǩǷУһǩһǩȥ飬Ҳǩȥ飬УЧʾ
+
+```
+//
+for(int i=0;i һЩƽʱ䶯С˵ƷϢԷŵ棬ʱȲѯ棬ûٲݿ⣬Ұݿݸµ档ǣʹûҪЩ㣺ݿһα֤Ⱥѩ洩⡣
+
+- ֤ݿͻһԣ**ʱ˫ɾɾԻơȡbiglog첽ɾ**
+-
+- ѩRedisȺ߿áùʱ
+- 洩ӿڲУ顢ѯΪøĬϿֵǡ¡
+
+һ```Redis```ֲʽ棬ȻЩʱҲԿʹñػ棬```Guava CacheCaffeine```ȡʹñػЩȱ㣬дݴ洢Ӧý̵ʧЧ
+
+## 14. ӿڿȵݸ
+
+˲ʱĸ߲ܻϵͳһЩȵݵĸ롣**ҵ롢ϵͳ롢û롢ݸ**ȡ
+
+- ҵԣ12306ķʱƱȵݷɢϵͳѹ
+- ϵͳ룺ϵͳֳûƷ顣ֱʹòͬݿ⣬ӽ㵽Ӧòٵݲȫ롣
+- û룺صûøõĻ
+- ݸ룺ʹõĻ漯Ⱥݿȵݡ
+
+## 15. ɱûƤл
+
+Ʒ˸ʥڵʱƤΪʥصģڵʱΪںƤȡ
+
+ڴдƣ´룺
+```
+if(duringChristmas){
+ img = redPacketChristmasSkin;
+}else if(duringSpringFestival){
+ img = redSpringFestivalSkin;
+}
+```
+ԪڵʱӪСͻȻ뷨ƤɵصģʱDzҪȥĴˣ·ˣ
+
+һʼӿʱʵ**һźƤñ**ƤûأƤֻһ±ݾͺˡ
+
+ȻһЩʺһЩûIJһҳơijʱЩԸ㵽û档**Ҳչ˼һ֡**
+
+## 16.ӿڿݵ
+
+ӿҪݵԵģתЩҪӿڡֱ۵ҵ**ûŵ**Ľӿû**holdס**Ϣгظѵҵôƣ
+
+£**ʲôݵȣ**
+
+> ѧУݵȱʾһκͶijһԴӦþͬĸã˵ӰһִеӰЧͬ
+
+ұ**غݵʵ**ҪΪ˱ظݣظɡݵƳѾҪÿͬһЧأܶʱǵĴ̡ƵĹ
+
+
+
+
+ӿݵʵַҪ8֣
+
+- select+insert+/Ψһͻ
+- ֱinsert + /Ψһͻ
+- ״̬ݵ
+- ȡر
+- token
+-
+- ֹ
+- ֲʽ
+
+ҿԿƪ¹[ݵ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=1260947715&lang=zh_CN#rd)
+
+## 17. д룬ȿǶӿ⣬עӳ
+
+ǵݿⶼǼȺģҲдӿ⣬ǰһ㶼Ƕдġдݣ϶д⣬ǶڶȡʵʱҪߵݣȿǶӿ⣬ΪԷֵѹ
+
+ȡӿĻҪӳٵ⡣
+
+## 18.ӿעⷵصҪҳ
+
+һӿڷرģӦðӣѹҲdzʵDZȽϴԷҳأǹܲصıģӦÿǽӿڲ֡
+
+## 19. õĽӿʵ֣벻SQLŻ
+
+˵ģдһӿڣ벻SQLŻ
+
+SQLŻ⼸ά˼
+
+- explain SQLѯƻصעtypeextrafilteredֶΣ
+- show profile˽SQLִе̵߳״̬Լĵʱ
+- Ż ǰԭʽתorder byԼgroup byŻjoinŻ
+- ҳŻӳٹ¼һҳID
+- ̫**ֱֿ**ͬesesѯ
+
+## 20.ȿƺ
+
+ʲôǼأ
+
+> ʵǾҪסķΧǶڼ䣬ֻҪסͿ˰ɣҪҶü˽Űɣļȡ
+
+дʱ漰ԴûбҪסġͺ䣬ðҶססžͿˡ
+
+磬ҵУһArrayListΪ漰̲߳ҪպһαȽϺʱIJе```slowNotShare```漰̰߳ȫ⣬μأ
+
+
+```
+//漰Դ
+private void slowNotShare() {
+ try {
+ TimeUnit.MILLISECONDS.sleep(100);
+ } catch (InterruptedException e) {
+ }
+}
+
+//ļ
+public int wrong() {
+ long beginTime = System.currentTimeMillis();
+ IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
+ //̫ˣslowNotShareʵ漰Դ
+ synchronized (this) {
+ slowNotShare();
+ data.add(i);
+ }
+ });
+ log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
+ return data.size();
+}
+```
+
+
+```
+public int right() {
+ long beginTime = System.currentTimeMillis();
+ IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
+ slowNotShare();//Բ
+ //ֻListⲿּ
+ synchronized (data) {
+ data.add(i);
+ }
+ });
+ log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
+ return data.size();
+}
+```
+
+## 21.ӿ״̬ʹҪͳһȷ
+
+ṩҪĽӿڵ״̬Ϣһת˽ӿڵdzɹʧܡлɹȣҪȷ߿ͻˡӿʧܣôʧܵԭʲôЩҪϢҪ߸ͻˣҪȷĴͶӦͬʱԱϢװһ£ҪѺ˵쳣Ϣȫ׳ͻˡ
+
+
+
+
+## 22.ӿҪ쳣
+
+ʵһõĽӿڣ벻ŵ쳣쳣ʮСɣ
+
+- Ҫʹ```e.printStackTrace()```,ʹ```log```ӡΪ```e.printStackTrace()```ܻᵼڴռ
+- ```catch```ס쳣ʱӡ```exception```ڸöλ
+- Ҫһ```Exception```пܵ쳣
+- ǵʹ```finally```رԴֱʹ```try-with-resource```
+- 쳣׳쳣ȫƥ䣬߲쳣쳣ĸ
+- 쳣ܺٴ־
+- ע쳣ĴνṹȾ
+- Զװ쳣Ҫԭʼ쳣Ϣ```Throwable cause```
+- ʱ쳣```RuntimeException``` Ӧͨ```catch```ķʽԤ飬磺```NullPointerException```
+- ע쳣ƥ˳Ȳ쳣
+
+СȤԿ֮ǰдƪ¹[Java 쳣ʮ](https://mp.weixin.qq.com/s/3mqY77c8iXWvJFzkVQi9Og)
+
+## 23. Ż
+
+Ż黹ͦҪģҲ˵ʵֵҵ룬**DZȽϸӵĻעд**У뾡Ч
+
+> 磬ҪʹûϢԣsessionѾȡ```userId```ˣȻͰûϢݿѯʹҪõûϢԣЩСû̫࣬־Ͱ```userId```ٴȥٲһݿ⡣ĿУִ롣ֱӰû
+
+α룺
+
+```
+public Response test(Session session){
+ UserInfo user = UserDao.queryByUserId(session.getUserId());
+
+ if(user==null){
+ reutrn new Response();
+ }
+
+ return do(session.getUserId());
+}
+
+public Response do(String UserId){
+ //һݿ
+ UserInfo user = UserDao.queryByUserId(session.getUserId());
+ ......
+ return new Response();
+}
+
+```
+
+
+
+```
+public Response test(Session session){
+ UserInfo user = UserDao.queryByUserId(session.getUserId());
+
+ if(user==null){
+ reutrn new Response();
+ }
+
+ return do(session.getUserId());
+}
+
+//ֱӴUserInfoɣٶһݿ
+public Response do(UserInfo user){
+ ......
+ return new Response();
+}
+```
+
+ȻֻһЩСһӣкܶƵӣҪҿУ˼Ĺ
+
+
+## 24. ӿʵֹ̻Уעļ
+
+- ȡļʱҪ```Files.readAllBytes```ֱӶȡڴ棬OOMģʹ```BufferedReader```һһ
+- ܵعʱ䳤ӳٵ⣬о
+- עһЩʹãΪֱӽģᴥfullGC
+
+## 25. ĽӿڣҪ
+
+ϵͳÿ뿸ס1000һʮأǶȾ˵߲ʱˣϵͳijôأ
+
+ȡʩеϵͳCPUڴ桢Load쮵ĺܸߣеӦ
+
+ֳǿԲΪ˱ϵͳֱӶ
+
+壺
+> ڼУǿӿڷͻʣɷֹDoSWeb档Ҳơָϵͳٸ߲ߴ£µϵͳķʣӶ֤ϵͳȶԡ
+
+ʹGuava```RateLimiter```Ҳʹ```Redis```ֲʽʹð↑Դ```sentinel```
+
+ҿԿ֮ǰƪ¹[4־㷨](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490393&idx=1&sn=98189caa486406f8fa94d84ba0667604&chksm=cf21c470f8564d665ce04ccb9dc7502633246da87a0541b07ba4ac99423b28ce544cdd6c036b&token=162724582&lang=zh_CN&scene=21#wechat_redirect)
+
+
+## 26.ʵʱעʱ쳣ָ롢±Խȣ
+
+ճУҪȡʩ**ָ߽**ʱƴȽϳ
+```
+String name = list.get(1).getName(); //listԽ磬Ϊһ2Ԫع
+```
+
+ӦòȡʩԤһ߽£
+```
+if(CollectionsUtil.isNotEmpty(list)&& list.size()>1){
+ String name = list.get(1).getName();
+}
+```
+
+
+
+## 27.֤ӿڰȫ
+
+APIӿǶṩģҪ֤ӿڵİȫԡ֤ӿڵİȫ**tokenƺͽӿǩ**
+
+**token֤**Ƚϼģ
+
+
+
+1. ͻ˷ȡtoken
+2. ȫΨһtoken浽redisУһһʱ䣩Ȼظͻˡ
+3. ͻ˴token
+4. ȥredisȷtokenǷڣһ redis.del(token)ķʽڻɾɹҵɾʧܲҵֱӷؽ
+
+**ӿǩ**ķʽǰѽӿϢģʱ汾šappidȣͻ˽ԿǩȻùԿǩ֤ͨΪǺϷġûб۸Ĺ
+
+йڼǩǩģҿԿƪ¹[Աرǩǩ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect)
+
+**ǩǩtokenƣӿڱһҪܵ**ȻhttpsЭǻԱļܵġǷĻμӽأ
+> ԲοHTTPSԭǷ˰ѹԿͻˣȻͻɶԳԿſͻ÷˵ĹԿܶԳԿٷˣԼ˽Կܣõͻ˵ĶԳԿʱͿ촫䱨ͻ**ԳԿ****öӦĶԳԿܱ**
+
+ʱӿڵİȫԣ**ֻš֤Ϣ**˵**û˽ݣ㱩¶**
+
+## 28.ֲʽα֤
+
+> ֲʽָIJߡ֧ķԴԼֱλڲͬķֲʽϵͳIJͬڵ֮ϡ˵ֲʽָľǷֲʽϵͳеĴھΪ˱֤ͬݿڵһԡ
+
+ֲʽļֽ
+- 2PC(ύ)3PC
+- TCCTryConfirmCancel
+- Ϣ
+- Ŭ֪ͨ
+- seata
+
+ҿԿƪ¹[һ⣺ֲʽ](https://mp.weixin.qq.com/s/3r9MfIz2RAtdFhYzwwZxjA)
+
+## 29. ʧЧһЩ䳡
+
+ǵĽӿڿУҪʹõҪܿʧЧһЩ䳡
+
+- ķȨޱpublicprivateȨޣʧЧ
+- finalģᵼʧЧ
+- ͬһеķֱڲãᵼʧЧ
+- һûspringͲspring
+- ̵߳ãͬһ߳УȡݿӲһġ
+- Ĵ洢治֧
+- Լtry...catch쳣ʧЧ
+- Ĵ
+
+Ƽҿƪ£[springʧЧ12ֳ̫](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494570&idx=2&sn=17357bcd328b2d1d83f4a72c47daac1b&chksm=cf223483f855bd95351a778d5f48ddd37917ce2790ebbbcd1d6ee4f27f7f4b147f0d41101dcc&token=2044040586&lang=zh_CN&scene=21#wechat_redirect)
+
+
+## 30. ճõģʽ
+
+ѴдãҪõģʽģʽģʽģ巽ģʽ۲ģʽȵȡģʽǴƾܽᡣʹģʽԿô롢ôױ⡢֤ɿԡ
+
+֮ǰдһƪܽṤгģʽ£дͦģҿԿ£[ʵսгõЩģʽ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495616&idx=1&sn=e74c733d26351eab22646e44ea74d233&chksm=cf2230e9f855b9ffe1ddb9fe15f72a273d5de02ed91cc97f3066d4162af027299718e2bf748e&token=1260947715&lang=zh_CN#rd)
+
+## 31. дʱȫ
+
+**߲**£```HashMap```ܻѭΪǷȫģԿʹ```ConcurrentHashMap```ҲϰߣҪ־һ```new HashMap()```;
+
+> - HashmapArraylistLinkedListTreeMapȶԲȫģ
+> - VectorHashtableConcurrentHashMapȶȫ
+
+
+
+
+## 32.ӿڶ淶
+
+д룬ΪʵֵǰĹܣҲҪںά˵ά벻дԼģҲǸ˿ġԽӿڶҪ淶
+
+## 33. ӿڵİ汾
+
+ӿҪð汾ơ˵ģӦð```version```ӿڰ汾ֶΣδӿڼݡʵҲӿչԵһֵɡ
+
+ͻAPPijŻˣϰ汾Ṳ棬ʱǵ```version```汾žóˣ```version```ð汾ơ
+
+## 34. ע淶
+
+עһЩĴ뻵ζ
+- ظ루鹫÷ģʽ
+- ࣨɷװһDTO
+- С
+- ж̫ࣨŻif...else
+- ûõĴ
+- עشʽ
+-
+
+ĻζҶд[25ִ뻵ζܽ+Żʾ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect)
+
+## 35.֤ӿȷԣʵDZ֤ٵbug
+
+֤ӿڵȷԣǶȽDZ֤ٵbugûbugԽӿڿһҪ**Բһ**ȻĻӿڵȷڣ̲߳ʱ**֤ݵȷ**,ȵȡһת˽ףۼʱͨCASֹķʽ֤ۼȷɡ
+
+ʵɱӿڣ÷ֹɡʹRedisֲʽֹ⡣ʹRedisֲʽмעҪ㣬ҿԿ֮ǰƪ¹[ַ̽Redisֲʽȷʹ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect)
+
+## 36.ѧṵͨǰ˹ͨƷͨ
+
+ҰһŵѧṵͨǷdzdzҪġ㿪ӿʱ**һԼͷѽӿڶ****Ҫͻȶӿ**һЩѵʱleader뷽ʵĹУʲô⣬ʱƷͨ
+
+֮ǣӿڹУһҪͨ~
+
+
+## (ע)
+
+ƪ¶ĻӭעҵĹںţݵСк
+
+
diff --git "a/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md" "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md"
new file mode 100644
index 0000000..762cb1b
--- /dev/null
+++ "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md"
@@ -0,0 +1,599 @@
+## ǰ
+
+ҺãǼݵСк
+
+Ǻ˼άרĵڶƪһƪ[36ƽӿڵĽ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499388&idx=1&sn=49a22120a3238e13ad7c3d3b73d9e453&chksm=cf222155f855a8434026b2c460d963c406186578c2527ca8f2bb829bbe849d87a2392a525a9b&token=1380536362&lang=zh_CN#rd)õdzСϿɡ
+36ƽӿڵĽҲᵽһ㣺**ʹòеŻӿ**ԽͿӱޣдڶƪְֽдһеģ塣
+
+- һеõӣAppҳϢѯ
+- CompletionServiceʵֲе
+- ȡͨõIJе÷
+- ˼ԼģʽӦ
+- ˼ܽ
+- ںţ**ݵСк**
+
+
+## 1. һеõ
+
+һAPPҳѯĽӿڣҪûϢҪ```banner```ϢҪǩϢȵȡһСʵ£
+
+```
+public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) {
+ //ûϢ
+ UserInfoParam userInfoParam = buildUserParam(req);
+ UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
+ //bannerϢ
+ BannerParam bannerParam = buildBannerParam(req);
+ BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
+ //ǩϢ
+ LabelParam labelParam = buildLabelParam(req);
+ LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
+ //װ
+ return buildResponse(userInfoDTO,bannerDTO,labelDTO);
+}
+```
+
+δʲô ʵһͦĴ룬ʵУѯûbannerǩϢ**Ǵе**ѯûϢ```200ms```ѯbannerϢ```100ms```ѯǩϢ```200ms```Ļʱ```500ms```
+
+
+
+ʵΪŻܣǿΪ**е**ķʽʱԽΪ```200ms```ͼʾ
+
+
+
+
+
+## 2. CompletionServiceʵֲе
+
+ӣ**ʵֲеأ**
+
+С˵ʹ```Future+Callable```ʵֶIJеáִ̳߳ʱֵ```Futureget()```ȡģǰһִбȽϺʱĻ```get```γŶӵȴ
+
+```CompletionService```ǶԶ```ExecutorService```˰װһ,һȡķֵ·ִֿ,֮䲻ụԻȡɵ
+
+
+> ```CompletionService```ʵԭȽϼײͨFutureTask+УʵɵĻȻȡҲ˵ִнɵȺ˳ɿŻȡڲһȽȳУڱѾִɵFuture```CompletionService```polltakeɻȡһѾִɵFutureͨFutureӿʵ```get```ȡյĽ
+
+
+
+
+£```CompletionService```ʵֲвѯAPPҳϢ˼£
+
+1. ȰѲѯûϢŵ̳߳أ£
+```
+ExecutorService executor = Executors.newFixedThreadPool(10);
+//ѯûϢ
+CompletionService userDTOCompletionService = new ExecutorCompletionService(executor);
+Callable userInfoDTOCallableTask = () -> {
+ UserInfoParam userInfoParam = buildUserParam(req);
+ return userService.queryUserInfo(userInfoParam);
+ };
+userDTOCompletionService.submit(userInfoDTOCallableTask);
+```
+
+2. Ѳѯ```banner```ϢҲŵ̳߳صĻֲ÷ˣΪͲһһ```UserInfoDTO```һ```BannerDTO```ʱDzǰѷΪObjectɣΪжǼ̳Objectģ£
+
+```
+ExecutorService executor = Executors.newFixedThreadPool(10);
+//ѯûϢ
+CompletionService