后端面经整理:数据库、操作系统、计算机网络等

文章目录

数据库

索引的底层实现:

B树:
B Tree 是平衡查找树,所有叶子节点到根节点的距离相等,节点间使用指针相连。每个节点内的数据按顺序存储,同时为其子节点的划分阈值。

B+树:
B+ Tree是B Tree的变种,其所有数据存储于叶子节点,中间节点只存储指针。
相同大小的磁盘页使用B+树能存储更多的节点元素,降低树的高度(更加矮胖),使得IO次数更少。
B+树每次查找都查找到叶子节点,查询性能更稳定。
B+树所有叶子节点形成有序链表,便于范围查询。B树查找范围时先通过二分查找找到下限,再不断中序遍历找到上限。而B+树先二分查找找到下限,再顺序遍历叶子节点链表即可找到上限。
哈希索引:
哈希索引是基于hash表实现的,hash索引本身只需要存储每个键值的hash码,因此其结构十分紧凑,查询效率也很高(能以O(1)的复杂度进行查找)。结构【键值,hash码,指针】。
哈希索引缺陷:

  • hash索引只能等值查找,不能进行范围查找和部分查找。
  • 无法用于排序与分组。
  • 当大量值的hash码存在冲突时,其效率不一定比B+树高。
  • 二次扫描:第一次找到满足hash值得数据,第二次对比键值取出数据。

聚簇索引、非聚簇索引(辅助索引):

聚簇索引:

• 索引和数据存放在一起,索引结构的叶子节点保存了行数据。
• 索引顺序与物理顺序相同,更适合between and和order by操作;
• 每张表只能有一个聚集索引;
• [InnoDB使用聚簇索引]

非聚簇索引:

• 索引和数据分开,索引结构的叶子节点指向数据。
• 索引顺序与物理顺序无关;
• 每新建一个索引就会建立一个非聚集索引,因此大量建立索引需要更多的资源和开销,并影响insert和update性能;
• [MyISAM使用非聚簇索引]

关系模型的三类完整性约束:

• 实体完整性(主键):每个元组都是唯一可标识的;
• 参照完整性(外键):将有关联的表使用外键联系起来,保证修改一个表中的数据• 时,对应的另一个表中数据及时更新。
• 用户定义完整性:又叫域完整性或语义完整性,指明关系中属性的取值范围,防止属性的值与应用定义矛盾。

一致性哈希:

一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

1.首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。
2.然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
3.然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。

一致性哈希算法原理

Redis:

Redis: 基于内存的高性能key-value数据库。
优点:

• 速度快:基于内存;
• 支持事务:处理都是原子性的;
• 支持丰富的数据类型:string,list,set等;
• 丰富的特性:可用于缓存、消息、按key设置过期时间。

为什么快:

• 基于内存:数据存在内存中,绝大部分的请求都是内存操作,速度很快;
• 单进程单线程:避免了进程线程的竞争和进程切换带来的开销,也不必考虑锁的问题;
• IO多路复用:使得单个线程可以处理多个接口的请求。

ACID 四大特性:

• 原子性(atomicity):事务被视为不可分割的最小单元,事务中的操作要么全部成功提交,要么失败回滚。反向执行回滚日志中的操作可实现回滚;
• 一致性(consistency):数据库在事务执行前后都保持一致性状态,在一致性状态下,所有事务对同一数据的访问结果是相同的;
• 隔离性(isolation):事务提交前,其所做的修改对其他事务不可见;
• 持久性(durable):事务提交后,其所做的修改会永久保存到数据库中,即使数据库发生崩溃也不会失效。

并发一致性问题:

• 丢失修改:两个事务同时修改一个数据,后修改的结果覆盖了前一个修改的结果。
• 读脏数据(脏读):事务1修改了数据后事务2读取数据,但紧接着事务1撤回了修改,事务2读取的数据就是脏数据。
• 不可重复读:事务1多次读取同一个数据,在事务1执行过程中,事务2修改数据并提交,之后事务1读取的数据和之前的结果不同。
• 幻读:事务1读取一个范围数据,事务2在其中插入数据后,事务1再次读取范围数据和之前不同。

并发一致性问题主要是破坏了事务的隔离性,解决方法是保证事务的隔离性。可以通过封锁来实现隔离性,但是封锁需要用户自己控制,相当复杂。数据库管理系统提供了不同的隔离级别,可以更方便地管理。

隔离级别:

• 未提交读:事务未提交之前,其所做的修改对其他事务也可见;
• 提交读:事务未提交之前,其所做的修改对其他事务不可见;
• 可重复读:保证同一个事务中对同一个数据的读取结果是一致的;【MySQL的默认隔离级别】
• 可串行化:强制事务串行执行,事务间就可以相互不影响。需要加锁保证同一时间只有一个事务执行。

视图:

视图是虚拟的表,不包含数据,只包含动态检索数据的查询(即SQL查询语句)。
使用场景:
• 重用SQL语句;
• 简化SQL操作,不必知道包含的基本查询的细节;
• 使用表的组成部分而不是整个表;
• 保护数据,可以给用户授权表的部分访问权限而非全部;
• 更改数据和表示:返回与底层表示和格式不同的数据;
规则和限制:
• 命名唯一;
• 数量没有限制,可以嵌套(从视图创建新视图);
• 不能索引,也不能有关联的触发器和默认值;
• 可以和表一起使用。

InnoDB与MyISAM比较:

