大家好,我是二哥呀。
腾讯也开奖了,星球里也有一些球友拿到了鹅厂的 offer,base 不错,还有股票和签字费,我只能说,这波真的香。
要知道,腾讯今年改革了薪酬体系,把房补加到了月 base 当中,意味着起薪要比去年至少多 4k,等于说每个月到手的工资更多了。
再加上每个月公司缴纳的 12% 全额公积金,香,实在是香,不愧是我心中的第一互联网公司。
我也统计了一波鹅厂开奖的情况,放在了 Java 面试指南中,方便大家作为参考。
后台开发,薪资开了 28k,感觉很有诚意,teg 研发,也就是技术工程事业群,目前的主要业务有混元大模型、腾讯 AI 平台,前者可以说是目前国内举足轻重的部门 客户端开发岗,总包 40 万,加 3 万签字费,硕士 211 学历,部门氛围还可以 后端开发,27k 的 base,15 薪,3 万签字费,硕士 985,如果抛去房补的话,相当于去年的 23k 也就是差不多一个小 sp 的水准 后端岗,总包 40 万,硕士 211,1085(早 10 晚 8,双休),晋升一年申请一次,感觉不利于新人
就我个人的情感而言,互联网offer 选择的第一定律“有鹅选鹅,无鹅延毕”永远都是值得的。
毕竟我特么是微信生态的受益者啊。😄
当然了,鹅厂整体的表现确实不错,微信这一个国民级应用就足够了,况且还有游戏、腾讯云这几块肥肉。
那接下来,我们就以Java 面试指南中收录的腾讯同学 27 云后台技术一面为例来看看,鹅厂的技术要求标准到底是怎么个样子。
1、二哥的 Linux 速查备忘手册.pdf 下载 2、三分恶面渣逆袭在线版:https://javabetter.cn/sidebar/sanfene/nixi.html
腾讯同学 27 云后台技术一面
HTTP的状态码
HTTP 状态码用于表示服务器对请求的处理结果,可以分为 5 种:
1xx 服务器收到请求,需要进一步操作,例如 100 Continue。 2xx 请求成功处理,例如 200 OK。 3xx 重定向:需要进一步操作以完成请求;例如 304 Not Modified 表示资源未修改,客户端可以使用缓存。 4xx 客户端错误:请求有问题,例如 404 Not Found 表示资源不存在。 5xx 服务器错误,例如500 Internal Server Error 表示服务器内部错误。
HTTP是怎么样传输数据的?
HTTP 是基于 TCP/IP 协议的应用层协议,它使用 TCP 作为传输层协议,通过建立 TCP 连接来传输数据。
HTTP 遵循标准的客户端-服务器模型,客户端打开连接发出请求,然后等待服务器返回的响应。
如果我从网上利用多线程来下载一个数据,那是要怎么下载呢?
可以采取分块下载的策略。首先,通过 HEAD 请求获取文件的总大小。然后根据文件大小和线程数,将文件进行切割。每个线程负责下载一个特定范围的数据。
可以通过设置 HTTP 请求头的 Range 字段指定下载的字节区间。例如,Range: bytes=0-1023
表示下载文件的前 1024 字节。
最后启动多线程下载。
如果我只要下载数据的前面十个字节呢?
只需要设置 Range 字段为 Range: bytes=0-9
即可。
connection.setRequestProperty("Range", "bytes=0-9"); // 请求前 10 个字节
HTTP常见的请求头部有些什么呢?【面试官应该是想引导我考虑到Range头,可惜没了解过捏】
Host: www.javabetter.cn
,表示请求的主机名(域名)Accept: text/html
,表示客户端可以接收的媒体类型User-Agent: Mozilla/5.0
,表示客户端的浏览器类型Range:用于指定请求内容的范围,如断点续传时表示请求的字节范围。
HTTP怎么保持长连接呢?
可以通过 Connection: keep-alive 实现。在 HTTP/1.1 中,长连接是默认开启的。
HTTPS是什么?他解决了HTTP什么问题?
HTTPS 是 HTTP 的增强版,在 HTTP 的基础上加入了 SSL/TLS 协议,确保数据在传输过程中是加密的。
HTTP 是明文传输的,存在数据窃听、数据篡改和身份伪造等问题。而 HTTPS 通过引入 SSL/TLS,解决了这些问题。
HTTPS怎么建立连接的?
HTTPS 的连接建立在 SSL/TLS 握手之上,其过程可以分为两个阶段:握手阶段和数据传输阶段。
①、客户端向服务器发起请求
②、服务器接收到请求后,返回自己的数字证书,包含了公钥、颁发机构等信息。
③、客户端收到服务器的证书后,验证证书的合法性,如果合法,会生成一个随机码,然后用服务器的公钥加密这个随机码,发送给服务器。
④、服务器收到会话密钥后,用私钥解密,得到会话密钥。
⑤、客户端和服务器通过会话密码对通信内容进行加密,然后传输。
HTTPS怎么保证建立的信道是安全的?
主要通过 SSL/TLS 协议的多层次安全机制,首先在握手阶段,客户端和服务器使用得是非对称加密,生成的会话密钥只有服务器的私钥才能解密,而私钥只有服务器持有。
在数据传输阶段,即使攻击者拦截了通信数据,没有会话密钥也无法解密。
如何优化慢查询语句?
首先,找到那些比较慢的 SQL,可以通过启用慢查询日志,记录那些超过指定执行时间的查询。
也可以使用 show processlist;
命令查看当前正在执行的 SQL 语句,找出执行时间较长的 SQL。
或者在业务基建中加入对慢 SQL 的监控,常见的方案有字节码插桩、连接池扩展、ORM 框架扩展。
然后,使用 EXPLAIN 查看查询执行计划,判断查询是否使用了索引,是否有全表扫描等。
EXPLAIN SELECT * FROM your_table WHERE conditions;
最后,根据分析结果,通过添加或优化索引、调整查询语句或者增加内存缓冲区来优化 SQL。
介绍下索引?底层是啥?
MySQL 的 InnoDB 存储引擎默认使用 B+ 树来作为索引的数据结构,而 B+ 树的查询效率非常高,时间复杂度为 O(logN)。
索引文件相较于数据库文件,体积小得多,查到索引之后再映射到数据库记录,查询效率就会高很多。
索引就好像书的目录,通过目录去查找对应的章节内容会比一页一页的翻书快很多。
为什么不用二叉树?为什么不用AVL树?
普通二叉树存在退化的情况,如果它退化成链表,就相当于全表扫描。
为什么不用平衡二叉树呢?
虽然 AVL 树是平衡二叉树,但因为只有 2 叉,高度会比较高,磁盘 I/O 次数就会非常多。
而 B+ 树是 N 叉,每一层可以存储更多的节点数据,树的高度就会降低,因此读取磁盘的次数就会下降,查询效率就快。
(联合索引)下面怎么走的索引?
select * from t where a = 2 and b = 2;
select * from t where b = 2 and c = 2;
select * from t where a > 2 and b = 2;
假设 t 表上有一个联合索引 (a, b, c),我们来分析一下:
第一条 SQL 语句包含条件 a = 2 和 b = 2,刚好符合联合索引的前两列。
第二条 SQL 语句由于未使用最左前缀中的 a,可能会触发全表扫描。
第三条 SQL 语句在范围条件 a > 2 之后,索引后会停止匹配,b = 2 的条件需要额外过滤。
事务的隔离级别?这些隔离级别是怎么保证数据的一致性的?默认的事务隔离级别是啥?(MVCC)
事务的隔离级别定了一个事务可能受其他事务影响的程度,MySQL 支持的四种隔离级别分别是:读未提交、读已提交、可重复读和串行化。
读未提交是最低的隔离级别,在这个级别,当前事务可以读取未被其他事务提交的数据,以至于会出现“脏读”、“不可重复读”和“幻读”的问题。
在读已提交级别,当前事务只能读取已经被其他事务提交的数据,可以避免“脏读”现象。
可重复读能够确保在同一事务中多次读取相同记录的结果是一致的,即使其他事务对这条记录进行了修改,也不会影响到当前事务。
可重复读是 MySQL 默认的隔离级别,避免了“脏读”和“不可重复读”,但可能会出现幻读。
串行化是最高的隔离级别,通过强制事务串行执行来避免并发问题,但会导致大量的超时和锁竞争问题。
MySQL 通过 MVCC 来保证一致性,当多个用户同时访问数据时,每个用户都可以看到一个在某一时间点之前的数据库快照,并且能够无阻塞地执行查询和修改操作,而不会相互干扰。
怎么更改事务的隔离级别?
使用 SET SESSION TRANSACTION ISOLATION LEVEL
可以修改当前连接的隔离级别,只影响当前会话。
使用 SET GLOBAL TRANSACTION ISOLATION LEVEL
可以修改全局隔离级别,影响新的连接,但不会改变现有会话。
Hashmap的底层?为什么链表要变成红黑树?为什么不用平衡二叉树?
JDK 8 中 HashMap 的数据结构是数组
+链表
+红黑树
。
链表的查找时间复杂度是 O(n)
,当链表长度较长时,查找性能会下降。红黑树是一种折中的方案,查找、插入、删除的时间复杂度都是 O(log n)
。
平衡二叉树比红黑树的要求更高,每个节点的左右子树的高度最多相差 1,这种高度的平衡保证了极佳的查找效率,但在进行插入和删除操作时,可能需要频繁地进行旋转来维持树的平衡,维护成本更高。
Hashmap能保证并发安全吗?HashTable了解吗?
HashMap 不是线程安全的,因此在早期的 JDK 版本中,是用 Hashtable 来保证线程安全的。
Hashtable 是直接在方法上加 synchronized 关键字,比较粗暴。
ConcurrentHashMap是怎么保证的?
ConcurrentHashMap 在 JDK 7 中使用了分段锁来保证线程安全,在 JDK 8 中使用了CAS(Compare-And-Swap)+ synchronized 关键字。
CAS算法具体内容是啥?他怎么保证数据原子性(这个没答出来)
CAS 是一种乐观锁的实现方式,全称为“比较并交换”(Compare-and-Swap),是一种无锁的原子操作。
在 CAS 中,有这样三个值:
V:要更新的变量(var) E:预期值(expected) N:新值(new)
比较并交换的过程如下:
判断 V 是否等于 E,如果等于,将 V 的值设置为 N;如果不等,说明已经有其它线程更新了 V,于是当前线程放弃更新,什么都不做。
这里的预期值 E 本质上指的是“旧值”。
这个比较和替换的操作是原子的,即不可中断,确保了数据的一致性。
为了保证CAS的原子性,CPU 提供了两种实现方式:
①、总线锁定,通过锁定 CPU 的总线,禁止其他 CPU 或设备访问内存。在进行操作时,CPU 发出一个 LOCK 信号,这会阻止其他处理器对内存地址进行操作,直到当前指令执行完成。
②、缓存锁定,当多个 CPU 操作同一块内存地址时,如果该内存地址已经被缓存到某个 CPU 的缓存中,缓存锁定机制会锁定该缓存行,防止其他 CPU 对这块内存进行修改。
现代CPU基本都支持和使用缓存锁定机制。
GC?怎么样去识别垃圾?
垃圾回收(Garbage Collection,GC)就是对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
通常会通过可达性分析算法来判断对象是否存活。
GC Root?
所谓的 GC Roots,就是一组必须活跃的引用,不是对象,它们是程序运行时的起点,是一切引用链的源头。在 Java 中,GC Roots 包括以下几种:
虚拟机栈中的引用(方法的参数、局部变量等) 本地方法栈中 JNI 的引用 类静态变量 运行时常量池中的常量(String 或 Class 类型)
回收的方法?
垃圾收集算法主要有三种,分别是标记-清除算法、标记-复制算法和标记-整理算法。
标记-清除
算法分为两个阶段:
标记:标记所有需要回收的对象 清除:回收所有被标记的对象
标记-复制
算法可以解决标记-清除算法的内存碎片问题,因为它将内存空间划分为两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后清理掉这一块。
标记-整理
算法是标记-清除复制算法的升级版,它不再划分内存空间,而是将存活的对象向内存的一端移动,然后清理边界以外的内存。
分代收集算法里面具体是怎么回收的?
分代收集
算法是目前主流的垃圾收集算法,它根据对象存活周期的不同将内存划分为几块,一般分为新生代和老年代。
新生代用复制算法,因为大部分对象生命周期短。老年代用标记-整理算法,因为对象存活率较高。
为什么要用分代收集呢?
分代收集算法的核心思想是根据对象的生命周期优化垃圾回收。
新生代的对象生命周期短,使用复制算法可以快速回收。老年代的对象生命周期长,使用标记-整理算法可以减少移动对象的成本。
FULL gc怎么去清理的?(引导之后答了个STW)
Full GC 会从 GC Root 出发,标记所有可达对象。新生代使用复制算法,清空 Eden 区。老年代使用标记-整理算法,回收对象并消除碎片。
停顿时间较长(STW),会影响系统响应性能。
怎么来区分对象是属于哪个代的?
Java 堆被划分为新生代和老年代两个区域。
新生代又被划分为 Eden 空间和两个 Survivor 空间(From 和 To)。
Eden 空间:大多数新创建的对象会被分配到 Eden 空间中。当 Eden 区填满时,会触发一次轻量级的垃圾回收(Minor GC),清除不再使用的对象。 Survivor 空间:每次 Minor GC 后,仍然存活的对象会从 Eden 区或 From 区复制到 To 区。From 和 To 区可以交替使用。
对象在新生代中经历多次 GC 后,如果仍然存活,会被移动到老年代。
Zset的底层实现?
跳跃表(也称跳表)是有序集合 Zset 的底层实现之⼀。在 Redis 7.0 之前,如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 的底层实现,否则会使用跳表;在 Redis 7.0 之后,压缩列表已经废弃,交由 listpack 来替代。
跳表由 zskiplist 和 zskiplistNode 组成,zskiplist ⽤于保存跳表的基本信息(表头、表尾、⻓度、层高等)。
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
zskiplistNode ⽤于表示跳表节点,每个跳表节点的层⾼是不固定的,每个节点都有⼀个指向保存了当前节点的分值和成员对象的指针。
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned int span;
} level[];
} zskiplistNode;
ending
一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 6500 多名球友加入了,如果你也需要一个良好的学习环境,戳链接 🔗 加入我们吧。这是一个 编程学习指南 + Java 项目实战 + LeetCode 刷题 + 简历精修 的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。
两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远。
欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。
最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。