⽹络
深信服 Java ⾯试
深信服每年在 9-10 ⽉份都会开展线下校园招聘,不少同学经过线下⾯试就直接通过了所有⾯试,
等待了⼏周等于开奖了。
不少211 、985 硕⼠同学跟我反馈,深信服给了个 ⼤⽩菜:19k x 16 (base 深圳),这个薪资相⽐互联⽹中⼤⼚是会缺少竞争⼒,被⼤多数同学称为劝退价。
其实深信服技术岗位的校招薪资也能开的很⾼,但是普 通档的 offer 和 sp 档的 offer 差距很⼤,根
据其他同学的爆料, 我也总结了深信服 25 届技术岗的开奖情况:
普通档:17 〜19k x (12 〜18 )
sp 档:22 〜24k x (12 〜18 ) + 15w 〜20 签字费分 3 年发
ssp 档位:28 〜29k x (12 〜18 ) + 30w 签字费分 3 年发
是不是感觉每个档次差距还挺⼤的,sp 、ssp 档属于少部分优秀的⼈才有机 会拿到,⼤部分都是在
普通档的区间,⽽且针对 sp 档以上的 offer ,还会有额外的签字费,15w-30w 分三年发, 中途离
职就没有了。
年终奖的话,有的部⻔是没有( 12 薪资),有的部⻔是 4 个⽉( 16 薪资),有的部⻔是 6 个⽉
(18 薪资),最终能不能拿满也是看绩效和公司的营收情况。
深信服是会加班的,加班接受程度,周⼀、周⼆、周三晚⼋点半, 每个⽉第⼀周六半天。
那问题来了,深信服⾯试难度如何呢?
我找 了⼀位深信服软开的校招⼆⾯的线下⾯经,提供给⼤家学 习学习,主要拷打了 35 分钟,问完
简历上上 的项⽬和校园经历之后,就开始问了⼀些⼋股⽂,主要是计算机⽹络和数据库⽅⾯的知
识,也需要现场⼿撕算法,最后还问了⼀个逻辑题⽬。

⽹络
⽹络协议为什么 需要分层?
复杂的系统需要分层,因为每⼀层都需要专注于⼀类事情。我们的⽹络分层的原因也是⼀样,每
⼀层只专注于做⼀类事情。分层带来的好处 :
「各层之间相互独⽴」:各层之间相互独⽴,各层之间不需要关⼼其他层是如何实现的,只需要
知道⾃⼰如何调⽤下层提供好的功能就可以了(可以简单理解为接⼝调⽤),这个和我们对开发
时系统进⾏分层是⼀个道理。
「提⾼了整体灵活性」 :每⼀层都可以使 ⽤最适合的技术来 实现,你只需要保证你提供的功能
以及暴露的接⼝的规则没有改变就⾏了,这个和我们平时开发系统的时候要求的⾼内聚、低耦
合的原则也是可以对应上的。
「⼤问题化⼩」 :分层可以将复杂的⽹络间题分解为许多⽐较⼩的、界线⽐较清晰简单的⼩问
题来处理和解决。这样使得复杂的计算机⽹络系统变得易于设计 ,实现和标准化。 这个和我们
平时开发的时候,⼀般会将系统功能分解,然后将复杂的问题分解为容易理解的更⼩的问题是
相对应的,这些较⼩的问题具有更 好的边界(⽬标和接⼝)定义。
说到计算机⽹络分层,我想到了计算机世界⾮常⾮常有名的⼀句话,这⾥分享⼀下:
「计算机科学领域的任何 问题都可以通过增加⼀个间接的中间层来解决,计算机整个体系从上 到
下都是按照严格的层次结构设计 的。」
还有还有,如果⼀层不够那就加两层吧!
因此,⽹络协议需要分层主要是因为以下原因:
⽹络不稳定:可能存在节点故障、停电、⽹线插头被拔等各种意外情况,导致数据传输失败,
分层可以更好地处理重传等问题。
数据量⼤:⼤数据需要切成块来传输,分层便于管理和控制分 块传输的过程。
不同应⽤需求:不同的应⽤层协议(如 HTTP 、FTP 、SMTP 等)对数据传输有不同的要求,分
层可以将共性的功能抽取出来,提⾼效率和灵活性。
tcp 保证可靠性的原理?
CP 协议主要通过以下⼏点来保证传输可靠性:连接管理、序列号、确认应答、超时重传、流量控
制、拥塞控制。
连接管理:即三次握⼿和四次挥⼿。连接管理机制能够建⽴起可靠的连接,这是保证传输可靠
性的前提。
序列号:TCP 将每个字节的数据都进⾏了编号,这就是序列号。序列号的具体作 ⽤如下:能够保
证可靠性,既能防⽌数据丢失,⼜能避免数据重复。能够保证有序性,按照序列号顺序进⾏数
据包还原。能够提⾼效率,基于序列号可 实现多次发送,⼀次确认。
确认应答:接收⽅接收数 据之后,会回传ACK 报⽂,报⽂中带有此次 确认的序列号,⽤于告知
发送⽅此次 接收数 据的情况。在指定时间后,若发送端仍未收到确认应答,就会启动超时重
传。
超时重传:超时重传主要有两种场景:数据包丢失:在指定时间后,若发送端仍未收到确认应
答,就会启动超时重传,向接收端重新发送数据包。确认包丢失:当接收端收到重复数据(通过
序列号进⾏识别)时将其丢弃,并重新回传ACK 报⽂。流量控制:接收端处理数据的速度是有限的,如果发送⽅发送数据的速度过快,就会导致接收
端的缓冲区溢出,进⽽导致丢包。为了 避免上述情况的发⽣,TCP ⽀持根据接 收端的处理能⼒,
来决定发送端的发送速 度。这就是流量控制。流量控制是通过在TCP 报⽂段⾸部维护⼀个滑动窗
⼝来实现的。
拥塞控制:拥塞控制就是当⽹络拥堵严重时,发送端减少数据发送。拥塞控制是通过发送端维
护⼀个拥塞窗⼝来实现的。可以得出,发送端的发送速 度,受限于滑动窗⼝和拥塞窗⼝中的最
⼩值。拥塞控制⽅法分为:慢开始,拥塞避免、快重传和快恢复。
拥塞控制的具体逻辑?
TCP 拥塞控制是为了 防⽌⽹络过载 ⽽采取的⼀系列措施,这些措施能够动态调整数 据传输速率以适
应⽹络状态。TCP 的拥塞控制主要包括以下⼏种算法和策略:
慢启动:当 TCP 连接建⽴或发⽣丢包重传时,TCP 从⼀个相对较⼩的拥塞窗⼝(cwnd )开始。
这⼀窗⼝随着每次 成功收到确认(ACK )⽽指数增⻓(每收到⼀个 ACK ,窗⼝⼤⼩增加 1),⽬
的是快速探测可⽤带宽。
拥塞避免:当拥塞窗⼝达到⼀个阈值(ssthresh, slow start threshold )后,TCP 进⼊拥塞避免阶
段。在这个阶段,拥塞窗⼝以线性增⻓⽅式增加(每经过⼀个轮次,增加 1,即每 RTT 增加
1),这样可以减少发⽣拥塞的⻛险。
快速重传(Fast Retransmit ):发送⽅在未收到某个数据包的 ACK 时,收到三个 重复的 ACK
(即接收⽅多次确认相同的数据包)时,发送⽅会⽴即重传缺失的数据包,⽽不是等待重传定
时器超时。这有助于减少由于丢 包导致的延迟。
快速恢复(Fast Recovery ):在快速重传机制之后,TCP 会进⼊快速恢复阶段。此时,ssthresh
被设置为当前的 cwnd 值的⼀半,接着 cwnd 被设置为 ssthresh 加上三个 未确认的 MSS (最⼤
段⼤⼩)。这样,TCP 在丢包后不会完全降低其传输速率,⽽是球⽴⼀个新的⽬标以便 快速返回
正常状态。
http 状态码有哪些?