事务:InnoDB是事务性的,可以有Commit和RollBack操作;
并发:MyISAM只支持表级锁,InnoDB还支持行级锁;
外键:InnoDB支持外键;
备份:InnoDB支持在线热备份(系统运行时备份);
崩溃恢复:MyISAM更容易崩溃,且修复很慢;
其他特性:MyISAM支持表压缩和空间数据索引。
MyISAM适合只读数据或表较小、可以容忍修复操作的场景。InnoDB适合大数据量的情况,故障可RollBack。
索引:MyISAM非聚簇索引,InnoDB聚簇索引

InnoDB 4大特性:

1. 插入缓冲:
    a. 要求:非聚簇索引,索引不唯一;
    b. 插入前先检查是否在缓冲区,若是则直接插入,否则先放入insert buffer对象中。再以一定的频率进行insert buffer和非聚簇索引的子节点进行合并,可以将多个处于同一索引页的插入合并到一个操作中,大大提高了非聚簇索引的插入性能,减少了随机IO带来的性能损耗。
2. 二次写:二次写缓存是位于系统表空间的存储,用来缓存从缓冲池到数据文件中的数据页,当数据库宕机时可以从中找到备份进行恢复;
3. 自适应哈希:经常访问的索引会被自动生成到哈希索引中去,通过缓冲池的B+树构造而来,建立速度很快。
4. 预读:(extent,page两种单位)
    a. 线性预读:将下一个extent读入buffer;
    b. 随机预读:将同一extent中的剩余page读入buffer。

范式(解决增删改异常)

第一范式:属性不可分;
第二范式:所有非主属性依赖于主键,而不是主键的一部分;
第三范式:所有非主属性直接依赖于主键,而不是传递(间接)依赖于主键。

三大范式依次以前一个为基础。

数据库优化:

结构优化:

• 分解字段很多的表:有些字段使用频率低;
• 增加中间表:经常需要联合查询的表建立中间表,将联合查询改为对中间表的查询;
• 为频繁使用和查询的字段建立索引;
• 尽可能使用not null,给空字段定义默认值;

SQL语句优化:

• 避免使用select *,将需要查询的字段列出来;
• 使用连接(join)代替子查询;
• 使用limit对查询结果进行限定;
• 避免在where子句中进行null判断,使用or和(!=, <>),否则数据库会放弃索引进行全表扫描。

Union 和 Union all 的区别:

• 重复数据:Union会去掉重复值,Union all不会;
• 顺序:Union按照字段排序结果,Union all不会;
• 效率:Union all比Union快很多。

MySQL索引:

  • 唯一索引:索引列的所有值必须唯一,可以为空;
  • 主键索引:是一种唯一索引,一张表只能有一个主键索引,且不能为空;
  • 普通索引:基本索引类型,没有唯一性限制,可以为空;
  • 全文索引:针对文件、文本的检索,MyISAM和高版本InnoDB支持;fulltext索引配合match against操作检车或过滤文本中的关键字,而不是直接与索引中的值相比较。
  • 组合索引:一个索引中包含多个列。只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。

存储过程:

  • 存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
  • 存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字- 并给定参数(需要时)来调用执行。
  • 存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

触发器是一种特殊类型的存储过程,主要是通过事件进行触发而被执行的,而存储过程可以通过存储过程名字而被直接调用。当对某一表进行诸如Update、 Insert、 Delete 这些操作时,SQL Server就会自动执行触发器所定义的SQL 语句,从而确保对数据的处理必须符合由这些SQL 语句所定义的规则。

联结JOIN:

  • CROSS JOIN:返回笛卡尔积;
  • INNER JOIN在CROSS JOIN的基础上筛选不符合条件的数据;
    自然联结在普通的INNER JOIN的基础上删除了重复列。

操作系统面试题

进程与线程的区别:

  • 资源:进程是资源分配的基本单位,但线程不拥有资源,线程能访问其所属进程的资源;
  • 调度:线程是独立调度的基本单位,同一进程中线程的切换不会引起进程的切换,而不同进程间线程的切换会引起进程的切换;
  • 系统开销:进程的新建和撤销时,系统需要为其分配和回收资源,如内存空间和I/O设备等,开销远大于线程的新建和撤销。进程的切换需要当前进程CPU环境的保护和新进程环境的设置,而线程的切换只需要保存和设置少量的寄存器内容,开销很小。因此,线程的系统开销远低于进程。
  • 通信:线程可以直接读写进程数据进行通信,但进程需要IPC(进程间通信技术)进行通信(管道、消息队列、共享内存)。

多进程与多线程:

多进程:

• 进程是一个程序在给定数据集上的一次运行;
• 多进程并发执行,共享物理内存、磁盘、IO、打印机等资源;

多线程:

• 线程是一个基本的CPU执行单位,必须依靠进程存活。线程是一个执行上下文(execution context),是CPU运行的一串指令;
• 多线程并发执行,共享地址空间、打开的文件等父进程的全部资源;
• 主线程完成从进程创建到结束的全部操作,期间其他的线程被创建或退出

选择多进程还是多线程:
• CPU密集型:偏重于计算,频繁使用CPU,适合多进程。如机器学习。
• I/O密集型:经常输入输出,适合多线程。如爬虫。

