> 文档中心 > python后端面试锦集

python后端面试锦集


python锦集

计算机网络

一.OSI与TCP/IP各层的结构与功能,都有哪些协议?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-op28uRUd-1648561668253)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646370993209.png)]

1.1 应用层

​ 应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统DNS,支持万维网应用的 HTTP协议,支持电子邮件的 SMTP协议等等。我们把应用层交互的数据单元称为报文

域名系统

域名系统(Domain Name System缩写 DNS,Domain Name被译为域名)是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。(百度百科)例如:一个公司的 Web 网站可看作是它在网上的门户,而域名就相当于其门牌地址,通常域名都使用该公司的名称或简称。例如上面提到的微软公司的域名,类似的还有:IBM 公司的域名是 www.ibm.com、Oracle 公司的域名是 www.oracle.com、Cisco公司的域名是 www.cisco.com 等。

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW(万维网) 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。(百度百科)

1.2 运输层

​ 运输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。应用进程利用该服务传送应用层报文。“通用的”是指并不针对某一个特定的网络应用,而是多种应用可以使用同一个运输层服务。由于一台主机可同时运行多个线程,因此运输层有复用和分用的功能。所谓复用就是指多个应用层进程可同时使用下面运输层的服务,分用和复用相反,是运输层把收到的信息分别交付上面应用层中的相应进程

运输层主要使用以下两种协议:

1. 传输控制协议 TCP(Transmission Control Protocol)--提供面向连接的,可靠的数据传输服务。2. 用户数据协议 UDP(User Datagram Protocol)--提供无连接的,尽最大努力的数据传输服务(不保证数据传输的可靠性)。

1.3 网络层

​ 在 计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多通信子网网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。 在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称 数据报

这里要注意:不要把运输层的“用户数据报 UDP ”和网络层的“ IP 数据报”弄混。另外,无论是哪一层的数据单元,都可笼统地用“分组”来表示。

这里强调指出,网络层中的“网络”二字已经不是我们通常谈到的具体网络,而是指计算机网络体系结构模型中第三层的名称.

互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Intert Protocol)和许多路由选择协议,因此互联网的网络层也叫做网际层或IP层

1.4 数据链路层

数据链路层(data link layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。 在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)

​ 在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。这样,数据链路层在收到一个帧后,就可从中提出数据部分,上交给网络层。 控制信息还使接收端能够检测到所收到的帧中有误差错。如果发现差错,数据链路层就简单地丢弃这个出了差错的帧,以避免继续在网络中传送下去白白浪费网络资源。如果需要改正数据在链路层传输时出现差错(这就是说,数据链路层不仅要检错,而且还要纠错),那么就要采用可靠性传输协议来纠正出现的差错。这种方法会使链路层的协议复杂些。

1.5 物理层

在物理层上所传送的数据单位是比特物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。 使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。

在互联网使用的各种协中最重要和最著名的就是 TCP/IP 两个协议。现在人们经常提到的TCP/IP并不一定单指TCP和IP这两个具体的协议,而往往表示互联网所使用的整个TCP/IP协议族。

二.TCP 三次握手和四次挥手(面试常客)

为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。

2.1 TCP 三次握手漫画图解

如下图所示,下面的两个机器人通过3次握手确定了对方能正确接收和发送消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GU3hicb0-1648561668254)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646375751826.png)]

简单示意图

  • 客户端–发送带有 SYN 标志的数据包–一次握手–服务端
  • 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
  • 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eB67vHvV-1648561668256)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646375936002.png)]

2.2 为什么要三次握手

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的

  • 第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
  • 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
  • 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常

所以三次握手就能确认双发收发功能都正常,缺一不可。

2.3 为什么要传回 SYN

接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。

SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。

2.4 传了 SYN,为啥还要传 ACK

双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。

2.5 断开一个 TCP 连接则需要“四次挥手”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-593JNcJa-1648561668257)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646377090290.png)]

  • 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
  • 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号
  • 服务器-关闭与客户端的连接,发送一个FIN给客户端
  • 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1

2.6 为什么要四次挥手

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。

推荐一篇讲的比较细致的文章:https://blog.csdn.net/qzcsu/article/details/72861891

三.TCP,UDP 协议的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GIISBHJ5-1648561668258)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646377488971.png)]

UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如: QQ 语音、 QQ 视频 、直播等等

TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。 TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务(TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源),这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。TCP 一般用于文件传输、发送和接收邮件、远程登录等场景。

四.TCP 协议如何保证可靠传输

  1. 应用数据被分割成 TCP 认为最适合发送的数据块。
  2. TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
  3. 校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
  4. TCP 的接收端会丢弃重复的数据。
  5. 流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
  6. 拥塞控制: 当网络拥塞时,减少数据的发送。
  7. ARQ协议: 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
  8. 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

4.1 ARQ协议

自动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层传输层错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议和连续ARQ协议

停止等待ARQ协议
  • 停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;
  • 在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认;

优点: 简单

缺点: 信道利用率低,等待时间长

  1. 无差错情况:

发送方发送分组,接收方在规定时间内收到,并且回复确认.发送方再次发送。

  1. 出现差错情况(超时重传):