HTTP 状态码分为 5 ⼤类
1xx 类状态码属于提⽰信息,是协议处理中的⼀种中间状态,实际⽤到的⽐较少。
2xx 类状态码表⽰服务器成功处理了客⼾端的请求,也是我们最愿意 看到的状态。
3xx 类状态码表⽰客⼾端请求的资源发⽣了变动,需要客⼾端⽤新的 URL 重新发送请求获取资
源,也就是重定向。
4xx 类状态码表⽰客⼾端发送的报⽂有误,服务器⽆法处理,也就是错误码的含义。
5xx 类状态码表⽰客⼾端请求报⽂正确,但是服务器处理时内部发⽣了错误,属于服务器端的错
误码。
其中常⻅的具体状态码有:
200 :请求成功;
301 :永久重定向;302 :临时重定向;
404 :⽆法找到此⻚⾯;405 :请求的⽅法类型不⽀持;
500 :服务器内部出错。
MySQL
数据库的锁有哪些锁?
在 MySQL ⾥,根据加锁的范围,可以分为全局锁、表级锁和⾏锁三类。

全局锁:通过flush tables with read lock 语句会将整个数据库就处于只读状态了,这时其他线程
执⾏以下操作,增删改或者表结构修改都会阻塞。全局锁主要应⽤于做全库逻辑备份,这样在
备份数据库期间,不会因为数据或表结构的更新,⽽出现备份⽂件的数据与预期的不⼀样。
表级锁:MySQL ⾥⾯表级别的锁有这⼏种:
表锁:通过lock tables 语句可以对表加表锁,表锁除了会限制别 的线程的读写外,也会限制本
线程接下来的读写操作。
元数据锁:当我们对数据库表进⾏操作时,会⾃动给这个表加上 MDL ,对⼀张表进⾏ CRUD 操
作时,加的是 MDL 读锁;对⼀张表做结构变更操作的时候,加的是 MDL 写锁;MDL 是为了 保
证当⽤⼾对表执⾏ CRUD 操作时,防⽌其他线程对这个表结构做了变更。
意向锁:当执⾏插⼊、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记 录加独占
锁。意向锁的⽬的是为了 快速判断表⾥是否有记录被加锁。
⾏级锁:InnoDB 引擎是⽀持⾏级锁的,⽽ MyISAM 引擎并不⽀持⾏级锁。
记录锁,锁住的是⼀条记录。⽽且记录锁是有 S 锁和 X 锁之分的,满⾜读写互斥,写写 互斥
间隙 锁,只存在于可重复读隔离级别,⽬的是为了 解决可重复读隔离级别下幻读的现象。
Next-Key Lock 称为临 键锁 ,是 Record Lock + Gap Lock 的组合,锁定⼀个范围,并且锁定记
录本⾝。
间隙 锁会不会出现死锁情况 为什么 ?
Gap Lock 称为间隙 锁,只存在于可重复读隔离级别,⽬的是为了 解决可重复读隔离级别下幻读的
现象。
假设,表中有⼀个范围 id 为(3,5)间隙 锁,那么其他事 务就⽆法插⼊ id = 4 这条记录了,这样就有效的防⽌幻读现象的发⽣。