协程:

  • 协程是微线程,在一个线程中执行,执行过程中可以随时中断,由用户控制(进程和线程本质上是系统运行)。执行效率高,减少了线程切换和锁开销。
  • 协程失去了标准线程使用多核的能力。多进程+协程,既可以利用多核,又能利用协程的高效率。
  • 实现:asyncio(异步IO,即不用等待其结束就能进行其他操作),在yield处暂停等待指令。

协程相比线程的优势:协程执行效率高。

  • 协程由用户控制,不需要线程切换开销。与多线程相比,线程数量越多,协程的优势越明显;
  • 协程不需要锁机制。线程中需要用锁保护数据,而协程不需要写变量保护,只需要判断状态就好了。

进程、线程、协程堆栈区别:

  • 进程:有独立的堆栈,不共享堆也不共享栈;由操作系统调度;
  • 线程:有独立的栈,共享堆而不共享栈;由操作系统调度;
  • 协程:有独立的栈,共享堆而不共享栈;由程序员自己调度。

进程调度:

  • 批处理系统(周转时间):先来先服务(短作业等待时间过长),短作业优先(饿死),最短剩余时间优先(短作业优先的抢占式版本)。
  • 交互式系统(快速响应):时间片轮转,优先级调度,多级反馈队列

进程同步:

  • 对临界资源进行访问的代码为临界区,临界区需要互斥访问。
  • 同步与互斥:同步是进程因合作产生制约关系,需要按一定的顺序执行。互斥是不同进程不能同时访问临界资源。
  • 信号量:PV操作,P是当信号量大于0时-1,若为零进入休眠;V操作是唤醒休眠的进程,即+1。
  • 互斥量:当信号量只能取01时即为互斥量,0表示临界区加锁,1表示临界区解锁。

死锁,举个例子,如何预防:

  • 死锁的四个必要条件:互斥、非抢占、拥有并等待、循环等待。
例子:哲学家问题。几个哲学家坐在圆桌上,每两个哲学家之间有一只筷子,一个哲学家只有同时拥有两只筷子才能开始进食。
若每个哲学家拿起左边的筷子,那么所有人就陷入了等待右边筷子的死锁。
【解决:按顺序给每支筷子设定优先级,每个哲学家首先拿起低优先级的筷子,这样,除了最后一位哲学家,所有人都会先拿起左边的筷子,因此打破了循环】
  • 死锁预防:破坏四个必要条件。
  • 死锁避免:
    • 安全状态:没有死锁发生时,即使所有进程突然请求最大资源需求数,也存在调度顺序可以使每个进程运行完毕。
    • 银行家算法:判断满足贷款需求是否会进入不安全状态,不安全就拒绝。

虚拟地址和物理地址:

  • 虚拟内存是为了将物理内存扩充为更大的逻辑内存。为了更方便地管理内存,操作系统将内存抽象成地址空间,每个程序有自己对应地地址空间。地址空间被分为很多块,一块被称为一页。虚拟地址空间的页映射到物理地址空间,不用连续映射也不用全部映射。当引用的页没有映射到物理内存时,引发缺页中断,将其调入再重新执行命令即可。这使得一个程序可以在不完全装入内存地情况下执行。
  • 内存管理单元(MMU)维护页表,保存程序虚拟地址空间与物理地址空间的映射关系。
  • 虚拟地址 = 页面号 + 偏移量。

页面置换算法:(缺页率最低)

  • 最优:被置换出的页面最长时间不会再使用;
  • 最近最久未使用;
  • 最近未使用:每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。
  • 先进先出;

分段

分页的表在一维地址空间动态增长可能发生覆盖,分段会将每个表分开成不固定大小并且可以动态增长的独立地址空间。段页式先将表分段,再将段分页,既有分段系统的共享和保护,又有分页系统的虚拟内存。

计算机网络

DNS: 域名解析系统。

是一个将域名和IP地址相互映射的分布式数据库。

解析过程(分级解析):
根域名 — 顶级域名 — 二级域名

步骤
(递归查询本地服务器,迭代查询其他远程服务器):

  • 看看DNS缓存里有没有,有的话直接返回;
  • 使用UDP向DNS服务器发送查询消息;
  • 接收返回的响应消息;

传输协议:
除超过512字节和主从DNS服务器的区域传送使用TCP外,都是用UDP协议。

  • 为什么使用UDP:因为快,只需要一个请求一个应答就够了,而TCP需要三次握手,请求与应答、四次挥手。如果多几次查询,每次都要握手挥手的时间开销太大了。并且DNS查询的数据都很小。
  • 为什么区域传送使用TCP:因为可!从主DNS服务器上复制内容需要可靠,并且同步的数据可能超过512字节。

浏览器输入URL(域名)到返回页面的全过程:

1. DNS域名解析,得到IP地址;
2. 拿到解析的IP地址进行TCP连接;
3. 向服务器发送HTTP请求;
4. 服务器处理请求;
5. 服务器返回响应结果;
6. 关闭TCP连接;
7. 解析HTML;
8. 渲染页面。

TCP连接的三次握手,四次挥手

3次握手:

  • 双方都确认自己和对方的收发能力是正常的,需要的最少握手次数。用不着4次,3次就够了。
  • 此外,若使用2次握手,当客户端的失效连接请求到达服务器,服务器会误打开连接,浪费资源。
  • 若第三次握手失败,服务器会关闭连接,防止SYN洪泛攻击。