停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为 自动重传请求 ARQ 。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议 可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。

  1. 确认丢失和确认迟到
  • 确认丢失 :确认消息在传输过程丢失。当A发送M1消息,B收到后,B向A发送了一个M1确认消息,但却在传输过程中丢失。而A并不知道,在超时计时过后,A重传M1消息,B再次收到该消息后采取以下两点措施:1. 丢弃这个重复的M1消息,不向上层交付。 2. 向A发送确认消息。(不会认为已经发送过了,就不再发送。A能重传,就证明B的确认消息丢失)。
  • 确认迟到 :确认消息在传输过程中迟到。A发送M1消息,B收到并发送确认。在超时时间内没有收到确认消息,A重传M1消息,B仍然收到并继续发送确认消息(B收到了2份M1)。此时A收到了B第二次发送的确认消息。接着发送其他数据。过了一会,A收到了B第一次发送的对M1的确认消息(A也收到了2份确认消息)。处理如下:1. A收到重复的确认后,直接丢弃。2. B收到重复的M1后,也直接丢弃重复的M1。
连续ARQ协议

连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。

优点: 信道利用率高,容易实现,即使确认丢失,也不必重传。

缺点: 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如:发送方发送了 5条 消息,中间第三条丢失(3号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。

4.2 滑动窗口和流量控制

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

4.3 拥塞控制

在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。

TCP的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。

  • 慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
  • 拥塞避免: 拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送放的cwnd加1.
  • 快重传与快恢复: 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。  当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。

五.在浏览器中输入url地址 ->> 显示主页的过程(面试常客)

打开一个网页,整个过程会使用哪些协议
在这里插入图片描述

总体来说分为以下几个过程:

  1. DNS解析
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染页面
  6. 连接结束

六.状态码

类别 原因短语
1xx Information(信息性状态码) 接收的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要进行附加操作以完成请求
4xx Client Error(客户端错误状态码) 服务器无法处理请求
5xx Server Error(服务器错误状态码) 服务器处理请求出错

七.各种协议与HTTP协议之间的关系

一般面试官会通过这样的问题来考察你对计算机网络知识体系的理解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-936SwSZ2-1648561668259)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646384778808.png)]

八.HTTP长连接,短连接

在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接

可参考博文https://www.cnblogs.com/gotodsp/p/6366163.html

九.HTTP是不保存状态的协议,如何保存用户状态?

HTTP 是一种不保存状态,即无状态(stateless)协议也就是说 HTTP 协议自身不对请求和响应之间的通信状态进行保存。那么我们保存用户状态呢?Session 机制的存在就是为了解决这个问题,Session 的主要作用就是通过服务端记录用户的状态。典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个 Session,过了时间限制,就会销毁这个Session)。

在服务端保存 Session 的方法很多,最常用的就是内存和数据库(比如是使用内存数据库redis保存)。既然 Session 存放在服务器端,那么我们如何实现 Session 跟踪呢?大部分情况下,我们都是通过在 Cookie 中附加一个 Session ID 来方式来跟踪。

Cookie 被禁用怎么办?

最常用的就是利用 URL 重写把 Session ID 直接附加在URL路径的后面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-73lMT5NE-1648561668260)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646384978264.png)]

十.Cookie的作用是什么?和Session有什么区别?

Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。

Cookie 一般用来保存用户信息 比如①我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;②一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);③登录一次网站后访问网站其他页面不需要重新登录。Session 的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。

Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。

Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。

十一. HTTP 1.0和HTTP 1.1的主要区别是什么?

这部分回答引用这篇文章 https://mp.weixin.qq.com/s/GICbiyJpINrHZ41u_4zT-A? 的一些内容。

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

  1. 长连接 : 在HTTP/1.0中,默认使用的是短连接,也就是说每次请求都要重新建立一次连接。HTTP 是基于TCP/IP协议的,每一次建立或者断开连接都需要三次握手四次挥手的开销,如果每次请求都要这样的话,开销会比较大。因此最好能维持一个长连接,可以用个长连接来发多个请求。HTTP 1.1起,默认使用长连接 ,默认开启Connection: keep-alive。 HTTP/1.1的持续连接有非流水线方式和流水线方式 。流水线方式是客户在收到HTTP的响应报文之前就能接着发送新的请求报文。与之相对应的非流水线方式是客户在收到前一个响应后才能发送下一个请求。
  2. 错误状态响应码 :在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  3. 缓存处理 :在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  4. 带宽优化及网络连接的使用 :HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

十二.URI和URL的区别是什么?

  • URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
  • URL(Uniform Resource Location) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。

URI的作用像身份证号一样,URL的作用更像家庭住址一样。URL是一种具体的URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。

十三.HTTP 和 HTTPS 的区别?

  1. 端口 :HTTP的URL由“http://”起始且默认使用端口80,而HTTPS的URL由“https://”起始且默认使用端口443

  2. 安全性和资源消耗HTTP协议运行在TCP之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS是运行在SSL/TLS之上的HTTP协议,SSL/TLS 运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS高,但是 HTTPS 比HTTP耗费更多服务器资源

    • 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;
    • 非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等

    参考自: https://github.com/Snailclimb/JavaGuide

十四.Cookie和session

14.1什么是Cookie?以及Cookie的简单的应用?

