(给哪吒编程加星标,提高Java技能)
大家好,我是哪吒。
昨天下午看到腾讯也开展24 届春招/25 届春招实习了,而且目标暑期实习是招 5000 个,这数量有点多啊,难道回暖了一点?互联网又可以了?
这一两周基本互联网大厂公司都已经全面进展到春招环节了,24 届/25 届的同学,要好好准备一下,早点投起来,早点投卷的人少,机会大一些。
不要准备到 90-100 分才敢面试,实际上 70 分左右就可以去投递面试了,边面试边查漏补缺的效率是最快的。
这次跟大家分享一位同学腾讯校招的 Java 后端面经,考察的内容不算特别多,但是都是比较基础的问题,有一些都是经典的问题,值得大家再次复习一波。
考察的知识内,我帮大家罗列了一下:
网络:HTTPS、对称加密与非对称加密
Java:hashmap、volatile、synchronized、乐观锁、springboot
MySQL&Redis:缓冲雪崩、缓冲一致性、索引失效
对称加密只使用一个密钥进行加解密,优点是运算速度快,缺点是密钥必须保密,无法做到安全的密钥交换。
非对称加密使用两个密钥:公钥和私钥。公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。公钥和私钥都可以用来加密和解密,流程的不同,意味着目的也不相同:
一般我们不会用非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能的。
对称加密算法包括:
非对称加密算法包括:

HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式:
SSL/TLS 协议基本流程:
前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。
基于 RSA 算法的 TLS 握手过程比较容易理解,所以这里先用这个给大家展示 TLS 握手过程,如下图:

TLS 协议建立的详细流程:
1. ClientHello
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。
在这一步,客户端主要向服务器发送以下信息:
(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。
(3)客户端支持的密码套件列表,如 RSA 加密算法。
2. SeverHello
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:
(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。
(3)确认的密码套件列表,如 RSA 加密算法。
(4)服务器的数字证书。
3.客户端回应
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
(1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
4. 服务器的最后回应
服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
如下图图所示,为数字证书签发和验证流程:

CA 签发证书的过程,如上图左边部分:
客户端校验服务端的数字证书的过程,如上图右边部分:
但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级:

对于这种三级层级关系的证书的验证过程如下:
在这四个步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,而 “GlobalSign Organization Validation CA - SHA256 - G2” 证书又信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。
总括来说,由于用户信任 GlobalSign,所以由 GlobalSign 所担保的 baidu.com 可以被信任,另外由于用户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任。

操作系统里一般都会内置一些根证书,比如我的 MAC 电脑里内置的根证书有这么多:

这样的一层层地验证就构成了一条信任链路,整个证书信任链验证流程如下图所示:

可以采取以下措施:
使用HTTPS下载:确保下载链接使用HTTPS协议,通过SSL加密传输数据,防止中间人攻击和数据篡改。
验证文件完整性:在下载文件后,使用哈希算法(如MD5、SHA-256)计算文件的哈希值,与官方提供的哈希值进行比对,验证文件是否被篡改。

HashMap的put()方法用于向HashMap中添加键值对。当调用HashMap的put()方法时,会按照以下详细流程执行:
第一步:根据要添加的键的哈希码计算在数组中的位置(索引)。
第二步:检查该位置是否为空(即没有键值对存在)
第三步:如果该位置已经存在其他键值对,检查该位置的第一个键值对的哈希码和键是否与要添加的键值对相同?
第四步:如果第一个键值对的哈希码和键不相同,则需要遍历链表或红黑树来查找是否有相同的键:
如果键值对集合是链表结构:
如果键值对集合是红黑树结构:
第五步:检查链表长度是否达到阈值(默认为8):
第六步:检查负载因子是否超过阈值(默认为0.75):
第七步:扩容操作:
第八步:完成添加操作。
需要注意的是,HashMap中的键和值都可以为null。
此外,HashMap是非线程安全的,如果在多线程环境下使用,需要采取额外的同步措施或使用线程安全的ConcurrentHashMap。
作用:
区别:
观锁通过在更新数据时先检查数据版本号(或时间戳等),若版本号匹配则更新数据,否则拒绝更新,实现并发控制。实现方式包括:
乐观锁的缺点是在高并发场景下,冲突较多,可能导致大量的重试操作。所以,在数据竞争非常激烈的环境中,乐观锁可能不太适用。
Spring Boot 在启动的时候会干这几件事情:
总结一下,其实就是 Spring Boot 在启动的时候,按照约定去读取 Spring Boot Starter 的配置信息,再根据配置信息对资源进行初始化,并注入到 Spring 容器中。这样 Spring Boot 启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应 Bean 资源即可。
通常我们为了保证缓存中的数据与数据库中的数据一致性,会给 Redis 里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。

那么,当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。

可以看到,发生缓存雪崩有两个原因:
数据库缓存一致的5个方案:
方案一
通过redis的过期时间来更新缓存,mysql 数据库更新不会触发redis 更新,只有当redis的key过期后才会重新加载
这种方案的缺点:
数据不一致的时间较长,会造成一定的脏数据
完全依赖过去时间,过期时间太短缓存更新太频繁,过长容易有太长时间更新延迟。
方案二
在方案一的基础上扩展,让key 的过期时间做兜底,在更新mysql 的同时也会更新redis.
这种方案的缺点:如果更新mysql 成功,更新redis 失败,又会成为方案一。
方案三
在方案二的基础上,对redis 的更新操作进行优化,增加消息队列,转为异步更新redis 数据, 将redis 的更新操作交给MQ ,队列来保证可靠性,异步更新redis。
这种方案的缺点:
解决不了时序的问题,如果有多个业务实例对同一条数据进行更新,数据更新的先后顺序可能会乱。
引入mq ,增加的系统复杂性,增加mq的维护成本。
方案四
将mysql和redis更新放在一个事务中操作,这样能保证达到强一致性。
这种方案的缺点:
mysql或者redis任何一个环节出问题,都会造成数据回滚或者撤销。
如果网络出现超时,不仅可能会造成数据回滚或者撤销,还会有并发问题。
方案五:
通过订阅mysql的Binlog 日志来更新redis, 把我们搭建的mq消费服务,作为mysql的一个salve ,订阅Binlog ,解析出更新的内容,再更新redis
这种方案的缺点:要单独搭建一个同步服务,并且来引入BinLog同步机制,成本较大。
like %xx 或者 like %xx%这两种方式都会造成索引失效;
增加索引 + 异步 + 不落地后,从 12h 优化到 15 min
·················END·················
看完本文有收获?请转发分享给更多人
关注「哪吒编程」,提升Java技能
点赞和在看就是最大的支持❤️