快手 Java开发 二面

#JAVA##JAVA面经##JAVA内推#

1. 分段锁设计,扩容时如何避免并发冲突?

思路

核心是“分段加锁+扩容时单段独立处理”,结合实际项目中应对并发的经验,用通俗的语言说明分段锁逻辑和扩容时的并发控制,不涉及代码,突出实操性。

回答示例

面试官您好,我们项目中用分段锁解决高并发下的扩容冲突问题,核心思路很简单,就是把整个存储容器分成多个独立的段,每个段单独加一把锁,互不影响。扩容的时候,我们不会对整个容器全局扩容,而是只针对单个段进行操作。

具体来说,当某个段的数据量达到扩容阈值时,我们只对这个段加锁,然后进行数据迁移、容量翻倍的操作,其他段正常处理读写请求,完全不受影响。而且扩容时会做双重校验,防止多个线程同时对同一个段进行扩容,迁移数据的时候全程加锁,避免数据丢失或重复,最后瞬间切换数据引用,完成扩容,整个过程不会出现并发冲突,也不会影响整体服务性能。

2. 秒杀库存扣减:Redis+Lua脚本实现原子性,解释脚本逻辑

思路

先说明秒杀库存扣减的核心痛点,再结合实际项目中Redis+Lua的应用场景,通俗解释原子性优势和脚本核心逻辑,不写脚本代码,突出落地性。

回答示例

面试官您好,秒杀场景最核心的问题就是并发超卖,比如多个用户同时抢购最后一件商品,很容易出现多个人都扣减成功的情况,本质就是“查库存+扣库存”这两个操作不是原子的,中间可能被其他请求打断。

我们项目中解决这个问题,就是用Redis配合Lua脚本来实现的,核心原因就是Lua脚本在Redis中是原子执行的,一旦开始执行,就会阻塞其他所有命令,直到脚本执行完成,从根本上避免了并发打断的问题。

脚本的逻辑很简单,没有复杂语法,就是先获取当前的库存数量,判断库存是否充足,如果库存为空或者不够扣减,就直接返回失败,不让用户抢购;如果库存充足,就执行库存扣减操作,最后返回成功,前端再根据返回结果提示用户是否抢购成功。实际落地时,我们还会配合Redis的AOF和RDB持久化,防止Redis宕机导致库存丢失,确保数据可靠,而且脚本很轻量,执行速度快,能支撑高并发的秒杀场景。

3. 手写LRU缓存(O(1)时间复杂度),用LinkedHashMap实现

思路

结合实际开发中使用LinkedHashMap实现LRU缓存的经验,通俗说明核心逻辑、O(1)时间复杂度的原因,不写代码,突出实用性和易懂性。

回答示例

面试官您好,LRU缓存就是“最近最少使用”的缓存策略,核心要求就是查询和存入操作的时间复杂度都是O(1),我们项目中没有手动去实现哈希表和双向链表,而是直接复用了Java自带的LinkedHashMap,开发效率高,也能满足需求。

LinkedHashMap底层其实就是“哈希表+双向链表”的组合,这也是它能实现O(1)时间复杂度的关键。哈希表的作用是快速定位数据,不管是查询还是存入,都能通过key的哈希值直接找到对应位置,时间复杂度是O(1);双向链表的作用是维护数据的访问顺序,最近使用的数据会被移到链表尾部,最久未使用的在链表头部,当缓存容量满了,就直接移除头部的数据,这个移动和移除操作也不用遍历,直接操作链表首尾,也是O(1)。

我们实际使用时,只需要重写LinkedHashMap的一个方法,用来判断缓存是否达到容量上限,达到上限就自动移除最久未使用的元素,不用额外写复杂逻辑,简单又实用,完全能满足项目中对LRU缓存的需求。

4. 分布式事务TCC模式,如何处理空回滚问题?(补偿事务设计)

思路

先解释TCC三个阶段和空回滚的实际场景,再结合项目中真实的解决方案,说明如何通过事务状态记录和补偿幂等性处理空回滚,不涉及代码,突出落地经验。

回答示例

面试官您好,首先我先简单说下TCC模式,它主要分三个阶段:Try阶段是预留资源,Confirm阶段是确认提交、真正执行业务,Cancel阶段是补偿回滚、释放预留的资源。空回滚就是指,Cancel阶段被触发了,但对应的Try阶段其实没执行成功,甚至根本没执行,这时候执行Cancel,就会出现空操作,甚至可能导致数据异常。

我们项目中处理空回滚,主要用了两个方案,结合起来双重保障,落地性很强。第一个是核心方案,就是建一个事务状态表,每个分布式事务都有一个唯一的事务ID,在Try阶段执行前,先往表里插一条记录,状态设为“正在执行Try”;如果Try执行成功,就把状态改成“Try成功”;如果Try失败,比如库存不足、接口调用失败,就改成“Try失败”。当Cancel阶段触发时,先查这个状态表,如果状态不是“Try成功”,就直接返回成功,不用执行回滚操作,避免空回滚。