Cookie是一种客户端状态管理技术: cookie由客户端创建,由浏览器保存
应用场景:

  1. 记住用户名和密码
  2. 商品的浏览记录

14.2Cookie的工作原理?如何创建Cookie对象?如何响应给浏览器?

  • 浏览器第一次访问服务器;

  • 在服务器端创建Cookie对象并相应给浏览器(会被浏览器保存在浏览器端)

Cookie c1=new Cookie(key,value);  //创建cookie对象response.addCookie(c1);  //添加cookie到response中,最后会相应给浏览器c1.setMaxAge(参数);  //正数:存货多少秒;0:立即删除;-1:浏览器关闭时删除
  • 浏览器非第一访问服务器;会携带所有保存过的本网站所有的cookie对象到服务器;服务器接收cookie对象;
Cookie[] cs=request.getCookies();     //获取所有的cookie对象    if(cs!=null){     //当cs不为空的时候遍历数组     for(Cookie c: cs){  //遍历cookie数组  String key=c.getName();     //获取Cookie的key  String value=c.getValue();    //获取cookie的值      }    }

14.3简单说一下Cookie的特点有什么?

  1. cookie存储在浏览器客户端,不安全;
  2. cookie中不能够存储中文
  3. cookie只能存储字符串(因为cookie最终在浏览器端;浏览器只识别字符串)
  4. cookie存储数据的大小有限制;cookie的大小不能超过4kb

14.4什么是Session?为什么要用Session?

  • Session是服务器端状态管理技术: session是服务器为浏览器创建的私人存储空间
    因为http协议是无状态协议;服务器无法知道这次请求和上次请求是否来自同一个用户。通过session技术可以记录一次会话中用户的状态;

14.5 Session有什么特点?

  1. session和浏览器一一对应;一个浏览器对应一个session对象;当浏览器关闭后session失效
  2. session保存在服务器端,比较安全
  3. session可以存储对象
  4. session可以充当作用域

14.6 简单说一下Cookie和Session的区别?

相同点: 都是会话跟踪技术(状态管理技术);都跟浏览器有关
不同点:

cookie存处在浏览器端,不安全;session存储在服务器端。比较安全
cookie只能存储字符串,最大4kb;session可以存储对象;可以存储更大的数据
cookie可以分担服务器压力,session不能

14.7 Session的底层原理?

  1. 当浏览器第一次访问服务器;并调用了request.getSession();的时候,服务器会创建Session对象;

  2. //1.获取session的id; String sessionId=session.getId();//2.根据id创建Cookie对象;Cookie c1=new Cookie(“JSESSIONID”,sessionId);//3.将cookie相应给浏览器,并存储在客户端;response.addCookie(c1);

tomcat底层有一个map集合用来存储session,会将sessionId作为key,session对象作为值存储到map中

  1. 当浏览器再次发出请求的时候回携带所有的该网站相关的Cookie对象到服务器;

  2. 当再次调用request.getSession();的时候,内部会执行验证

Cookie[] cookies=request.getCookies();  //获取所有的cookie对象String value=null;if(cookies!=null){for(Cookie c: cookies){String name=c.getName();  //获取cookie的keyif(“JSESSIONID”.equels(name)){ //看name是否为“JSESSIONID”value=c.getValue();   //获得session的唯一标记}}}//根据id值获取session对象,并返回session对象if(value!=null)  Session=map.get(value);  //根据session标记获取session//如果value为空的话应该也会返回创建一个session,并返回

14.8Session的生命周期

  1. 当浏览器第一次访问;并且调用了request.getSession()的时候;session被创建出来
  2. 服务器一般设置session的时间是30分钟;如果没有操作的话session会被销毁并从map中清除
  3. 在一次会话中浏览器发出请求的时候,session的时间会重新计算
  4. 当浏览器关闭的时候服务器的session并没有立刻被销毁,只是浏览器端的cookie(中的sessionid)被销毁;下次访问服务器的时候因为没有id所以会新建session对象;原来的session对象在30分钟时间的到后会被清除销毁

十五.get请求和post请求的区别

1、GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。 POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就是实际的传输数据。 因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。

2、传输数据的大小 在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。 对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。

3、安全性 POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。除此之外,GET请求提交的数据还可能会造成Cross-site request frogery攻击

4.GET产生一个TCP数据包;POST产生两个TCP数据包。 长的说: 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据); 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

十六.TCP与UDP区别总结

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP首部开销20字节;UDP的首部开销小,只有8个字节 6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

数据结构

一.B树和B+树

1.B-Tree

  • 叶子结点具有相同深度
  • 所有索引元素不重复
  • 节点中的数据索引从左到右依次递增

从查询流程我们能看出,B-Tree的查询效率好像也并不比平衡二叉树高,但查询所经过的结点数量要比二叉树少了1个,在数据量大的时候意味着要少很多次的磁盘IO,这对性能的提升是很大的。前面对B-Tree操作的图我们能看出来,元素就是类似1、2、3这样的数值,但是数据库的数据都是一条条的数据,如果某个数据库以B-Tree的数据结构存储数据,那数据怎么存放的呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NnTgVqY0-1648561668261)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646644207264.png)]

具体存放如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FKDmfgTq-1648561668262)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646644257885.png)]

在进行数据库存储中,我们把元素部分拆分成了key-data的形式,key就是数据的主键,data就是具体的数据。