间隙 锁虽然存在 X 型间隙 锁和 S 型间隙 锁,但是并没有什么 区别,间隙 锁之间是兼容的,即两个 事务可以同时持有包含共同间隙 范围的间隙 锁,并不存在互斥关系,也就是⼀个事 务如果获取了
(3, 5 )范围的间隙 锁,那么另外的事务也能成功获取相通范围的间隙 锁,因为间隙 锁的⽬的是防
⽌插⼊幻影记录⽽提出的。
间隙 锁主要是与插⼊意向锁会产⽣冲突,说间隙 锁在遇到插⼊意向锁的时候,是会发⽣死锁的问
题的,⽐如下⾯的场景:

事务A和事务B 在执⾏ select for update 的时候,产⽣了间隙 锁,但是在后⾯的 insert 过程中都阻
塞了,因为插⼊的位置是间隙 锁范围的,因此就阻塞了,两个事 务都在等待双⽅的间隙 锁释放,
于是就造成了循环等待,导致死锁。
⼿撕
⼆叉树层序遍历
可以使 ⽤队列来实现,下⾯是⼀个⽤ C++ 编写的⼆叉树层序遍历的⽰例代 码。该实现使⽤了标准
库中的队列( std::queue )来完成层序遍历。
#include <iostream>
#include <queue>
using namespace std ;
// 定义二叉树节点结构
struct TreeNode {
int val ;
TreeNode * left ;
TreeNode * right ;
TreeNode (int x) : val (x), left (nullptr ), right (nullptr ) {}
};
// 层序遍历函数
void levelOrderTraversal (TreeNode * root ) {
if (root == nullptr ) {
queue<TreeNode*> q; // 使用 std::queue 来实现队列
q.push(root); // 将根节点入队
while (!q.empty()) {
TreeNode* current = q.front(); // 取出队列的前端节点
q.pop(); // 出队
cout << current->val << " "; // 访问当前节点
// 将左子树和右子树入队
if (current->left != nullptr) {
q.push(current->left);
}
if (current->right != nullptr) {
q.push(current->right);
}
}
}逻辑
七个 球,有⼀个不 ⼀样,称⼏次能找出来
要找出七个 球中⼀个不 ⼀样的球(假设这个球的重量 不同),可以使 ⽤天平称重的⽅法,称的次数
取决于不 ⼀样的球是重还是轻。以下是解决⽅案的步骤:
第⼀步:分组称重
第⼀次称重:将七个 球分为三 组,分别 是:
组1:3个球(⽐如球A、B、C)
组2:3个球(⽐如球D、E、F)
组3:1个球(球G)
将组1与组2进⾏称重:
情况1:如果两组重量 相等,说明不⼀样的球在组3的球G中。
情况2:如果两组重量 不等,进⼀步确定哪个组中有不⼀样的球(我们也可以知道这个球是重还
是轻,根据称重的结果)。
第⼆步:根据第⼀次称重的结果进⾏第⼆次称重
情况1:如果第⼀次称重重量 相等(球G不⼀样)。
第⼆次称重:从组G(只有1个球)中选出,直接得出球G就是不⼀样的球。
情况2:如果组1(A、B、C)和组2(D、E、F)有不同的重量 ,假设组1重于组2。那么可以进⼀
步判断:
第⼆次称重:将组1的两个 球与组2的两个 球进⾏称重:⽐如将A和B与D和E进⾏称重。
如果A和B重于D和E,则不⼀样的球在A或B中,并且可以知道它是重的。
如果A和B轻于D和E,则不⼀样的球在D或E中,并且可以知道它是轻的。
如果两边重量 相等,则不⼀样的球必在组中没有称重的那个球(C或F),并且可以知道它是重还
是轻。
第三步:找到不⼀样的球
第三次称重:根据上⼀步的结果,可以从两个 候选球中选择⼀个进⾏称重。⽐如:
假如上⼀步得知A和B中有⼀个球不⼀样,再称重⼀次A和B,这样可以确定出哪个是不同的球。
因此,通过这 三个 称重的步骤,最多只需要 3 次称重 就可以找出7个球中那个不 ⼀样的球。