第二个是兜底方案,保证Cancel方法的幂等性。就算出现了空回滚,也不会影响数据,比如Cancel阶段是释放预留的库存,我们会先判断这个事务ID对应的库存是否被预留过,存在就释放,不存在就直接返回成功,避免重复回滚导致库存多增等异常。比如下单扣库存的场景,Try阶段失败了,状态改成“Try失败”,后续Cancel触发时,查状态表就不会执行回滚,既避免了空回滚,也保证了数据安全。

5. Nacos自定义负载均衡策略:基于权重的轮询算法

思路

结合项目中Nacos的实际使用经验,说明自定义权重轮询策略的原因、核心逻辑和配置方式,不写代码,突出实操性和业务适配性。

回答示例

面试官您好,Nacos本身自带负载均衡功能,但默认是简单轮询,也就是每个服务节点被选中的概率是一样的。但我们项目中,服务节点的性能有差异,比如有的节点配置高、性能好,有的节点配置一般,简单轮询会导致性能差的节点被压垮,所以我们自定义了基于权重的轮询策略。

核心逻辑很简单,就是根据服务节点的性能,在Nacos控制台给每个节点配置不同的权重,性能好的节点权重设高一点,比如5,性能一般的设1,性能差的设0.5,自定义策略会读取每个节点的权重,然后按权重分配请求,权重越高的节点,被选中的概率越大,这样就能让性能好的节点承担更多流量,避免性能差的节点过载,提升整体服务的稳定性和响应速度。

实现的时候,我们是通过实现Nacos兼容的负载均衡接口,复用了Nacos自带的权重轮询工具类,不用自己写复杂的权重计算逻辑,开发起来很简单,配置好权重后,策略会自动生效,后续如果节点性能有变化,直接在Nacos控制台修改权重就行,不用改代码、不用重启服务,很方便。

6. 高并发ID生成器:改进雪花算法(增加数据中心ID,每秒10万+)

思路

结合高并发场景下ID生成的实际需求,说明原生雪花算法的不足、改进思路,以及如何实现高并发、唯一ID生成,不写代码,突出实用性和性能保障。

回答示例

面试官您好,我们项目是高并发场景,对ID生成的要求是唯一、高效,每秒能生成10万+ID,原生雪花算法虽然能生成唯一ID,但它没有数据中心ID,在多数据中心部署的时候,比如不同地域的服务节点,很容易出现ID重复的问题,所以我们对雪花算法做了改进。

改进的核心就是增加了数据中心ID字段,同时调整了各字段的位数,保证整体是64位ID。具体来说,64位ID里,1位是符号位,固定为0,保证ID是正数;31位是毫秒级时间戳,能使用约69年,足够我们项目使用;5位是数据中心ID,支持32个数据中心,满足多地域部署需求;5位是机器ID,每个数据中心支持32台

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

本专栏在精不在多,内容分为八股文、大厂真实面经,面试通过后将offer和面试题私发给我,可退还专栏的收益部分费用。欢迎大家共建专栏

全部评论
27届拼多多实习机会或看我主页 https://careers.pddglobalhr.com/campus/intern?t=4OmKPVeX9a
点赞 回复 分享
发布于 03-23 17:46 上海
拼多多招27届实习生啦 https://careers.pddglobalhr.com/campus/intern/detail?t=dRvUVvcTiA
点赞 回复 分享
发布于 03-23 10:55 上海

相关推荐