B-Tree存在的问题B-Tree在每一个节点存储了索引和数据,导致进行搜索的时候需要把索引和数据都加载到内存中,这样就不是很划算,内存资源这么宝贵,多存些索引岂不是更好。

2.B+Tree

B+Tree:

  • 在B-Tree基础上的一种优化,使其更适合实现外存储索引结构
  • 非叶子节点不存储数据,只存索引(冗余),这样可以保证存放更多的索引
  • 叶子节点存储所有索引字段
  • 所有查询都要查找叶子节点,查询性能稳定。
  • 叶子节点用指针连接,提高区间访问性能

B+Tree存放数据示例图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lb7AqLCt-1648561668262)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646644628147.png)]

3. MyISAM存储引擎索引实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MyKyPnh6-1648561668264)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646645068316.png)]

select * from tablename where id = 20 MyISAM查询流程:先去MYI文件查询索引数据,定位到 id = 20的叶子节点处,拿到20所对应的数据文件地址"0x6A",然后从MYD文件中查找,根据此文件地址定位到具体id = 20的那一行记录。

4.InnoDB底层存储引擎索引实现

InnoDB的主键索引为聚集索引(索引和数据存放在一起),而一个二级索引索引位置存放的数据为主键ID的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ds1S17oO-1648561668265)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646645225483.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Psqzvm7Y-1648561668265)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646645258653.png)]

  1. select * from tablename where id = 20 InnoDB主键索引查询流程直接从IDB文件中查询id = 20的索引即可直接获取当前行数据。

  2. select * from tablename where name = ‘Alice’ InnoDB非主键索引查询流程直接从IDB文件中查询name = Alice 的索引,得到主键id = 18,再从当前文件中查找id为18的数据。

5.索引有关知识点

  • 什么是索引

    在数据之外,数据库还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用指向数据,这样就可以在这些数据结构上实现高效查找,这些数据结构就是索引。

  • 索引的分类

    **单值索引:**一个索引只包含单个列,一个表中可以有多个单值索引

    **唯一索引:**索引列的值必须唯一,可为空

​ **复合索引:**一个索引包括多个列

  • 索引的优势

​ 1)提高数据检索效率,降低磁盘IO成本

​ 2)通过对数据的排序,降低排序成本

  • 索引的劣势

​ 1)索引虽提高了查询效率,但同时降低了更新、修改、删除的效率,因为MySQL不仅要保存数据,还要维护数据和索引的关系。

​ 2)需要成本去维护索引。一个性能良好的索引需要不断的去尝试,以找到最优解。

  • 什么情况下适合建立索引
     1)主键自动建立唯一索引  2)频繁作为查询条件的字段(where后面的字段) 3)查询中与其他表关联的字段(各种join on后面的字段) 4)单值/复合索引选择?(高并发下倾向选择复合索引) 5)查询中排序的字段 6)查询中统计或分组的字段
  • 什么情况下不适合建立索引

​ 1)表数据太少

​ 2)频繁更新的字段

​ 3)where后面用不到的字段

  • 为什么建议InnoDB必须建主键

    ​ 对于InnoDB来说,如果不手动建主键索引,MySQL底层依然会帮我们创建个聚集索引来维护整张表的所有数据,因为B+Tree必须依靠索引才能建立。为什么建议InnoDB必须建主键呢?因为本身数据库的资源就非常宝贵,我们尽量能手动做的就不要麻烦MySQL去帮我们维护,说白了就是降低数据库开销。

  • 为什么推荐使用整型主键

    ​ 我们就拿UUID举个例子,一大串十分长但无具体意义的字符串,回顾上面InnoDB的索引图,是比较两个int型数据快捷呢还是比较两个字符串快捷呢?想都不用想肯定是比较两个int型更具有优势,字符串需要逐位的去比较,如果碰巧两个字符串只有最后一位不一致那不是亏得要死。

  • 为什么InnoDB非主键索引存储的是主键值

    ​ 保持一致性,当数据库表进行DML操作时,同一行记录的页地址会发生改变,因非主键索引保存的是主键的值,无需进行更改。同时还可以节省存储空间,因为Innodb数据本身就已经汇聚到主键索引所在的B+树上了, 如果普通索引还继续再保存一份数据,就会导致有多少索引就要存多少份数据。

  • 为什么不使用B-Tree而使用B+Tree

    B+Tree将数据的存储都放在了叶子节点,非叶子节点全部用来存放冗余索引,这样可以保证非叶子节点可以存储更多的索引,因为决定B+Tree高度的就是非叶子节点,如果非叶子节点可以存储更多的值就会使树的整体高度变少,从而降低磁盘IO次数,降低系统消耗。

6. B+ 树为什么快?

B+树可以理解是扁且宽的。也就是层数少,每层的节点数目很多但是每层的节点多归多,却不存储数据,只起到索引效果,所有的数据都存在叶子节点上头。B+ 树所有的 Data 域在叶子节点,一般来说都会进行一个优化,就是将所有的叶子节点用指针串起来。这样遍历叶子节点就能获得全部数据,这样就能进行区间访问啦。

下来说Mysql如何衡量查询效率主要是通过磁盘IO次数判断
B+树中,层数少,只在叶子节点存数据的特点就能极大的保证磁盘IO次数少,进而说,效率高~