为什么建立连接是三次握手,关闭连接确是四次挥手:

  • 建立连接时,服务器处于listen状态,当收到客户端的SYN请求时,会将响应的SYN、ACK放在同一个响应报文里发送给客户端。
  • 关闭连接时,服务器收到客户端的FIN请求,表示客户端不再发送数据,但此时还可以接收数据。因此,服务器可以先响应ACK给客户端,表示收到了请求,然后将服务器端还没发送完的数据全部发送给客户端之后再发送FIN表示不再发送数据。服务器将ACK和FIN分开发送,导致多了一次数据传输。
    为什么客户端最后在TIME_WAIT还要等待2MSL(最大报文存活时间):
  • 保证服务器收到客户端的最后一个ACK,若这个ACK丢失,服务器会重发一次FIN+ACK,这时客户端还没有关闭连接,就能收到重发的请求并给出响应。同时重启2MSL计时器。
  • 客户端发送完最后一个ACK后,本连接持续时间内的所有报文段都会从网络中消失,新的连接中就不会收到已经关闭的旧连接中的报文。

TCP流量控制:

流量控制是为了调整发送方的发送速率,使得接收方来得及接收。
接收方的确认报文中有一个窗口字段,用来控制发送方的窗口大小,从而控制发送速率。

TCP拥塞控制:

拥塞控制是为了降低整个网络的拥塞程度(和流量控制区分)。
当网络出现拥塞时,分组丢失引发重传,继而加重拥塞程度,因此需要控制。发送方维护一个叫做拥塞窗口的状态变量cwnd,实际决定发送数据量的还是发送窗口。

  • 慢启动 & 拥塞避免:
    • 发送最初执行慢启动,cwnd = 1,发送方只能发送一个报文段。发送方每次收到ACK后将cwnd加倍。
    • 为避免成倍增加的cwnd使得网络拥塞的可能增加,设置慢启动阈值ssthreash。当cwnd >= ssthresh的时候,进入拥塞避免,每次只能将cwnd的值加一。
    • 若出现超时,则将 ssthresh减半,cwnd=1,重新开始慢启动。
  • 快速重传 & 快速恢复:
    • 在接受方,每次只确认收到的最后一个有序报文段;
    • 在发送方,若收到m2的3次重复ACK,则可以确认m3丢失,此时执行快速重传,即立即重传m3;同时,由于只是丢包而不是网络拥塞,执行快速恢复:ssthresh = cwnd / 2 ,cwnd = ssthresh

TCP与UDP区别:

1. 连接:TCP面向连接(传输前需要在双方建立可靠连接);UDP非面向连接(不需要在传输前建立连接),直接往对面发送就行了;
2. TCP可靠交付,有序交付;UDP尽可能最大交付,不保证有序;
3. TCP面向字节流(TCP把上层到达的数据看作字节流,太大会划分,太小可累积);UDP面向报文(应用层交给UDP多大的报文他就直接转发这个报文);
4. TCP有流量控制和拥塞控制,UDP的吞吐量只受数据生成速率、收发性能、带宽等影响;
5. TCP需要维护连接,需要的资源更多;
6. TCP全双工点对点通信,UDP可以一对一、一对多、多对一、多对多通信。

TCP如何实现可靠交付:

1. 序列号:只确认最后一个有序到达的数据包,保证有序;
2. 校验和:每个数据包保持一个端到端的校验和,接收方收到之后检查数据在传输过程中有没有改变,若发生了改变则丢弃;
3. 流量控制:保证接收方缓冲区足够接收数据,防止丢失;
4. 拥塞控制:降低网络拥塞程度,防止数据包丢失;
5. 停止等待:发送一个数据包之后暂停发送,等到接收到对方的确认后在发送下一个数据包;若接收方接收到重复数据包则丢弃,但仍需返回确认;
6. 超时重传:若超时未接受到对方的确认,立即重传数据包。

如何让UDP实现可靠传输:

RUDP
TCP属于是通过增大延迟和传输成本来保证质量的通信方式,UDP是通过牺牲质量来保证时延和成本的通信方式,RUDP就是在这两者中找到一个平衡点。
RUDP 提供一组数据服务质量增强机制,如拥塞控制的改进、重发机制及淡化服务器算法等,从而在包丢失和网络拥塞的情况下, RTP 客户机(实时位置)面前呈现的就是一个高质量的 RTP 流。在不干扰协议的实时特性的同时,可靠 UDP 的拥塞控制机制允许 TCP 方式下的流控制行为。
RTP
实时传输协议(RTP)为数据提供了具有实时特征的端对端传送服务,如在组播或单播网络服务下的交互式视频音频或模拟数据。应用程序通常在 UDP 上运行 RTP 以便使用其多路结点和校验服务;这两种协议都提供了传输层协议的功能。但是 RTP 可以与其它适合的底层网络或传输协议一起使用。如果底层网络提供组播方式,那么 RTP 可以使用该组播表传输数据到多个目的地。
RTP 本身并没有提供按时发送机制或其它服务质量(QoS)保证,它依赖于底层服务去实现这一过程。 RTP 并不保证传送或防止无序传送,也不确定底层网络的可靠性。 RTP 实行有序传送, RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置,例如:在视频解码中,就不需要顺序解码。
UDT
基于UDP的数据传输协议(UDP-basedData Transfer Protocol,简称UDT)是一种互联网数据传输协议。UDT的主要目的是支持高速广域网上的海量数据传输,而互联网上的标准数据传输协议TCP在高带宽长距离网络上性能很差。顾名思义,UDT建于UDP之上,并引入新的拥塞控制和数据可靠性控制机制。UDT是面向连接的双向的应用层协议。它同时支持可靠的数据流传输和部分可靠的数据报传输。由于UDT完全在UDP上实现,它也可以应用在除了高速数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等。

