2026-10-23 07:51:49
此次用友友新星Java二面主要围绕个人情况、技术知识以及项目实践展开。既考察了基础的技术概念,如缓存相关问题,也深入探讨了实际项目中遇到的问题及解决方案,如秒杀系统中库存超卖、数据库和Redis一致性等,最后通过手撕算法题和反问环节进一步了解面试者。
各问题解答要点内容规划:简要介绍个人基本信息,如姓名、学校、专业。重点突出与Java开发相关的课程学习成果,如数据结构、算法、数据库等课程的优异成绩。列举参与过的Java相关项目,说明在项目中承担的角色和取得的成果,例如完成了某个模块的开发,提升了系统性能等。提及获得的与Java开发相关的奖项或证书,如Java编程竞赛奖项、Oracle认证等。
表达技巧:语言简洁明了,逻辑清晰,突出重点,展示自己的优势和特点,让面试官快速了解自己的能力和潜力。
诚实作答:根据自身实际情况,明确告知能够实习的具体时间段,如从几月几日到几月几日。
表达意愿:如果希望长期实习或转正,可适当表达自己愿意长期投入工作,为公司创造价值的意愿。
缓存击穿
定义:指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
解决方案:设置热点数据永不过期;加互斥锁,当缓存失效时,不是立即去加载数据库,而是先获取互斥锁,只有获取到锁的线程才能去加载数据库并更新缓存,其他线程等待一段时间后重试。
缓存穿透
定义:指查询一个一定不存在的数据,由于缓存和数据库中都没有该数据,导致每次请求都直接打到数据库上,给数据库造成很大的压力。
解决方案:布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被bitmap拦截掉,从而避免了对数据库的查询压力;对于不存在的数据,也在缓存中设置一个默认值,当查询该数据时,直接返回默认值,不过这种方式需要设置合理的过期时间。
缓存雪崩
定义:指缓存中大量的数据在同一时间过期,或者缓存服务器宕机,导致大量的请求直接打到数据库上,使数据库瞬间承受巨大的压力,甚至导致数据库崩溃。
解决方案:均匀设置过期时间,避免大量数据同时过期;采用多级缓存,如本地缓存和分布式缓存结合,当分布式缓存出现问题时,可以使用本地缓存应急;搭建缓存集群,提高缓存的可用性。
高性能:Redis是基于内存的数据库,数据存储在内存中,读写速度非常快,能够满足秒杀系统高并发的读写需求。
数据结构丰富:提供了多种数据结构,如字符串、列表、集合、有序集合等,可以根据不同的业务场景选择合适的数据结构,例如使用有序集合可以实现商品的排序和限量抢购功能。
原子性操作:支持原子性操作,能够保证在多线程环境下数据的一致性,避免出现库存超卖等问题。
分布式锁:可以实现分布式锁,解决分布式系统中的并发控制问题,确保秒杀活动的公平性。
保证原子性:Lua脚本在Redis中执行时是原子性的,可以将多个操作作为一个整体执行,避免了在多线程环境下出现数据不一致的问题。例如,在秒杀场景中,可以将判断库存、扣减库存、创建订单等操作放在一个Lua脚本中执行,确保这些操作的原子性。
减少网络开销:将多个操作封装在一个Lua脚本中,只需要一次网络请求就可以完成多个操作,减少了网络开销,提高了系统的性能。
灵活性:可以根据不同的业务需求编写不同的Lua脚本,实现复杂的业务逻辑,提高了系统的灵活性。
乐观锁原理:乐观锁认为在一般情况下,数据不会产生冲突,所以在访问数据时不会加锁,而是在更新数据时通过比较版本号或时间戳等方式来判断数据是否被其他线程修改过。如果数据没有被修改过,则进行更新操作;如果数据已经被修改过,则放弃更新操作或进行重试。
实现方式:在数据库表中增加一个版本号字段,每次更新数据时,将版本号加1。在更新数据时,先读取数据的版本号,然后在更新语句中添加版本号的判断条件,只有当版本号与读取时的版本号一致时才进行更新。例如,在MySQL中可以使用如下语句实现乐观锁:UPDATE product SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 1。
原方案:先修改数据库,再删除Redis缓存。这种方案在大多数情况下可以保证数据的一致性,但在高并发场景下可能会出现问题。例如,在修改数据库后,删除Redis缓存之前,有其他线程查询数据,此时会从数据库中读取旧数据并更新到Redis中,导致Redis中的数据与数据库不一致。
改进方案(消息队列异步执行):使用消息队列异步执行Redis修改库存和创建订单的操作。当修改数据库后,将操作消息发送到消息队列中,由消费者从消息队列中获取消息并执行Redis操作。这样可以保证数据库操作和Redis操作的顺序性,避免出现数据不一致的问题。
算法思路:可以使用双指针法,创建一个新的数组,然后分别从两个有序数组的开头开始比较元素的大小,将较小的元素放入新数组中,直到其中一个数组的元素全部放入新数组中,再将另一个数组中剩余的元素依次放入新数组中。
代码实现(Java示例)
提升自己:可以询问面试官对于提升Java开发能力、解决实际项目问题的建议,如学习哪些技术框架、阅读哪些书籍、参与哪些开源项目等。
业务和技术栈:了解公司的ToB金融业务具体是做什么的,主流的Java技术栈有哪些,以便更好地了解公司的业务需求和技术方向,为后续的学习和工作做好准备。