7.B+树为什么比B树快?

B+树是B-Tree的改进版本,同时也是数据库索引索引所采用的存储结构**。数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都指向相邻的叶子节点的地址**。相比B-Tree来说,进行范围查找时只需要查找两个节点,进行遍历即可而B-Tree需要获取所有节点,相比之下B+Tree效率更高

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gXO8EaUr-1648561668266)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646790990602.png)]

8.为什么官方建议使用自增长主键作为索引?

结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率

9.数据结构种类

1、数组

数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问,数组下标从0开始。例如下面这段代码就是将数组的第一个元素赋值为 1。

int[] data = new int[100];data[0]  = 1; 
优点

1、按照索引查询元素速度快
2、按照索引遍历数组方便

缺点

1、数组的大小固定后就无法扩容了
2、数组只能存储一种类型的数据
3、添加,删除的操作慢,因为要移动其他的元素。

适用场景

频繁查询,对存储空间要求不大,很少增加和删除的情况。

2、栈

栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作**。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈

python后端面试锦集

栈的结构就像一个集装箱,越先放进去的东西越晚才能拿出来,所以,栈常应用于实现递归功能方面的场景,例如斐波那契数列。

3、队列

队列与栈一样,也是一种线性表不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出。从一端放入元素的操作称为入队,取出元素为出队

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPBNrmGj-1648561668268)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646798261933.png)]

使用场景

因为队列先进先出的特点,在多线程阻塞队列管理中非常适用

4、链表

链表是物理存储单元上非连续的、非顺序的存储结构数据元素的逻辑顺序是通过链表的指针地址实现,每个元素包含两个结点**,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域**。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。

python后端面试锦集

链表的优点

链表是很常用的一种数据结构,不需要初始化容量,可以任意加减元素

添加或者删除元素时只需要改变前后两个元素结点的指针域指向地址即可,所以添加,删除很快

链表的缺点

因为含有大量的指针域,占用空间较大;
查找元素需要遍历链表来查找,非常耗时

适用场景

数据量较小,需要频繁增加,删除操作的场景

5、树

是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 每个节点有零个或多个子节点;

  • 没有父节点的节点称为根节点;

  • 每一个非根节点有且只有一个父节点;

  • 除了根节点外,每个子节点可以分为多个不相交的子树;

在日常的应用中,我们讨论和用的更多的是树的其中一种结构,就是二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kv4zx3x2-1648561668269)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646798332548.png)]

二叉树是树的特殊一种,具有如下特点:

1、每个结点最多有两颗子树,结点的度最大为2。
2、左子树和右子树是有顺序的,次序不能颠倒。
3、即使某结点只有一个子树,也要区分左右子树。

二叉树是一种比较有用的折中方案,它添加,删除元素都很快并且在查找方面也有很多的算法优化,所以,二叉树既有链表的好处,也有数组的好处,是两者的优化方案,在处理大批量的动态数据方面非常有用。

扩展:
二叉树有很多扩展的数据结构,包括平衡二叉树、红黑树、B+树等,这些数据结构二叉树的基础上衍生了很多的功能,在实际应用中广泛用到,例如mysql的数据库索引结构用的就是B+树,还有HashMap的底层源码中用到了红黑树。这些二叉树的功能强大,但算法上比较复杂,想学习的话还是需要花时间去深入的。

6.散列表

散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。

记录的存储位置=f(key)

这里的对应关系 f 成为散列函数,又称为哈希 (hash函数),而散列表就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里,这种存储空间可以充分利用数组的查找优势来查找元素,所以查找的速度很快。

哈希表在应用中也是比较常见的,就如Java中有些集合类就是借鉴了哈希原理构造的,例如HashMap,HashTable等,利用hash表的优势,对于集合的查找元素时非常方便的,然而,因为哈希表是基于数组衍生的数据结构,在添加删除元素方面是比较慢的,所以很多时候需要用到一种数组链表来做,也就是拉链法。拉链法是数组结合链表的一种结构,较早前的hashMap底层的存储就是采用这种结构,直到jdk1.8之后才换成了数组加红黑树的结构,其示例图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b17agOD3-1648561668269)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646798429940.png)]

从图中可以看出,左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

哈希表的应用场景很多,当然也有很多问题要考虑,比如哈希冲突的问题,如果处理的不好会浪费大量的时间,导致应用崩溃。

7、堆

堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质:

堆中某个节点的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki = k2i,ki >= k2i+1), (i = 1,2,3,4…n/2),满足前者的表达式的成为小顶堆,满足后者表达式的为大顶堆,这两者的结构图可以用完全二叉树排列出来,示例图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rQ2Fntz0-1648561668270)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/1646798603595.png)]

因为堆有序的特点,一般用来做数组中的排序,称为堆排序。

8、图

图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。

按照顶点指向的方向可分为无向图和有向图:
python后端面试锦集

python后端面试锦集

图是一种比较复杂的数据结构,在存储数据上有着比较复杂和高效的算法,分别有邻接矩阵 、邻接表、十字链表、邻接多重表、边集数组等存储结构,这里不做展开,读者有兴趣可以自己学习深入。

FastAPI知识点