TCP/UDP使用场景:

TCP(质量高):

  • HTTP、HTTPS、FTP、邮件传输协议等需要可靠交付的协议;

UDP(速度快):

  • 音频、视频等对实时性要求较高的多媒体通信;
  • 数据包传输量较少的协议:如DNS;
  • 广播、多播通信

HTTP常见状态码

1XX:信息状态码

• 100 Continue,到目前为止都很正常

2XX:成功状态码

• 200 OK:成功处理请求
• 204 No Content:成功处理请求,但响应报文的数据主体为空,一般用在客户端不需要服务器返回数据时;
• 206 Partial Content:成功处理请求,但客户端进行了范围请求,响应报文只包含Content-Range范围内的数据。

3XX:重定向

• 301 Moved Permanently:永久重定向,资源永久地移动到了另外一个URI,服务器一般会返回这个URI;
• 302 Found:暂时重定向,其他同上;
• 303 See Other:同302,但明确客户端需要用GET;
• 304 Not Modified:客户端发送附带条件的请求时,不满足条件,返回时无响应主体;
• 307 Temporary Redirect:同302,但明确客户端不使用GET。

4XX:客户端错误

• 400 Bad Request:请求报文中有语法或参数错误,服务器无法理解导致无法处理;
• 401 Unauthorized:需要认证或认证失败;
• 403 Forbidden:对请求资源的访问被拒绝;
• 404 Not Found: 服务器找不到对应资源。

5XX:服务器错误

• 501 Internal Server Error:服务器出错;
• 503 Service Unavailable:服务器忙或服务器正在维护,无法请求。

HTTP请求报文:

• HTTP请求行:请求方法(GET\POST\HEAD)、URL、协议版本
• HTTP请求头:user-agent、accept、host
• 空行
• 请求数据主体:POST传输表单等数据时使用

HTTP 2.0与HTTP 1.1的区别:

HTTP 1.1新特性:

• 默认长连接
• 支持流水线
• 支持同时打开多个TCP连接
• 支持虚拟主机
• 增加状态码100
• 支持分块传输编码
• 新增缓存处理置零max-age

HTTP 1.X缺点:实现简单但性能低

• 需要打开多个连接才能实现并发和缩短延迟;
• 不压缩请求和响应首部,增加不必要的流量;
• 不支持有效的资源优先级,导致底层TCP连接利用率不高。

HTTP 2.0 与HTTP 1.X:

1. 服务器端推送:客户端在请求一个资源时,服务器会将相关资源一同返回,客户端不需要再次发送请求;比如客户端请求main.html时,服务器见script.js与style.css一同发送给客户端;
2. 请求和响应首部压缩:HTTP 1.1首部含有大量信息,每次都要重复发送。HTTP 2.0的客户端和服务器共同维护一个出现过的首部字段表,避免重复传输。此外,2.0还会使用Huffman编码对首部字段进行压缩。
3. 新的二进制形式:HTTP 1.X是基于文本的,文本形式多样,而二进制只有01组合,因此使用二进制形式更加健壮;
4. 多路复用:即连接共享,每个request对应一个id,一个连接中可以有多个request,接收端再根据id将每个request归属到不同的请求中。

Cookie Session区别:

1. session在服务器,cookie在客户端(浏览器);
2. session默认放置在服务器的一个文件里,但其实还可以放在数据库或内存中;
3. session的实现依赖session_id,一般需要使用cookie来获取session_id,但也可以在URL中传递session_id;
4. 用户验证这种场合一般使用session;
5. 维持一个会话(广义session)的核心就是客户端的唯一标识:session_id。

WebSocket:

HTTP的缺陷:单向通信,当客户端想知道服务器的状态时需要轮询,轮询效率低;

• 实现了服务器端推送,真正平等的双向通讯;
• 没有同源限制,客户端可以与任意服务器通信;
• 可以传输文本或二进制数据;
• 数据格式轻量,性能开销小,通信高效。

POST与GET的区别:

GET:

• ‘读取’一个资源。
• 反复读取不应该有副作用,也即‘幂等‘的。
• 因为是读取,就可以将请求的数据缓存,降低开销;

POST:

• ‘提交’一些信息让服务器执行一定的操作;
• 这往往会产生一些结果,也即‘不幂等’的。
• 不幂等就表示不能随意执行多次,因此不能缓存,也不能保存书签。
数据:GET只能由输入或点击一个URL触发,要携带数据只能下载URL附带的questionstring里。
  • POST一般由提交表单触发,提交时将表单编码在HTTP请求body里。
  • 安全性:不管GET还是POST,只要是携带在URL里的参数都是明文的,因此从攻击的角度来说一样的不安全。只不过GET携带的数据都在URL上,相较于请求体中的数据更容易泄漏。唯一的安全手段就是HTTPS。
  • 长度:有个说法是GET有长度限制,其实这个URL的长度限制,并且不同浏览器的限制不太一样。
  • TCP次数:GET请求体中的内容是空的,只需要一次连接,服务器就能取出请求头里的信息并返回相应资源。而优化的POST第一次发送请求头,服务器进行验证返回100 continue,这时候再发送数据,避免无法处理的请求也传输了数据浪费带宽。

