408准备复试过程中整理的知识点(一)
本文是几个markdown的合并,所以结构较乱,仅记录痕迹;
数据结构
双栈实现队列
《剑指Offer》09-用两个栈实现队列 - 分享技术成长历程~ - 博客园 (cnblogs.com)
散列表及冲突解决
散列表建立了关键字和存储地址之间的一种直接映射关系;从而加快查找速度;
散列表通常用于牺牲空间复杂度,来换取时间上的高效;
冲突:在散列函数散列关键字的过程中,可能会把多个不同的关键字映射到同一个地址;
减少冲突:好的散列函数可以减少冲突;
冲突是不可避免的,所以还要设计好处理冲突的办法;
处理冲突:
- 开放定址法:空闲空间既向当前关键字的同义词开放,又向其非同义词开放;
- 线性探测法、平房探测法、再散裂法;
- 拉链法:把同义词用链表链接;
TOPN问题
海量数据中取最大的N个数:如,10亿个数中找出最大的10000个数;
首先要了解堆排序;
- 先拿10000个元素建堆;---O(n)
- 遍历剩下的所有,每次将一个元素加入堆,并调整堆,只保留前10000个元素(也就是每次插入后,最么一个元素不要,一直保持10000个元素堆);---O(logn)
- 遍历完所有,剩下的就是要找的TOP 10000;---O(nlogn)
优化方式:
可以把所有10亿个数据分组存放,比如分别放在1000个文件中;这样处理就可以分别在每个文件的10^6个数据中找出最大的10000个数,合并到一起在再找出最终的结果。
图
排序算法
稳定性判别法:若存在不相邻元素间交换,则是不稳定的排序;
插入排序:直接插入排序
时间复杂度:
最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为O(n) ;
最坏的情况下:逆序有序,这样每一个元素就需要比较n次,共有n个元素,因此实际复杂度为O(n2);
平均情况下:O(n2);
空间复杂度:可能借用了“哨兵”,O(1);
稳定性:直接插入排序是稳定的;(一个一个挨着来,不存在不相邻元素的交换,所以是稳定的;)
插入排序:希尔排序
希尔排序也是一种插入排序方法,实际上是一种分组插入方法。先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt=1(dt<dt-1<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止;
增量d=1时,相当于一次直接插入排序;
时间复杂度:
最好情况:由于希尔排序的好坏和步长d的选择有很多关系,所以其最好情况的时间复杂度暂无定论;
最坏情况下:O(NlogN);
平均情况下:O(NlogN);(d逐渐减为1,形状上类似下宽上窄的树形,因此有logn);
空间复杂度:O(1);
稳定性:不稳定;因为有增量的原因,所以存在不相邻元素的交换,所以是不稳定的;
交换排序:冒泡排序
通过无序区中相邻记录关键字间的比较和位置的交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”,所以叫冒泡排序;
时间复杂度 :
最好情况下:正序有序,则只需要比较n次。故,为O(n);
最坏情况下:逆序有序,则需要比较(n-1)+(n-2)+……+1,故,为O(n2);
平均情况下:O(n2);
这里发现一个Markdown小语法:在Typora中 shift + enter = 单回车,而enter = 双回车。
空间复杂度:O(1);
稳定性:稳定的;因为不存在不相邻元素的交换;
交换排序:快排
快排是分治法的思想;
- 找枢轴元素,根据枢轴(pivot)元素划分子序列(如,大于pivot的移动到枢轴右侧,小于枢轴的移动到枢轴左侧);
- 分别对枢轴左右两侧的子序列再进行快排;
- 每次可以确定枢轴元素位置即为最终位置;
- 采用递归;
分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题,原问题的解即子问题的解的合并。
时间复杂度:
最好情况:O( nlogn );每一次取到的枢轴元素都刚好平分数列;
最坏情况下:O( n2);即每一次取到的枢轴元素,都使该数列处于枢轴的同一侧;
平均情况下:O( nlogn );
空间复杂度:因为快排算法采用递归,所以空间复杂度主要来源是递归调用栈;
最好情况:O( logn );每一次取到的枢轴元素都刚好平分数列;
最坏情况下:O( n);即每一次取到的枢轴元素,都使该数列处于枢轴的同一侧;
稳定性:不稳定;因为存在不相邻元素的交换,所以是不稳定的;
选择排序:直接选择排序
每次直接选择最大或最小元素放在最终位置上;
时间复杂度:O( n2);
空间复杂度:O(1);
稳定性:不稳定;因为存在不相邻元素的交换,所以是不稳定的;
堆排序
有时候需要的不是所有元素的“排序”,而是找最大或最小的部分;比如最大/小的元素,topK之类,这时候堆排序的优势就出来了;用堆排序可以在N个元素中找到top K,时间复杂度是O(N log K),空间复杂的是O(K);
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
给定一个数组,将其进行堆排序,首先第一步要建堆(构造堆);
建堆(以大顶堆为例):
- 初始数组可以看做为一个顺序存储的完全二叉树;
- 自下往上逐步调整为大顶堆;
堆的插入:
- 将新元素插入到堆的末尾;
- 再将其调整为一个大根堆;
删除根节点(也就是取得当前最大/小的关键字):
- 将堆顶元素与堆尾元素交换,并删除交换后的堆尾元素;
- 重新调整为大根堆;
时间复杂度(完全排序):O( nlogn );
- 建堆(初始化堆):对完全二叉树进行从下往上逐层调整为大/小根堆;耗时O(n);
- 排序:
- 根与最末的元素交换,删除,再调整;---每个花费时间为O(logN);和层数相关;
- 重复直到只剩下最后一个节点,所以关键字排序完成;---n个所以O( nlogn );
- 所以总的是O(n)+O( nlogn ) -> O( nlogn );
时间复杂度(找TOPK):O( klogn );
- 建堆O(n);
- 排序:
- 根与最末的元素交换,删除,再调整;---每个花费时间为O(logN);和层数相关;
- 因为只用找TopK个,所以重复K次;---K个所以O( Klogn );
空间复杂度:O(1);因为使用了常数个辅助单元;
稳定性:不稳定;因为存在不相邻元素的交换,所以是不稳定的;
归并排序
时间复杂度:O( nlogn );因为“树形”结构;
空间复杂度:O(n);因为Merge()函数中,辅助空间刚好为n个;
稳定性:稳定;(简单记忆,二路归并,两个两个相邻元素的交换);
排序算法总结
这个表快排的空间复杂度错了,应为O(logn)
参考目录
常见排序算法小结_whuslei的博客-CSDN博客_常用排序算法
[海量数据处理 - 10亿个数中找出最大的10000个数(top N问题)_大数据架构师Pony的博客-CSDN博客](
操作系统
内核态、用户态
-
内核态: CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU 也可以将自己从一个程序切换到另一个程序,可以执行指令集合的全集;
-
用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU 资源可以被其他程序获取,只能执行非特权指令;
进程与线程的区别
-
一个进程由一个或多个线程构成;
-
进程是资源分配的最小单位,线程是CPU调度的最小单位;
-
进程间的地址空间相互独立(内存保护);
-
线程不拥有资源(少量线程栈),同一进程内的所有线程共享该进程的所有资源;
各线程共享相同的代码和全局数据,但各有其自己的堆栈;
-
同一进程内的线程切换,不会引起进程切换;不同进程的线程切换会导致进程切换;
-
同一进程的线程、不同进程的线程都可以并发;
多线程
前提:线程虽然共享进程的所有资源,但线程也有少量的线程栈,在线程切换时也需要保存少量的线程现场;
多线程,是否一定提高效率了?
不一定的,要看情况。线程是CPU调度的最小单位,如果一个程序CPU占用时间较长,那多线程提高效率并不明显;但如果程序CPU长时间很闲,io较多,这时候多线程效率就提升很多,因为多线程就是为了充分利用CPU资源。而且线程的切换比进程的切换快很多。
进程间通信
为什么需要进程间通信?
- 有时想要在多个个进程之间实现数据传输、资源共享、通知事件(例如进程终止时会通知父进程)以及进程控制(debug进程可以控制其他进程的执行)等需要进程间“交流”,但我们知道进程与进程之间是相互独立的,有独立的虚拟地址空间,所以进程间通信是很难的,由此就引出了我们的几大进程间通信的方式:管道、消息队列、共享内存、信号量。接下来就是分别描述这几个方式。
管道:
- 本质是一份文件,不同进程都可以访问的文件;互斥访问;
- 半双工通信,读空;
- 缺点:a 进程给 b 进程传输数据,只能等待 b 进程取了数据之后 a 进程才能返回;因此不适合频繁的进程通讯;
消息队列:
-
把进程的数据放在消息队列后就马上让进程返回,无需等待其他进程来取就返回呢;
-
a 进程要给 b 进程发送消息,只需要把消息放在对应的消息队列里就行了,b 进程需要的时候再去对应的消息队列里取出来。同理,b 进程要个 a 进程发送消息也是一样。这种通信方式也类似于缓存;
-
缺点:进程发送的数据占的内存比较大,并且两个进程之间的通信特别频繁的话,消息队列模型就不大适合了。因为进程发送的数据很大的话,意味发送消息(拷贝)这个过程需要花很多时间来读内存;
共享内存:
- 共享内存这个通信方式就可以很好着解决拷贝所消耗的时间了,(因为不需要拷贝数据,共享同一块内存);
每个进程不是有自己的独立内存吗?两个进程怎么就可以共享一块内存了?
系统加载一个进程的时候,分配给进程的内存并不是实际物理内存,而是虚拟内存空间(如分页,分段,段页),那么就可以让两个进程各自拿出一块虚拟地址空间来,然后映射到相同的物理内存中,这样,两个进程虽然有着独立的虚拟内存空间,但有一部分却是映射到相同的物理内存,这就完成了内存共享机制了;
信号量:
如果说上面几种进程通信方式偏向于进程间数据共享的话,那么信号量方式是用来解决进程同步,进程互斥问题;
- 信号量本质就是一个计数器,用来实现程同步,进程互斥;
Socket:
上面说的共享内存、管道、信号量、消息队列,都是多个进程在一台主机之间的通信,那物理上相隔的进程能够进行通信吗?
就是利用socket这种方式,其核心是ip+端口用来表示一个网络内的某个进程,从而实现端对端通信;
死锁
死锁:多个进程因为竞争资源造成的一种僵局,没有外力作用,这些进程都无法向前继续推进;
死锁产生的四个必要条件
- 互斥条件
- 不剥夺条件
- 请求并保持条件
- 循环等待:存在互斥资源的循环等待链;
解决死锁的三个方法
死锁预防、死锁避免、死锁的检测及解除
- 死锁预防:破解死锁产生的四个必要条件;
- 死锁避免:银行家算法(避免使系统进入不安全状态;),若存在一条安全序列,就不是不安全状态;
- 死锁检测及解除:若死锁已经发生,要能检测到,并且能够解除;
- 强制性地从系统中撤消一个或多个死锁的进程以断开循环等待链,并收回分配给终止进程的全部资源供剩下的进程使用;
- 使用一个有效的挂起和解除机构来挂起一些死锁的进程,其实质是从被挂起的进程那里抢占资源以解除死锁;
中断
-
中断的作用:
在 CPU 每执行完一条指令后,查询是否有中断请求,如果没有中断请求,CPU 继续执行;若有中断请求,会中断原先占用 CPU 的程序的执行,把被中断程序的断点及现场保存起来,让操作系统的处理服务程序占用 CPU 对事件进行处理,处理完后,再恢复被中断的程序并继续执行下去。
-
内中断与外中断
内中断:指令执行时(CPU,内存内部)产生的中断,如缺页中断,除零;
外中断:由外部中断源引起的,如键盘;
-
中断处理过程
关中断:在保护断点和现场期间不能被新的中断所打断,否则在中断服务程序结束后就不能正确的恢复;
引出中断服务程序:中断服务程序的入口地址;
虚拟内存和分页、分段、段页式
- 虚拟内存技术:操作系统将内存中暂时不使用的内容换出到外存上,从而腾出内存空间来调入要用到的内容,在逻辑上为用户提供了一个比实际内存大的多的存储器;(核心还是局部性原理)
虚拟内存技术必不可少的要将一个进程(当前要用/当前不用)的部分换入或换出内存,就要考虑把这个进程怎么分,平均按页,按段,还是段页,这就引出了非连续的内存的分配方式;
- 非连续的内存分配方式(分页、分段、段页式)
- 分页是为了提高内存的利用率,提高计算机性能,且分页通过硬件机制来实现,对用户完全透明。
- 分段是为了方便编程,信息保护和共享、动态增长及动态链接等多方面的需要。
- 断页式是两者的结合。
缺页
根据逻辑地址获取页号,根据页号先在快表中查找,若快表中没有再去内存中的页表查找,若页表中也没有此时即为缺页;
操作系统在发生缺页中断时,是否一定会执行页面淘汰算法?
- 不一定;若内存中的页表还有空的页表项,只需要直接调入不需要进行页面置换;
IO方式
程序查询,中断,DMA
-
程序直接控制:CPU需要对外设状态进行循环检查,直到确定该字已经在I/O控制器的数据寄存器中;
由于CPU的高速性和I/O设备的低速性,CPU的绝大部分时间都在等待状态;
-
中断:由I/O设备主动打断CPU的运行,并请求服务,数据传送时依旧占用CPU;
-
DMA方式:在IO设备和内存之间开辟直接的数据交换通路,彻底解放CPU;
DMA的传送过程
- DMA:在IO设备和内存之间开辟直接的数据交换通路,彻底解放CPU;
- DMA的数据传送过程分为三个阶段:预处理、数据传送、后处理;
- 预处理:CPU完成一些必要的准备工作;(如,测试DMA控制器状态,I/O设备状态);
- 数据传送:数据传送阶段完全由DMA(硬件)控制,DMA和CPU并行;
- 后处理:DMA控制器向CPU发送中断请求,CPU执行中断服务程序做DMA结束处理;
DMA 的优先级为什么比 CPU 的优先级高
因为 DMA 请求得不到及时响应,I/O 传输数据可能会丢失
计算机网络
TCP三次握手,四次挥手
三次握手
- 第一次握手(SYN=1, seq=x):
- 客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号x,保存在包头的序列号(Sequence Number)字段里;
- 发送完毕后,客户端进入
SYN_SEND
状态;
- 第二次握手(SYN=1, ACK=1, seq=y, ack=x+1):
- 服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1;
- 服务器端选择自己 ISN 序列号,放到 seq 域里,同时将确认序号ack设置为客户的 ISN 加1,即x+1;
- 发送完毕后,服务器端进入
SYN_RCVD
状态;
- 第三次握手(ACK=1,ACKnum=y+1)
- 客户端再次发送确认包(ACK),ACK 标志位为1,seq=x+1;ack=y+1;
- 发送完毕后,客户端进入
ESTABLISHED
状态,当服务器端接收到这个包时,也进入ESTABLISHED
状态,TCP链接建立;
三次握手而不是二次握手?
解释一(主要):
防止已经失效的请求连接报文突然又传送到了服务器,从而产生错误(也可能造成死锁);
两次握手发送ACK应答即认为链接建立!如下情况:
- 如“两次握手”建立连接,假设有这样一种场景,客户端发送了第一个SYN连接请求服务端收到了;
- 服务端接收到后,发送ACK确认链接,如果这个ACK报文在网络中丢失了;
- 此时服务端还在等着服务端发送的ACK确认链接(但ACK报文已经丢失),而服务端发送了ACK应答(在两次握手模式下发送ACK应答即认为链接建立),认为链接建立;服务端开始发送数据包;
- 此刻,客户端等待ACK确认包,而接收到的一直是数据包,客户端会一直将其丢弃;
这种情况就类似进程死锁,整个系统停滞不前;
解释二(次要,忘了咋所可以说这个):
假设A和B要建立连接,而A、B都有两个能力发送能力、接收能力,具体如下:
-
第一次握手A向B发送SYN;
-
B收到A的SYN请求后,B此时知道了A的发送能力是好的;第二次握手B发给ACK、SYN;
-
A接收到B的请求后,A知道了B的发送能力,和接收能力都是OK的;
两次握手结束后,A知道B的接收能力、发送能力都是好的,但B此时只知道A的发送能力OK,并不知道A的接收能力是否健康;
-
第三次握手后,B才知道A的接收能力也OK,此时双方都知道了对方两个能力都是健康的,因此链接建立。
三次握手过程中可否携带数据?
第一次、第二次握手不可以携带数据,其中一个简单的原因就是会让服务器更加容易受到攻击了,还没建立连接也不可靠。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以第三次握手是可以发送数据的。
SYN攻击是什么?
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址是伪造的,真实并不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS (分布式拒绝服务)攻击。
四次挥手
-
第一次挥手:客户端进程发出连接释放报文,并且停止发送数据;
- FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1,意思是对前一个报文传送数据的确认);
- 客户端进入FIN-WAIT-1(终止等待1)状态;
- FIN报文段即使不携带数据,也要消耗一个序号;
-
第二次挥手:服务器收到连接释放报文,发出确认报文(对释放连接报文的确认);
- ACK=1,ack=u+1,并且带上自己的序列号seq=v;
- 此时,服务端就进入了CLOSE-WAIT(关闭等待)状态;
- TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间;
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据);
-
第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文;
- FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w;
- 此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认;
-
第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认(C向S发送);
-
ACK=1,ack=w+1,而自己的序列号是seq=u+1;
-
此时,客户端就进入了TIME-WAIT(时间等待)状态,
此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB(传输控制块)后,才进入CLOSED状态;
-
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
-
为何客户端最后还要等待2MSL?
MSL(Maximum Segment Lifetime)为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃;
第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN报文请求断开了,客户端长时间没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
拥塞控制与流量控制
-
流量控制:如果发送端发送数据太快,接收端来不及接收,可能会丢失数据;所以流量控制是让发送端不要发送太快,要让接收端来得及接收;
- 接收方控制发送方的发送速率,保证接收方可以正确的接收到数据;
-
拥塞控制:是防止过多的数据注入网络,造成网络中的路由器过载;
流量控制:根据接收方的接受能力(接收窗口大小)来限制发送方;
拥塞控制:根据网络中的拥塞情况来限制发送方不要注入过量数据给网络造成压力;
拥塞控制
TCP的四种拥塞控制算法:
慢开始和拥塞避免;快重传和快恢复;
- 慢开始:是指一开始向网络注入少量报文段,并不是指拥塞窗口cwnd增长速度慢;
- 经过一个传输轮次(RTT),拥塞窗口加倍(最大不超过阈值)。
- 拥塞避免:拥塞窗口到达慢开始门限(ssthresh),启动拥塞避免算法;拥塞避免算法后按线性规律增长(每轮窗口值只增加1);
- 继续发送,若出现超时重传,判断网络可能出现拥塞;
- 将ssthresh的值更新为发生拥塞时的cwnd的一半;
- 将cwnd的值减少为1,井重新开始执行慢开始算法。
- 如果遇到超时现象,则将阈值设为当前窗口值的一半,窗口值设为1,重新开始慢启动过程。
- 快重传:发送方尽快进行重传,而不是等超时重传计时器超时再重传;
- 要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认;
- 即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认;
- 发送方一旦收到3个连续的重复确认,就将相应的报文段立即重传,而不是等该报文段的超时重传计时器超时再重传;
- 对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞(进而降低拥塞窗口cwnd
为1);
- 快恢复:发送方一旦收到3个重复确认,就知道现在只是丢失了个别的报文段,于是不启动慢开始算法,而执行快恢复算法;
- 发送方将慢开始门限ssthresh值和拥塞窗口cwnd值调整为当前窗口的一半,开始执行拥塞避免算法;
这里不理解快重传的话可以看下这个相关部分:TCP的拥塞控制(详解)_程序媛_婷的博客-CSDN博客_tcp拥塞控制
流量控制
停止-等待协议:发送窗口大小=1,接收窗口=1;
后退N帧协议(GBN):发送窗口大小>1, 接收窗口=1;
选择重传协议(SR):发送窗口大小>1, 接收窗口>1;
-
停止等待协议:发送方发送完一个帧后就停止发送,等待对方的确认,当收到对方发送的确认帧后才进行下一帧的发送;信道利用率太低;
-
发送方为发送的每个帧都配置了一个计时器,若计时器到时还没收到发送方的确认,则重发该帧;
-
接收方收到一个位错帧:直接丢弃等待超时重发;
-
接收方收到一个失序帧:直接丢弃,并发送期望接收到帧序号的ACK;
-
-
滑动窗口协议:
-
后退N帧协议(GBN);
优点:累计确认;(可以对前面的连续多帧进行确认);
缺点:批量重传;(一旦出现错误帧,就需要对已经发送的后面的帧进行批量重传);-
连续发送发送窗口内的帧,无序发一帧等着一帧等请求;
-
累计确认;发送方收到确认后移动窗口;
-
发送方为发送的每个帧都配置了一个计时器,若计时器超时(未收到确认帧),则重发此时发送窗口内的该帧及其后的所有帧;
-
接收方收到一个失序帧:直接丢弃,并发送期望接收到帧序号的ACK;发送方重发此时发送窗口内的该帧及其后的所有帧;
-
-
选择重传协议(SR);
-
接收方对每一帧都发送ACK;
-
接收方对未正确接收的帧返回NAK,请求发送方重传该帧;不必等到超时重传;
-
可以选择重传的帧,不需要批量重传;
-
-
通过滑动窗口可以很方便的实现流量控制:
滑动窗口协议:允许发送方在停止并等待确认前可以连续发送多个分组,由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输;只有收到确认的时候才可以移动窗口;
HTTP与HTTPS
-
HTTP是HTTP协议运行在TCP之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份;
-
HTTPS是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。
SSL/TLS 是传输层里的协议,与TCP,UDP同层;
-
HTTPS协议需要到ca申请证书,有免费的和付费的;
-
HTTP协议端口是80;HTTPS协议的端口为443;
参考目录
TCP的三次握手和四次挥手及常见面试题_@Demi的博客-CSDN博客_tcp三次握手和四次挥手面试
数据库
事务操作及ACID
事务:是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位;
ACID是指在可靠数据库管理系统(DBMS)中,事务所应该具有的四个特性:
- 原子性(Atomicity):指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生;类似操作系统中的原语;
- 一致性(Consistency):事务开始之前和事务结束以后,数据库的完整性约束没有被破坏;也就是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性;
- 隔离性(Isolation):多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果;
- 持久性(Durability):意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚;即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中;(类似PV);
数据库范式
先明白名词“范式”:大概意思是广泛遵守的模式;
第一范式(1NF):
- 每一个分量都必须是不可分的数据项;
- 表现形式:数据库表中的所有字段值都是不可分割的原子值;
第二范式(2NF):
-
如果关系模式属于第一范式,并且每一个非主属性完全函数依赖于任何一个候选码,则该关系模式属于第二范式;
-
表现形式:在每一列的字段值都是不可分割的原子值的情况下,数据库表中的每一列都与主键完全相关,而不能只与主键的部分相关;
显然若一个模型属于2NF,那么一定属于1NF;
第三范式(3NF):
-
在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式;
所谓传递函数依赖:指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段x → 非关键字段y
-
表现形式:数据库表中的每一列和主键直接相关,而不是间接相关,也就是属性不能传递依赖于主属性;
数据库访问次数很多,如何优化
- SQL查询语句优化:
- 使用索引;索引的目的是为了无序变的有序(参考B+树的叶子结点,可以有序遍历);
- 减少使用SELECT * FROM语句;
- 查询尽可能使用limit来减少返回的行数;
- 使用查询缓存,并将尽量多的内存分配给MYSQL做缓存;
- 主从复制,读写分离,负载均衡:
- 采用两台或多台数据库的主从关系,将一台数据库服务器的数据更新同步到另一台服务器上;
- 可以借此实现数据库的读写分离,从而改善数据库的负载压力;
- 数据库分表,分区,分库:
- 分表-垂直拆分:如果一个表中某些列常用,而另外一些不常用,可以采用垂直拆分;主键和常用列放在一个表中,然后把主键和另外的不常用列放在另一个表中;
- 分表-水平拆分:根据一列或者多列数据的值把数据按行放到两个独立的表中;
- 分区:把一张表的数据分成多个区块,放在不同磁盘上,可以多块磁盘同时处理不同请求,从而提高磁盘I/O读写性能;(类似于低位交叉编址)
SQL语法
https://gitmind.cn/app/doc/b11263704