一. 什么是FastAPI

1.FastAPI是一款现代化、高性能的Web框架用于构建基于Python3.6及以上的API

其具有以下特征:

  • 速度快:非常高的性能,与NodeJS和Go不相上下,是最快的Python框架之一;

  • 编码快:将开发特性所需的速度提高大约200%到300%;

  • 错误少:减少大约40%的人为(开发)错误;

  • 直观:强大的编辑器支持,支持多场景开发,调试所花的时间更少;

  • 简单:被设计为易于使用和学习,减少阅读文档的时间;

  • 代码少:最小化重复,更少的错误;

  • 健壮:代码可随时部署到生产环境,并自动提供交互文档;

  • 标准:基于(并完全兼容)api的开放标准:OpenAPI(以前称为Swagger)和JSON模式。

2.FastAPI的速度得益于使用了Starlette——一个轻量级的ASGI框架。

ASGI,全称为Asynchronous Server Gateway Interface,为了规范支持异步的Python Web服务器、框架和应用之间的通信而定制,同时囊括了同步和异步应用的通信规范,并且向后兼容WSGI。由于最新的HTTP协议支持异步长连接,而传统的WSGI应用支持单次同步调用,即仅在接受一个请求后返回响应,从而无法支持HTTP长轮询或WebSocket连接。在Python3.5增加async/await特性之后,基于asyncio和协程的异步应用编程变得更加方便。ASGI协议规范就是用于asyncio框架的最低限度的底层服务器/应用程序接口。

3.异步非阻塞I/O & 协程

阻塞I/O,非阻塞I/O,I/O多路复用都属于同步I/O。而异步I/O则不一样,当进程发起I/O操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说I/O完成。在这整个过程中,进程完全没有被阻塞。在非阻塞I/O中,虽然进程大部分时间都不会被阻塞,但是它仍然要求进程去主动的查询,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom()来将数据拷贝到用户内存。

相对于线程,协程是程序级的I/O调度,是对一个线程进行分片,使得线程在代码块之间来回切换执行,而非逐行执行,因此能够支持更快的上下文切换协程本身并不能实现高并发,但与I/O切换结合后能够大大提高性能。每当发生I/O,自动切换协程,让出CPU资源,即可减少高并发场景下服务的响应时间。因此,结合async/await语法,将代码块定义为协程,使用异步服务器即可实现程序级I/O切换和协程调度。

4.asyncio事件循环

因为Python是单线程的,同一时间只能执行一个方法,所以当一系列的方法被依次调用的时候Python会先解析这些方法,把其中的同步任务按照执行顺序排队到一个地方,这个地方叫做执行栈。主线程之外,还存在一个"任务队列"(task queue)。当遇到异步任务时,异步任务会被挂起,继续执行执行栈中任务,等异步任务返回结果后,再按照执行顺序排列到"事件队列中"。一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务排列到事件队列中。主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个过程是循环往复的,这个过程被称为Event Loop,即事件循环。

由于异步非阻塞框架基本为单线程运行,因此要利用协程实现事件循环。FastAPI推荐使用uvicorn来运行服务,uvicorn是基于uvloop和httptools构建的闪电般快速的ASGI服务器Python3.5+的标准库asyncio提供了事件循环用来实现协程,并引入了async/await关键字语法以定义协程同是异步非阻塞框架的Tornado通过yield生成器实现协程,它自身实现了一个事件循环,其在Python3之后也支持async/await关键字语法,以使用标准库asyncio。而FastAPI则是利用了uvloop,相对于asyncio,更进一步地提升了速度。uvloop是用Cython编写的,并建立在libuv之上。libuv是一种高性能的、跨平台异步的I/O类库,nodejs也使用到了它。由于nodejs是如此的广泛和流行,可以知道libuv是快速且稳定的。uvloop实现了所有的asyncio事件循环APIs。高级别的Python对象包装了低级别的libuv结构体和函数方法。 继承可以使得代码保持DRY(不要重复自己),并确保任何手动的内存管理都可以与libuv的原生类型的生命周期保持同步。

进程、线程、协程

一. 进程、线程、协程介绍

1、进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全

2、线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据

3、协程

协程是一种用户态的轻量级线程(微线程),协程的调度完全由用户控制协程拥有自己的寄存器上下文和栈协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

二、区别:

1、进程多与线程比较

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:

  • 地址空间: 线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间而进程有自己独立的地址空间
  • 资源拥有: 进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
  • 线程是处理器调度的基本单位,但进程不是
  • 二者均可并发执行
  • 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
2、协程多与线程进行比较
  • 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU
  • 线程进程都是同步机制而协程则是异步
  • 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

三、进程和线程、协程在python中的使用

1、多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用。**多进程的优势就是一个子进程崩溃并不会影响其他子进程和主进程的运行,但缺点就是不能一次性启动太多进程,会严重影响系统的资源调度,特别是CPU使用率和负载。**使用多进程可以查看文章《python 多进程使用总结》。注:python2的进程池在类中的使用会有问题,需要把类函数定义成全局函数。具体可参考 http://bbs.chinaunix.net/thread-4111379-1-1.html

2**、多线程一般是使用threading库,完成一些IO密集型并发操作。多线程的优势是切换快,资源消耗低,但一个线程挂掉则会影响到所有线程,所以不够稳定**。现实中使用线程池的场景会比较多,具体可参考《python线程池实现》。