Linux面试题

文件描述符:

文件描述符是一个非负的索引,一般从3开始(0,1,2均被使用),指向内核中的“文件记录表”,内核为进程要使用的文件维护一个“文件记录表”。

  • 进程需要打开或新建文件时,内核向进程返回一个文件描述符;
  • 进程需要读写文件时,也需要将文件描述符作为参数传递给函数;
  • Linux下所有对设备和文件的操作都由文件描述符完成。

I/O模型

  • 同步阻塞IO:在内核等待数据和将数据复制到进程地址空间的两个过程,除了等待啥也不做。及时返回数据,无延迟。
  • 同步非阻塞IO:在内核等待数据的阶段,进程可以轮询(瞅瞅它准备好数据了没),后一阶段等待。此外,在轮询之外的时间可以干其他活儿了,但是这样会拉长此进程的时延(也许人家在你轮询之前准备好了)。
  • 异步IO:异步模式时被动接收消息,如通过回调、通知、状态等方式被动获取;不是顺序执行。异步非阻塞IO中,用户进程进行系统调用后,无论内核是否准备好都会返回响应,进程可以去做别的事情,内核复制好数据之后会通知进程。
  • 信号驱动IO:建立SIGIO信号处理函数,数据准备好后进程会收到SIGIO信号。可以在信号处理函数中调用IO操作函数处理数据。
  • IO多路复用:
    • 多路:多个连接,复用:一个或少量线程。即使用一个或少量的线程去处理多个连接。
    • 不停地查看多个任务的完成状态,只要有任何一个任务完成了,就去处理它。
    • 三种模式:
      • select:轮询,任何一个进程的数据准备好了就来通知一声,限制只能同时监视1024个接口;
      • poll:和select一样,不过去掉了1024的限制;
      • epoll:回调,不用去轮询了。

同步与异步:主要关注消息通信机制。

  • 同步:就是在发出一个调用之后,调用没有得到结果之前该调用不返回。即同步是主动等待消息。
  • 异步:异步调用不会等待结果而是立即返回,然后等待被调用者使用消息、通知或者回调函数来通知调用者。即异步是被动接收消息。

阻塞与非阻塞:主要关注程序在等待时的状态。

  • 阻塞是指程序在等待结果的时候被挂起,不能去完成别的任务【浪费时间】;
  • 非阻塞是指程序在等待的过程中可以做别的事情【需要切换开销】。

Linux常用指令:

• ls:列出文件或者目录的信息;
• cd:切换路径(绝对/相对路径);
• mkdir:新建目录;
• rmdir:删除目录,目录必须为空;
• rm -rf :+{文件名/} 递归删除目录即子目录
• touch:更新文件时间或新建文件;
• cp:复制文件,若源文件有两个及以上,目标路径一定要是目录;
• mv:移动文件;
• rm:删除文件;

C++面试题

指针和引用的区别:

本质:指针是地址,引用是别名。

  • 引用被创建时必须同时初始化,指针可以在任意时刻初始化;
  • 引用不能为空,指针可以为空;引用一旦初始化就不能改变引用关系,指针指向的对象可以改变。
  • 指针强大但危险,若只需借用别名时应使用引用。

函数传参的方式:

  • 值传递:【n -> x】,函数内的x是外部变量n的拷贝,当x改变时n不会改变;
  • 指针传递:【&n -> *x】,函数内的x是指向外部变量的指针,当x改变时n也会改变;
  • 引用传递:【n -> &x】,函数内的x是n的引用,实际上是同一个对象,当x改变时n也会改变。

为什么需要结构体字节对齐

  • 某些平台只能在特定地址处访问特定类型数据;
  • 提高存取速度(int类型若从奇数地址处开始需要两个读取周期);
  • 字节对齐的作用不仅是便于cpu快速访问,同时合理的利用字节对齐可以有效地节省存储空间。

内存动态申请和释放

  • 动态申请的空间没有具体名称,只能通过指针间接访问(无论new还是malloc方式)。
  • 动态申请空间都是存放在堆中,有别于系统自动分配的空间是存放在堆栈中(即栈)。
  • 栈中系统分配的空间,在使用结束会自动释放。而程序员动态申请的空间则需要人为来释放,否则随着程序运行,内存占用积累,很容易造成内存泄漏。

内存泄漏和内存溢出

程序申请的内存没有及时回收就造成了内存泄漏,多次内存泄漏会积累成内存溢出。当程序申请内存时,可用内存不够了,就叫内存溢出。

多态

调用成员函数时,会根据调用函数的对象而调用不同的函数。

面向对象

面向对象是一种对现实世界的理解和抽象,将需求要素转化为对象的数据和方法进行问题处理。

STL

  • 容器(Containers):deque、list、vector、map等;
  • 算法(Algorithms):对容器内容执行初始化、排序、搜索、转换等方法;
  • 迭代器(Iterators):遍历对象集合的元素。

其他面试题

测开

hash冲突:

hash表:【正向快速、逆向困难、输入敏感、冲突避免】
hash冲突: 两个不同的对象计算的hash值相同,就出现了冲突。

