此劝退,非彼劝退。
大家好,我是皇子。
深信服每年在 9-10 月份都会开展线下校园招聘,不少同学经过线下面试就直接通过了所有面试。线下面得还挺快的,当天上午一面二面,下午HR面,每轮都是半小时左右。
然后现在就在等着开奖了,其中也不少的211、985硕士的同学。
不过有同学反馈已经收到了offer了,不过开得比白菜价高一点点,相比于中大厂没有啥竞争力,自然被那些大多数称为劝退价
。所以很多还在许愿其他公司尽快走完流程,许愿OC(offer 候选人)中…
根据网上资料,我也总结了一下深信服 25 届技术岗的开奖情况如下。
-
普通档:17~19k x (12~18) -
sp 档:22~24k x (12~18) + 15w~20签字费分 3 年发 -
ssp 档位:28~29k x (12~18) + 30w签字费分 3 年发
除了月薪还有年终奖,有的部门只有12个月的薪资,而表现优秀的部门则可能享受4个月(16个月薪资)真正6个月(18个月薪资)的奖励。最终能够拿到全额年终奖,需要开盲盒,除了自己的绩效表现,还得看部门和公司的整理营收情况。已经拿到offer的就好好努力,等着开盲盒就好了。
然后再说一说sp和ssp挡位,这算是深信服薪资结构的顶端了,与普通档相差挺大的,谁能拿到那都是值得羡慕的“别人家的孩子”。因为这两个挡位除了高薪之外,还有额外的签字费,15万到30万不能,分三年发放完,如果中途离职就泡汤了。
嗯,需要提醒的是:在深信服的工作节奏挺快的,加班也是家常便饭,身体不好的,就别往火坑里跳了。每周一、周二、周三晚上八点半,以及每个月的第一个周六半天,是公司规定的加班时间。大家合理安排好时间。还有一个隐藏信息是,Java八股文很少问,感觉Java选手可能要转go或者c++。
那问题来了,深信服面试难度如何呢?
我找了一位深信服软开的校招二面的线下面经,提供给大家学习学习,主要拷打了 30 分钟,问完简历上上的项目和实习经历之后,就开始问了一些八股文,主要是计算机网络和数据库方面的知识,也需要现场手撕算法,现场给纸和笔,出算法写思路,不需要写出具体代码。
网络
说说五层网络协议体系结构的理解
五层协议的体系结构算是中和了 OSI 和 TCP/IP 的优点,直接图:
应用层:为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。
传输层(运输层):为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。
网络层:为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。
数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。
物理层:考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。
其中表示层和会话层用途如下:
表示层 :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。
会话层 :建立及管理会话。
五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。
TCP 和 UDP 区别是什么
特性 | TCP (传输控制协议) | UDP (用户数据报协议) |
---|---|---|
连接类型 | 面向连接 | 无连接 |
建立连接 | 需要三次握手 | 不需要建立连接 |
释放连接 | 需要四次挥手 | 无需释放连接 |
可靠性 | 提供可靠传输 | 不保证可靠传输 |
确认机制 | 有确认机制 | 无确认机制 |
数据顺序 | 按序到达 | 可能乱序到达 |
重传机制 | 有重传机制 | 无重传机制 |
拥塞控制 | 有拥塞控制机制 | 无拥塞控制机制 |
头部开销 | 较大(20字节) | 较小(8字节) |
适用场景 | 文件传输、网页浏览、电子邮件等需要可靠传输的应用 | 实时通信、语音和视频通话、在线游戏、直播等对实时性要求高的应用 |
性能 | 较低(由于额外的可靠性和控制机制) | 较高(由于较少的开销和控制机制) |
数据单位 | 字节流 | 数据报 |
端到端通信 | 点对点 | 支持一对一、一对多、多对一和多对多的通信 |
流量控制 | 有流量控制机制 | 无流量控制机制 |
在网络通信中,传输层的两个主要协议是 TCP(传输控制协议)和 UDP(用户数据报协议)。
TCP 提供的是面向连接的服务,这意味着在实际传送数据之前,发送方和接收方之间需要先建立一个连接。这个过程通过三次握手
完成,确保双方都准备好进行数据交换。数据传送结束后,还需要通过四次挥手
的过程来断开连接,以释放系统资源。
这种机制虽然增加了复杂性和开销,但也带来了高度的可靠性。首先,数据传输前的三次握手确保了连接的建立。其次,数据传输过程中,TCP 使用确认机制来确保每个数据包都被正确接收。如果某个数据包丢失,TCP 会自动重传。另外,TCP 还通过滑动窗口机制进行流量控制,防止接收方因数据量过大而无法处理。最后,TCP 具有拥塞控制机制,可以根据网络状况动态调整发送速率,避免网络拥塞。
虽然牛逼的这些可靠性机制也带来了额外的开销。TCP 协议数据单元的头部相对较大,包含了许多控制信息。在处理确认、流量控制、计时器以及连接管理等任务需要消耗较多的处理器资源。
相比之下,UDP 是一种无连接的协议。在发送数据之前,发送方不需要与接收方建立连接。接收方在收到 UDP 数据报后,也不需要给出任何确认。这种设计使得 UDP 的通信过程非常快速和直接,减少了延迟。
UDP 不提供数据传输的可靠性保证。发送的数据报文可能会丢失、重复或乱序到达接收端,但 UDP 不会对这些问题进行纠正。因此,UDP 更适合于那些对实时性要求较高,但可以容忍一定程度数据丢失的应用场景。例如,即时通信中的语音和视频通话、在线游戏、直播流媒体等应用通常使用 UDP。这些应用更看重数据传输的速度和实时性,而不是绝对的可靠性。即使某些数据包丢失,也不会对整体体验造成太大影响。
TCP 黏包是怎么产生的?
发送方产生粘包
采用 TCP 协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘包),双方在连接不断开的情况下,可以一直传输数据。
但当发送的数据包过于的小时,那么 TCP 协议默认的会启用 Nagle 算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包
的状态了。
接收方产生粘包
接收方采用 TCP 协议接收数据时的过程是这样的:
数据到接收方,从网络模型的下方传递至传输层,传输层的 TCP 协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C 语言用 recv、read 等函数)。
这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包
。(放数据的速度 > 应用层拿数据速度)。
怎么解决拆包和粘包?
在使用 TCP 协议进行数据传输时,由于 TCP 是基于字节流的,数据包在传输过程中可能会发生拆包和粘包现象。拆包
是指一个完整的数据包被分割成多个小包,而粘包
则是指多个数据包被合并成一个大包。这两种现象会导致接收端在处理数据时遇到困难,因为无法准确区分每个完整的数据包。所以解决 TCP 拆包和粘包问题的关键在于确保接收端能够准确地识别每个数据包的边界。
UDP 没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是 UDP 报文或用户数据报,发送的时候既不合并,也不拆分。
1、特殊字符控制
一种常见的解决方法是在每个数据包的末尾添加特殊字符或字符串作为分隔符。接收端通过识别这些特殊字符来判断数据包的边界。
示例:
-
发送端发送数据包: "Hello,World!@#Goodbye,World!@#"
-
接收端通过识别 !@#
来分割数据包,提取出"Hello,World!"
和"Goodbye,World!"
。
优点:
-
实现简单,容易理解。
缺点:
-
特殊字符可能会出现在实际数据中,需要额外处理。 -
如果数据中包含大量特殊字符,可能会导致误判。
2、在包头添加数据包长度
另一种更可靠的方法是在每个数据包的头部添加一个固定长度的字段,用于指示数据包的实际长度。接收端通过读取这个长度字段来确定数据包的边界。
示例:
-
发送端发送数据包: "00015Hello,World!00014Goodbye,World!"
-
接收端首先读取前四个字节 00015
,得知第一个数据包的长度为 15 字节,然后读取接下来的 15 字节,提取出"Hello,World!"
。接着读取下一个长度字段00014
,再读取 14 字节,提取出"Goodbye,World!"
。
优点:
-
可靠性高,不会受到数据内容的影响。 -
适用于各种类型的数据。
缺点:
-
需要在每个数据包中增加额外的长度字段,增加了数据传输的开销。 -
实现稍微复杂一些,需要处理长度字段的读取和解析。
3、使用 Netty 等框架
对于复杂的网络编程任务,可以使用成熟的网络编程框架,如 Netty。Netty 提供了专门的编解码器来处理拆包和粘包问题。
MySQL
limit 1000000 很慢的话,需要如何解决?
方案一:使用最大记录偏移量如果 id 是连续的,可以通过返回上次查询的最大记录(偏移量),再往下查询。
示例:
-- 第一次查询
SELECT id, name FROM employee WHERE id > 0 ORDER BY id LIMIT 10;
-- 下一次查询
SELECT id, name FROM employee WHERE id > 1000000 ORDER BY id LIMIT 10;
优点:
-
避免了大量数据的扫描,提高了查询效率。 -
适用于 id 连续且递增的情况。
缺点:
-
如果 id 不连续或有删除操作,可能需要额外处理。
方案二:限制页数
在业务允许的情况下,限制用户查询的页数。大多数用户不会翻阅太多页,因此可以与业务方讨论是否有必要支持如此深的分页。
示例:
-- 限制最多查询第 100 页
SELECT id, name FROM employee ORDER BY id LIMIT 1000, 10;
优点:
-
简化了查询逻辑,提高了性能。 -
减少了数据库的压力。
缺点:
-
可能会影响用户体验,如果确实有用户需要查询更深的分页。
方案三:使用 ORDER BY + 索引
确保 id 列上有索引,然后使用 ORDER BY 进行排序。
示例:
-- 确保 id 列上有索引
CREATE INDEX idx_employee_id ON employee(id);
-- 查询
SELECT id, name FROM employee ORDER BY id LIMIT 1000000, 10;
优点:
-
利用索引加速排序和查询。 -
简单易实现。
缺点:
-
对于非常大的偏移量,仍然可能性能不佳。
方案四:利用延迟关联或子查询优化
先快速定位需要获取的 id 段,然后再进行关联查询。
示例:
-- 先快速定位需要获取的 id 段
SELECT id FROM employee ORDER BY id LIMIT 1000000, 10;
-- 再关联查询详细信息
SELECT a.id, a.name
FROM employee a
JOIN (SELECT id FROM employee ORDER BY id LIMIT 1000000, 10) b
ON a.id = b.id;
优点:
-
通过子查询快速定位 id 段,减少了主查询的扫描范围。 -
提高了查询效率。
缺点:
-
实现稍微复杂一些,需要两次查询。
聚簇索引与非聚簇索引的区别?
-
聚簇索引:数据和索引放在一块。 -
非聚簇索引:数据和索引分开存储,索引结构的叶子节点指向数据的对应行。
聚簇索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一 个聚簇索引,因为一个表的物理顺序只有一种情况,所以,对应的聚簇索引只能有一个。聚簇索引的叶子节点就是数据节点,既存储索引值,又在叶子节点存储行数据。Innodb 创建表后生成的文件有:
-
.idb:表里面的数据+索引文件。 innodb_file_per_table = ON
-
ibdata1:共享表空间文件,存储所有 InnoDB 表的数据和索引。 innodb_file_per_table = OFF
在 MySQL 8.0 之前的版本中,每个表都会生成一个
.frm
文件,用于存储表的定义。从 MySQL 8.0 开始,表定义文件存储在系统表空间中,不再生成.frm
文件。
非聚集索引(MyISAM 引擎的底层实现)的逻辑顺序与磁盘上行的物理存储顺序不同。非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针。索引命中后,需要回表查询。MyISAM 创建表后生成的文件有:
-
.frm
文件:存储表的结构定义。 -
.MYD
文件:存储表的实际数据记录。 -
.MYI
文件:存储表的索引信息。
MySQL 是如何保证数据不丢失的?
只要redolog 和 binlog 保证持久化磁盘就能确保MySQL异常重启后回复数据。
在恢复数据时,redolog 状态为 commit 则说明 binlog 也成功,直接恢复数据;如果 redolog 是 prepare,则需要查询对应的 binlog事务是否成功,决定是回滚还是执行。
算法
如何实现一个lru算法
实现LRU(Least Recently Used,最近最少使用)该算法是一种常用的缓存淘汰策略,给出思路即可。
它的核心思想是:当缓存满时,优先淘汰最近最少使用的数据项。
数据结构选择:
-
双向链表:用于维护数据项的使用顺序。最近使用的数据项放在链表的头部,最久未使用的数据项放在链表的尾部。 -
哈希表:用于快速查找数据项。键是数据项的标识,值是数据项在双向链表中的节点。
操作步骤:
-
获取数据:如果数据项存在于缓存中,将其移到链表头部,并返回数据项。 -
添加数据项:如果数据项不存在于缓存中,将其添加到链表头部。如果缓存已满,先删除链表尾部的数据项,再添加新数据项。 -
删除数据:删除链表尾部的数据项。
以上,点亮【赞
与在看
】让我们心中充满力量、披荆斩棘、不惧未来!
【推荐阅读】
面试官常拷打:如何下保证MySQL数据库与Redis缓存数据一致性?
关于作者:一位热爱技术,并在职场与自媒体间探索的实践者,希望通过分享个人经验和见解,帮助更多人实现自我成长和价值。
原文始发于微信公众号(皇子谈技术):被深信服开了劝退价…