3、协程一般是使用gevent库,当然这个库用起来比较麻烦,所以使用的并不是很多。相反,协程在tornado的运用就多得多了,使用协程让tornado做到单线程异步,据说还能解决C10K的问题。所以协程使用的地方最多的是在web应用上。

总结一下就是IO密集型一般使用多线程或者多进程CPU密集型一般使用多进程强调非阻塞异步并发的一般都是使用协程,当然有时候也是需要多进程线程池结合的,或者是其他组合方式。

数据库

一.msql篇

1.mysql事务隔离级别

  • Serializable:串行化,一个事务一个事务的执行。
  • Repeatable read:可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响。
  • Read committed:读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值。
  • Read uncommitted:读取未提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。

2.简述在MySQL数据库中引擎MyISAM和InnoDB的区别?

  • 存储结构方面:MyISAM每个MyISAM在磁盘上存储成三个文件.frm文件存储表定义、数据文件的扩展名为.MYD、索引文件的扩展名是.MYI;InnoDB所有的表都保存在同一个数据文件中,表的大小只受限于操作系统文件的大小,一般为2GB。
  • 存储空间方面:MyISAM可被压缩,存储空间较小。支持三种不同的存储格式:静态表、动态表、压缩表;InnoDB需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。
  • 事务支持方面:MyISAM强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持;InnoDB提供事务支持事务。
  • 表锁差异方面:MyISAM只支持表级锁;InnoDB支持事务和行级锁。
  • **表主键:**MyISAM允许没有任何索引和主键的表存在,索引都是保存行的地址。InnoDB如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键。
  • 增删改查操作方面:如果执行大量的SELECT,MyISAM是更好的选择。如果执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表。
  • 外键方面:MyISAM不支持;InnoDB支持。

3.什么是锁

数据库是一个多用户使用的共享资源,当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。基本锁类型:锁包括行级锁和表级锁

4.解释 MySQL 外连接、内连接与自连接的区别?

  • 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一个表的所有记录和另一个表中的所有记录一一匹配。
  • 内连接则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记录不会出现在结果集中,即内连接只连接匹配的行。
  • 外连接的结果集中不仅包含符合连接条件的行,而且还会包括左表、右表或两个表中的所有数据行,这三种情况依次称之为左外连接,右外连接和全外连接。
  • 左外连接也称左连接,左表为主表,左表中的所有记录都会出现在结果集中。对于那些在右表中并没有匹配的记录仍然要显示,右边对应的那些字段值以NULL 来填充。
  • 右外连接,也称右连接,右表为主表,右表中的所有记录都会出现在结果集中。
    左连接和右连接可以互换,MySQL 目前还不支持全外连接。

5. 什么情况下不宜建立索引?

对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等

6.锁的优化策略?

  • 读写分离
  • 分段加锁
  • 减少锁持有的时间
  • 多个线程尽量以相同的顺序去获取资源不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。

7.如何通俗地理解三个范式,说说范式化设计优缺点?

第一范式(1NF):是对属性的原子性约束,要求属性具有原子性,不可再分解;
第二范式(2NF):是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
第三范式(3NF):是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。

范式化的优点: 可以尽量得减少数据冗余,使得更新快,体积小。
缺点:对于查询需要多个表进行关联,减少写操作的效率和增加读操作的效率,更难进行索引优化

8.说说对 SQL 语句优化有哪些方法?

  • Where子句中:where表之间的连接必须写在其他 Where 条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾和HAVING 最后。
  • 用EXISTS替代IN,用NOT EXISTS替 NOT IN。
  • 避免在索引列上使用计算、IS NULL和IS NOT NULL
  • 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
  • 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描

9.主键和候选键有什么区别?

表格的每一行都由主键唯一标识,一个表只有一个主键,主键也是候选键按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。

10.MySQL中有哪几种锁?

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

11.事务的ACID特性

  • 原子性:一个事务必须被视为一个不可分割的最小单位,要不全部提交成功,要么全部失败回滚。
  • 一致性:数据库总是从一个一致性的状态转换到另外一个一致性状态,不会部分数据状态改变了部分状态没有改变。
  • 隔离性:通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。这个和数据库的隔离级别有关,所以只能通常来说。
  • 持久性:一旦事务提交,则其所做的修改会被永久保存到数据库中。

11.mysql脑图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZW8sMmHV-1648561668271)(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9F%A5%E8%AF%86%E7%82%B9.assets/naotu.png)]

二. mongodb篇

1.索引限制

  • 索引-额外开销
    每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。
  • 索引-内存(RAM)使用
    由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。
  • 索引-查询限制
    索引不能被以下的查询使用:正则表达式及非操作符,如 $nin, $not, 等。算术运算符,如 $mod, 等。$where 子句所以,检测你的语句是否使用索引是一个好的习惯,可以用explain来查看。
  • 索引键限制
    从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。
  • 插入文档超过索引键限制
    如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具类似。
  • 索引-最大范围限制
    1、索引关键字:该字段的取值不能超过1024字节,否则不能添加到一个索引中。2、集合的最大所有个数:643、索引名称长度:包括数据库于集合名称总共不超过125字符。4、联合索引最大字段个数:315、不能联合使用text索引与其他类型索引