怎么解决:

  • 开放定址法【再散列法】:发生冲突后按照某一次序找到下一个空闲的地址。
    • 线性再散列:di = 1, 2, 3… 从发生冲突的下一个单元开始依次探查是否有空闲,若到表尾还没游空闲就从表头开始接着探查;
    • 二次再散列:di = 12,-12,22,-22…. 在表的左右进行跳跃式探测,比较灵活;
    • 随机再散列:di = 伪随机序列。
  • 再哈希法:同时构造多个hash函数,一个不行换下一个。不易产生聚集,但增加了计算时间;
  • 链地址法:将所有hash值相同的元素存放在一个链表中,并将链表头节点放入哈希表对应地址。插入删除时访问链表,适用于经常插入删除的情况;
  • 建立公共溢出区:建立基本表和溢出表,和基本表发生冲突的都存进溢出表。

接口

  • python接口类:只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能。在python中接口由抽象类和抽象方法去实现,接口是不能被实例化的,只能被别的类继承去实现相应的功能。
  • 软件接口:负责在不同的系统间,或者同意系统内不同模块之间传输或接收数据的并做处理的类或者函数。

软件测试四个阶段:

 - 单元测试:检验软件最小单元的正确性,消除局部模块上的逻辑或功能的错误和缺陷。
    a. 阶段:编码后;
    b. 对象:最小单元——模块;
    c. 人员:测试人员,开发人员;
    d. 依据:代码和注释 + 详细设计文档;
    e. 方法:白盒测试;
    f. 内容:模块接口测试,局部数据结构测试,路径测试,错误处理测试,边界测试。
 - 集成测试:将程序模块使用适当的集成策略组装起来,对系统的接口及继承后的功能进行正确性检测。主要检测软件单位之间的接口是否正确。
    a. 阶段:单元测试后;
    b. 对象:模块间的接口;
    c. 人员:测试人员,开发人员;
    d. 依据:单元测试的模块 + 概要设计文档;
    e. 方法:黑盒 + 白盒;
    f. 内容:模块之间的数据传输、模块间的功能冲突、模块组装功能正确性、全局数据结构、单模块缺陷对系统的影响。
 - 系统测试:对功能、性能及软件所运行的软硬件环境进行测试,包括回归测试和冒烟测试。
    a. 阶段:集成测试后;
    b. 对象:整个系统;
    c. 人员:黑盒测试人员;
    d. 依据:需求规格说明文档;
    e. 方法:黑盒测试;
    f. 内容:功能、界面、可靠性、易用性、性能、兼容性、安全性等。
 - 验收测试:确保软件准备就绪,按照项目合同、任务书、双方约定的验收依据文档,向软件购买者展示该软件系统满足需求。
    a. 阶段:系统测试后;
    b. 对象:整个系统;
    c. 人员:用户或需求方;
    d. 依据:用户需求、验收标准;
    e. 方法:黑盒测试;
    f. 内容:同系统测试。

测试方法分类

  • 白盒测试:关注内部处理逻辑和输入输出;分为静态测试(不运行,模拟分析)和动态测试(运行实际用例),动态测试主要技术有路径和分支测试;
  • 黑盒测试:不关注内部逻辑,只关注输入输出。是更接近用户的测试方法,会关注易用性、性能、用户界面、业务流程等内容。
  • 灰盒测试:集成测试阶段同时使用白盒测试和黑盒测试即为灰盒测试。是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。灰盒测试不像白盒那样详细、完整,但又比黑盒测试更关注程序的内部逻辑,常常是通过一些表征性的现象、事件、标志来判断内部的运行状态。

技术运维

CDN加速

CDN:内容分发网络
通过在现有的网络中中增加一层新的CACHE(缓存)层,将网站的内容发布到最接近用户的网络”边缘“的节点,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。

解析过程:
浏览器向域名解析服务器发出解析请求,由于CDN 对域名解析过程进行了调整,所以用户端一般得到的是该域名对应的 CNAME 记录(存有CDN节点),此时浏览器需要再次对获得的 CNAME 域名进行解析才能得到缓存服务器实际的IP 地址。 注:在此过程中,DNS 解析服务器根据用户端的源IP 地址,如地理位置(北京还是上海)、接入网类型(电信还是网通)将用户的访问请求定位到离用户路由最短、位置最近、负载最轻的Cache 节点(缓存服务器)上,实现就近定位。定位优先原则可按位置、可按路由、也可按负载等。

分布式

  • Hadoop是由java语言编写的,在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架。Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
  • HDFS是一个分布式文件系统:引入存放文件元数据信息的服务器Namenode和实际存放数据的服务器Datanode,对数据进行分布式储存和读取。把HDFS理解为一个分布式的,有冗余备份的,可以动态扩展的用来存储大规模数据的大硬盘。
  • MapReduce是一个计算框架:MapReduce的核心思想是把计算任务分配给集群内的服务器里执行。通过对计算任务的拆分(Map计算/Reduce计算)再根据任务调度器(JobTracker)对任务进行分布式计算。把MapReduce理解成为一个计算引擎,按照MapReduce的规则编写Map计算/Reduce计算的程序,可以完成计算任务。

DevOps

是开发(Development)和运维(Operations)的组合,旨在促进开发和运维人员之间的沟通,使软件的开发、测试和发布更快捷并且可靠。实际上这个名字忽略了测试,DevOps其实是一个闭环,从规划、开发,到测试、部署、运维都涵盖在里面。我理解的DevOps是一种思想,一种方案。

机房建设的考虑

1. 安全:包括用电安全和制冷
2. 能源:需要稳定供电保证运行
3. 可扩展性:升级扩展的时候比较方便,能向下兼容
4. 维护方便