04-22 23:15
中南大学 Java
时间:4.21公司地点:北京时长:52min做了一些简化,提取了更有借鉴价值的部分1. 部门介绍2. 自我介绍3. 这两个项目你觉得哪个项目复杂度高一些,可以多聊一会4. 你这个项目主要是想解决什么样的问题呢5. 你刚才说这些方法的话,应该说也是社区内或者说比较常见的一些处理方式了,对吧?我都或多或少都能get到,但是我有一个问题,就是**你做这件事情之前,就是每一个技术的应用之前,你有没有去验证这个技术确实提升了准确率呢**6. 我们的这些处理方式是否真正的真的提升了它的准确率。就是我们只是堆砌技术,还是说我们确确实实是提升了这件事情?7. 你这一知识库当时怎么选的?你做的知识库是什么类型的知识库?8. 那你这知识库里面涉及了哪些方面的内容?比如说文学类的,还是什么科技类的?是什么航天类的等等?有没有就说具体一些?9. 那你这个博客内容写的多吗?10. 那如果14篇文章的话,而且你这14篇文章看起来所涉及的范围是比较发散的,那么在这种情况下,其实这个rag的检索本身就不容易出现,刚才说的那个检索有问题的情况,这可能本身就不是个问题。11. 在我们去真正去做一件事情这的处理的时候,其实我们还是应该先去有一个度量的标准,不然我们优化可能是负优化,我们都不知道对吧?就是说我现在要做 rag 检索。我要去先做一个度量的方式,然后去验证它的准确率,你应该怎么做?12. 你怎么判断问题回答是准确的13.那么我怎么看到线上的这些回答的准确率呢?14. 有必要搞多级缓存吗15. 好,那首先就多级缓存来说,你觉得,它有什么弊端,还有它有什么优势?这个讲一下。16. 我有一个问题,首先其实我们一般认为 redis 它的那个吞吐是非常高的,而且如果说我们比如说数量很大,Redis 它也是支持那个多节点对吧,比如说…… 不管是哪种方案吧,Redis 也可以支持多节点的这种部署,所以在这种情况下的话,我们认为 redis 从网络压力这一块是没有太大问题的。那么在你看来,有了 redis 的话,我们还要去引入本地缓存的主要目的是什么?因为刚才你说的只是为了减少网络开销。 但是现在我们实际的生产环境中 redis 的是网络开销 其实是没什么太大的问题的。你应该明白我的意思,就是 redis 网络开销不是它的核心问题,就是我为什么非要引入本地缓存17. 我看后面你还自己写过两个 SKILL 对不对?能具体展展开一个就是你可能平时,有没有平时用的比较多的,我想知道不是那种为了写而写的那种,就真正能解释你生活中问题的那种 SKILL18. 你最近面试多吗?19. 那八九场的话,就是你觉得你做的自己就是面试,就是相当于面试自己的这种 Skill 和你真正去面试中拿到的面试题,它相似度高吗?>我是我当时了解到,主要是主要是因为网络开销的问题,进一步提升响应速度20. 说实话 redis 并不存在很大量的网络开销问题  对不对?21. 我看后面你还自己写过两个 SKILL 对不对?能具体展展开一个就是你可能平时,有没有平时用的比较多的,我想知道不是那种为了写而写的那种,就真正能解释你生活中问题的那种 SKILL22. 你最近面试多吗?23. 那八九场的话,就是你觉得你做的自己就是面试,就是相当于面试自己的这种Skill和你真正去面试中拿到的面试题,它相似度高吗24. OK, 那你觉得你这个 SKILL 有没有帮你解决到一些实际面试中的问题,有没有确实命中的一些面试中的一些真正的面试题25. 对你来说,现在比如经验完经历完这场面试之后,你觉得你的 SKILL 应该如何提升呢?26. 你理解什么叫 CAS?27. 那它和悲观锁有什么不同?那首先第一个问题就是纯靠CAS就能解释就能实现这个乐观锁吗?28. 解释一下volatile的这个关键字的目的和作用29. 既然我们提到了CAS操作就一定能保证。并发更新的安全性了。那么我们为什么还要用 volatile 去修饰这个变量呢?这不多此一举吗?30. 好,那继续问 CAS 里面会有什么问题?就它会有什么其他的什么问题呢?31. 你了解 CAS 的 ABA 问题吗?32. 讲一下怎么解决就可以了33. 那现在回到这儿来说就是有乐观锁和悲观锁两种锁,对不对。那么,我什么时候要选择乐观锁?什么时候要选择悲观锁?你看,我们知道 JDK 里面 synchronized 的关键字是悲观锁,对吧?而 ReentrantLock 是个就是这种我们一般认为是 CAS+volatile 这种乐观锁的方式那么这两种方式的话,你觉得我们在应用中。什么情况下会采用乐观锁?什么时候要采用悲观锁?34. 为什么?35. 这个我知道好,那现在问一个问题 就是说,既然高并发情况下用悲观锁就很好,那我无脑用悲观锁不就完了吗?就是既然说。乐观锁有就是说并发高了,它就不行,自选浪费 CPU,对吧?那我无脑用悲观锁,不就 OK 了吗?不挺好的吗?36.那还有一个问题,就是我们刚才说的这些并发的处理的方式。都是基于一个理念叫共享内存,对吧,相当于都是无论是悲观锁还是乐观锁,我们都相当于是要在对象上加锁,然后限制一些线程的进入和退出,对不对。 那么有没有别的方式照样可以实现并发更新的?并发更新的这样的一个方式,就除了共享内存方式,还有没有别的。比如说或者说我这么说吧,就全世界上处理同一个数据的多线程更新的这个问题,只有乐观锁和悲观锁两种方式吗?是非阻塞不能处理吗?因为不管是哪个锁,其实都会进入到一个阻塞的状态,对吧?必须是通过阻塞的方式才能搞实现多线程对同一变量的更新吗?37. 手撕环节:[电话号码的字母组合](*******************************************************************)48. 反问环节
查看30道真题和解析
点赞 评论 收藏
分享
评论
1
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务