三. redis篇

1. redis 存储数据类型

string(字符串)

hash(哈希)

list(列表)

set(集合)

zset(sorted set:有序集合)。

2.Redis是单进程单线程的?

Redis是单进程单线程的,Redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

3.Redis为什么是单线程的?

多线程处理会涉及到锁,而且多线程处理会涉及到线程切换而消耗CPU。因为CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存或者网络带宽。单线程无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来解决。

4.Redis提供了哪几种持久化方式?

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储

  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.

如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.

你也可以同时开启两种持久化方式, 在这种情况下, 当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始。

5.Redis常见性能问题和解决方案?

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽量避免在压力很大的主库上增加从库

6.Redis哈希槽的概念?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

7.Redis如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.

8.Redis回收进程如何工作的?

一个客户端运行了新的命令,添加了新的数据。

Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。

9.Redis回收使用的是什么算法?

LRU算法

10.RDB

默认开启,会按照配置的指定时间将内存中的数据快照到磁盘中,创建一个dump.rdb文件,Redis启动时再恢复到内存中。

Redis会单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。

需要注意的是,每次快照持久化都会将主进程的数据库数据复制一遍,导致内存开销加倍,若此时内存不足,则会阻塞服务器运行,直到复制结束释放内存;都会将内存数据完整写入磁盘一次,所以如果数据量大的话,而且写操作频繁,必然会引起大量的磁盘I/O操作,严重影响性能,并且最后一次持久化后的数据可能会丢失;

11.AOF

以日志的形式记录每个写操作(读操作不记录),只需追加文件但不可以改写文件,Redis启动时会根据日志从头到尾全部执行一遍以完成数据的恢复工作。包括flushDB也会执行。

主要有两种方式触发:有写操作就写、每秒定时写(也会丢数据)。

因为AOF采用追加的方式,所以文件会越来越大,针对这个问题,新增了重写机制,就是当日志文件大到一定程度的时候,会fork出一条新进程来遍历进程内存中的数据,每条记录对应一条set语句,写到临时文件中,然后再替换到旧的日志文件(类似rdb的操作方式)。默认触发是当aof文件大小是上次重写后大小的一倍且文件大于64M时触发。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。一般情况下,只要使用默认开启的RDB即可,因为相对于AOF,RDB便于进行数据库备份,并且恢复数据集的速度也要快很多。

开启持久化缓存机制,对性能会有一定的影响,特别是当设置的内存满了的时候,更是下降到几百reqs/s。所以如果只是用来做缓存的话,可以关掉持久化。

四.综合篇

1.mongdb 和 redis 的区别

1.内存管理 Redis 数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。 MongoDB 数据会优先存于内存,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。 需要注意的是Redis 和mongoDB特别消耗内存,一般不建议将它们和别的服务部署在同一台服务器上。

2.数据结构 Redis 支持的数据结构丰富,包括hash、set、list等。 MongoDB 数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库支持的查询语言非常丰富

3.数据量和性能 当物理内存够用的时候,性能,redis>mongodb>mysql 数据量,mysql>mongodb>redis 注意mongodb可以存储文件,适合存放大量的小文件,内置了GirdFS 的分布式文件系统。

4.可靠性 mongodb从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性; Redis依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能。 可靠性上MongoDB优于Redis。

2.mysql 和 redis 的区别

1)类型上 从类型上来说,mysql是关系型数据库,redis是缓存数据库

2)作用上 mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢 redis用于存储使用较为频繁的数据到缓存中,读取速度快

3)需求上 mysql和redis因为需求的不同,一般都是配合使用。

python基础篇

python是动态语言还是静态语言?强类型语言还是弱类型语言

的一倍且文件大于64M时触发。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。一般情况下,只要使用默认开启的RDB即可,因为相对于AOF,RDB便于进行数据库备份,并且恢复数据集的速度也要快很多。

开启持久化缓存机制,对性能会有一定的影响,特别是当设置的内存满了的时候,更是下降到几百reqs/s。所以如果只是用来做缓存的话,可以关掉持久化。

四.综合篇

1.mongdb 和 redis 的区别

1.内存管理 Redis 数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。 MongoDB 数据会优先存于内存,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。 需要注意的是Redis 和mongoDB特别消耗内存,一般不建议将它们和别的服务部署在同一台服务器上。

2.数据结构 Redis 支持的数据结构丰富,包括hash、set、list等。 MongoDB 数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库支持的查询语言非常丰富

3.数据量和性能 当物理内存够用的时候,性能,redis>mongodb>mysql 数据量,mysql>mongodb>redis 注意mongodb可以存储文件,适合存放大量的小文件,内置了GirdFS 的分布式文件系统。

4.可靠性 mongodb从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性; Redis依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能。 可靠性上MongoDB优于Redis。

2.mysql 和 redis 的区别

1)类型上 从类型上来说,mysql是关系型数据库,redis是缓存数据库

2)作用上 mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢 redis用于存储使用较为频繁的数据到缓存中,读取速度快

3)需求上 mysql和redis因为需求的不同,一般都是配合使用。

python基础篇

python是动态语言还是静态语言?强类型语言还是弱类型语言

待补充…敬请期待