服务运行时监控什么:

1. CPU利用率
2. 资源的使用情况
3. 告警情况(告警数、告警类别)
4. 网络速率
5. 存储空间使用情况

编程题

10亿个数字中最大的100个:

  • 使用最小堆:先取出100个元素建立最小堆(插入法heappush建堆时间复杂度为O(klogk),下拉交换法heapify建堆时间复杂度为O(k)),然后依次用剩下的元素和堆顶比较,若新元素更小则丢弃,否则将其插入堆中并维护堆性质。总的时间复杂度为O(klogk + (n-k)logk) = O(nlogk),空间复杂度O(n)。
  • 优化:将原文件分割成多个子文件,找出每个子文件中最大的100个数,再使用结果集进行归并获得总的最大的100个数。

1亿数据如何排序:

  • 分成几块,每块分别使用堆排序,然后将最后结果归并;
  • 为什么不用快速排序,因为堆排序能保证一定是O(nlogn),而快速排序在较差情况可能为O(n^2);

排序算法的选择:

  • 当n较大时,选用时间复杂度为O(nlogn)的排序算法:
    • 要求稳定性时使用归并排序,可以在子序列较小时使用插入排序提高效率;
    • 数据随机分布且有足够辅助内存时使用快速排序;
    • 当内存有限时使用堆排序,且堆排序不会出现快速排序可能出现的最坏情况;
  • 当n较小时,选用插入排序或选择排序:
  • 当数据基本有序时使用插入排序或冒泡排序。

如何判断一个数是否在40亿个整数中:

**bitmap:**使用位来表示状态。
32位int的范围,总共就是2的32次方,大概42亿多点。可以申请2的32次方个位,每个位的0/1代表相应整数是否存在,如(11011代表数字2不存在)。一个32位整数占4个字节,2的32次方个位是2的29次方个字节,1GB约10亿个字节,共512MB内存。原来32位的整数转化成了1位的布尔值,占用空间减少了32倍。

BitMap的原理和实现

1亿个正整数范围是0-42亿,求出现次数是2的数字:

为每个数字分配2个位,表示其出现的次数:00表示0次,01表示1次,10表示2次,11表示大于2次。顺序扫描数组并记录出现次数,最后再扫描一遍数组找出出现两次的数字。

其它

Git命令:

新建:git init
克隆:git clone
添加更改:git add <filename>
添加全部更改:git add .
删除:git rm <filename>
提交更改:git commit -m 'commit message'
将更改推到远程:git push origin master
本地连接远程:git remote add origin <server>
本地与远程同步:git pull
新建分支:git checkout -b <branch>
切换到master分支:git checkout master
删除分支:git branch -d <branch>
将分支推到远程:git push origin <branch>
将更改合并到另一分支:git merge <branch>
比较两分支:git diff <source> <target>
新建标签:git tag <tag> <commit id>
提交id:  git log
复原:git checkout --<filename>

中间件

将具体业务和底层逻辑解耦的组件,使用服务的人不需要知道底层逻辑的实现原理。

中间件能给客户带来什么:系统开发更简单,基于成熟的组件来做,可以极大的减少技术选择成本。在系统交互的时候,直接使用中间件进行连接和交互,以及代码开发和人工成本。

内存对齐:

由于计算机不是以字节为单位访问数据,而是以块(2,4,6,8字节)为单位。如果不进行内存对齐,原本一次完成的读写可能需要多次,还有额外的数据merge或数据分离操作,这些使得效率低下。

常见加密算法:

  • 对称加密:加密和解密使用同样的密钥,用同一种方式安全地共享密钥很难。如AES。
  • 非对称加密:加密和解密使用不同的密钥(公共密钥/私有密钥)。如RSA。
  • 数字证书:属于非对称加密。一个组织可以使用证书将一组公钥和私钥与其拥有者相关联。

哈希算法:

  • 哈希算法主要用于保障数据的真实性(即完整性),发送方将数据和哈希值一同发送,接收方通过相同的哈希函数计算哈希值来验证数据在通信过程中是否被篡改。【正向快速、逆向困难、输入敏感、冲突避免】
  • MD5:消息填充,最后的消息长度是512的整数倍,生成128位报文摘要;
  • SHA:
    • SHA1对明文的预处理和MD5相同,得到512整数倍的消息。区别在于消息不得长于2^64次方,并生成160位的报文摘要。
    • SHA256产生256位的报文摘要。

设计模式:

是一套被大家广泛认可和使用的,经过分类的代码设计经验的总结。

  • 工厂模式:使一个类的实例化延迟到子类。
    定义一个用于创建对象的接口,让子类决定实例化哪一类。

  • 单例模式:保证系统中一个类只有一个对象的实例。

  • 为什么使用单例模式:

    • 单例模式节省公共资源;
    • 单例模式方便控制。
  • 实现:

    • 构造私有:为了防止一个类被多次实例化,将类的构造函数设为私有;
    • 以静态方法返回实例:提供类的方法让外界获取实例对象;
    • 只实例化一次,之后都使用实例化后的对象。
  • 两种模式:

    • 饿汉模式:先创建好实例,需要时直接使用。容易造成资源浪费(一直不使用);
    • 懒汉模式:需要使用的时候再构造实例,解决了资源浪费。但是在并发时可能同时实例化多个对象,需要用锁控制。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值