一、黑马点评

1.项目介绍

我做的这个项目主要就是模仿大众点评,是使用springboot开发的前后端分离的项目,实现了商户的搜索、点评,好友关注、动态推送以及优惠券秒杀等功能。其中使用了redis做数据缓存,提高数据的访问效率,以及利用redis实现分布式锁解决一人一单的问题。此外,还利用了RabbitMq异步的创建优惠券订单,提高抢票的相应速度。

2.项目中遇到的难点

我认为项目中主要的难点在于优惠券秒杀这个功能的实现。首先这个需要考虑到库存超卖和一人一单问题,针对库存超卖问题,我使用的是乐观锁的思想,考虑到库存如果使用版本号来判断扣减库存是否成功的话,会出现库存充足的情况下依旧会抢券失败的情况。所以我最后没有使用版本号,而是在给扣减库存的sql语句加个where条件判断库存数量是否大于0。然后针对一人一单问题,因为数据库的一人一单判断和库存扣减操作是非原子性的,所以,我的解决方案是我一开始是给用户id放到常量池中,使用synchronized这个关键字获取到用户id的锁后进行相关的抢票校验代码。但后面考虑到在多个相同服务部署的情况下,锁无法跨服务感知,所以使用了redis来创建分布式锁。用的是redisson这个类来创建的锁。

后面考虑到抢券流程会经过查询优惠券、查询库存、查询一人一单、扣减库存、创建订单等多次的数据库操作,导致的响应速度可能比较慢,所以我就用把优惠券的信息缓存到redis中,在redis上进行优惠券的校验流程。校验成功后使用rabbitMq来异步通知订单的创建,加快抢券的响应速度。

3.简历上的技术亮点

(1).基于redis做缓存

原话: 基于Redis做数据缓存,使用布隆过滤器+缓存空值解决缓存穿透问题,通过Sentinel的限流和熔断机制解决缓存雪崩问题、利用互斥锁和逻辑过期解决缓存击穿问题。

提问:为什么要使用redis做缓存?

因为redis是把数据存放在内存中,而mysql数据库则是存放在磁盘中,磁盘的访问速度比内存要慢的多,使用redis可以提高数据的访问效率。在项目中,我主要是对商户信息做了数据缓存,因为考虑到商户信息访问量很多,如果每次都访问数据库获取商户信息,会给数据库带来很大的压力。

提问:怎么解决缓存穿透问题?

缓存穿透指的是请求访问的是数据库中不存在的数据,因为数据库中数据不存在,所以redis中也无法命中,导致每次的这种无效请求都会打到数据库。所以,我使用布隆过滤器和缓存空值这两种方法来解决。在访问redis和数据库时会先经过布隆过滤器的判断来决定是否进行后续的查询。

布隆过滤器底层是一个大型位数组(二进制数组)+多个无偏hash函数。

布隆过滤器针对已有的数据,肯定可以校验通过,而不存在的数据可能会有漏放的风险存在。所以我还使用了缓存空值的方式,若请求的商户id不存在,则会在redis中缓存这个商户id,并赋予空值和一个较短的过期时间,来减轻数据库的压力。

提问:缓存雪崩问题?

缓存雪崩指的是在同一个时间段内,大量的key同时过期,或者说redis突然宕机,导致请求都直接打到数据库的情况。

我项目里是给每个key设置过期时间值时,除了固定的过期时间常量外,再额外添加随机的秒数,保证key的过期时间不会过于接近。

提问:缓存击穿问题?

缓存击穿问题指的是对某一个key的访问量很大,当这个key突然过期,且恢复到缓存的时间比较久的情况下,这段时间内的请求都会打到数据库。

我采用的是互斥锁和逻辑过期的方式来解决的。只使用互斥锁的话,会导致多个请求在获取锁失败的情况下陷入阻塞状态,或者是直接返回请求失败的信息,用户体验上来说并不是很好。所以可以再加个使用逻辑过期的方式,获取到锁时,通过异步的方式刷新缓存。无论有无获取到锁,都是直接返回旧值。所以数据及时性方面还是有欠缺的。基于Token和Redis实现用户认证,利用双拦截器机制(刷新Token拦截器+登录拦截器)实现无感刷新与权限控制,通过ThreadLocal实现用户信息透传,提升接口安全性。

(2)续约token

嗯,我项目中前端调用登录接口后,后端会生成一个随机token令牌,返回给前端,并以token为key,用户信息为value存入redis中并设置过期时间。

用户访问页面时,使用了两个拦截器,一个是刷新token拦截器,一个是登录拦截器。其中刷新token拦截器优先级最高,是全接口拦截且全放行的拦截器,主要作用就是如果请求头有携带token,那么就解析token,要是redis中存在value值,就存入threadlocal中,并把redis的这个key的过期时间进行延长。至于登录拦截器则是专门对必须登录才能访问的接口进行拦截,会判断threadLocal中用户信息是否为null,如果为null就进行拦截。

二、黑马头条

面试话术 - 技术亮点介绍

开场介绍

“我最近完成了一个基于SpringCloud的分布式自媒体新闻平台项目,这个项目采用了微服务架构,包含了自媒体平台、用户平台和后台管理等多个子系统。在技术选型上,我重点考虑了系统的可扩展性、高可用性和性能优化。”

1. 智能搜索系统

在搜索功能这块,用户输入关键词时,系统会先从MongoDB中查询该用户的历史搜索记录,再去查询联想词库,总计最多10条联想词的一个列表返回给前端,从而实现智能联想功能。同时,基于Elasticsearch构建全文搜索引擎,我实现了标题和正文的加权检索,因为标题通常更能体现文章主题,所以给标题设置了更高的权重。此外,通过es支持高亮的特性,给关键词前后加上font标签来实现关键词红色高亮显示,提升用户体验。

面试官追问时补充:

  • “MongoDB主要存储用户搜索历史和联想词,因为它的文档结构更适合这种场景”

  • “ES的加权检索通过multi_match查询实现,标题权重设为2,正文权重为1”

2. 消息架构设计

“为了确保数据一致性,我设计了Kafka双Topic的消息管道架构。业务操作和数据同步分别使用不同的Topic,比如文章上下架操作走业务Topic,而ES数据同步走专门的同步Topic。这样设计的好处是即使某个环节出现问题,也不会影响其他业务,同时通过消息重试机制保证最终一致性。”

面试官追问时补充:

  • “具体实现是通过@KafkaListener监听不同的Topic”

  • “消息幂等性通过业务状态判断来保证”

3. 定时任务调度

“定时任务这块我采用了Redis + XXL-Job的混合架构。Redis负责任务预热和优先级管理,使用一个ZSet存储未来任务,一个List存储当前任务,一个List存储任务完成的id集合来避免任务重复消费。

XXL-Job主要就是负责未来任务预热、当前任务刷新和任务拉取的定时调度。

面试官追问时补充:

  • “Redis的ZSet按执行时间排序,每分钟定时刷新到List中

  • XXL-Job提供了可视化的任务管理界面

4. AI内容审核

“内容审核是新闻平台的核心功能,我集成了Langchain4j构建AI审核模型。系统采用三级审核机制:首先进行敏感词过滤,然后AI智能审核,最后人工复核。AI模型能够准确识别违规内容,返回PASS、MID、FAIL三种状态,大大减少了人工审核的工作量,同时保证了内容安全。”

面试官追问时补充:

  • “AI模型基于OpenAI API,通过SystemMessage定义了审核规则”

  • “敏感词使用Trie树算法实现高效匹配”

5. 实时热点计算

热点文章计算我使用了Kafka Streams进行实时流处理。用户的行为数据(点赞、评论、收藏、浏览)实时发送到Kafka,通过KStream进行聚合处理。我设置了10秒的时间窗口,在这个窗口内对同一篇文章的所有行为数据进行聚合,然后将聚合结果发送到文章处理Topic上,触发文章热度的实时更新。”

面试官追问时补充:

  • 用户行为的消息传递其实就是发送了一个用户行为枚举类(点赞、浏览、评论和收藏)和一个计数值的消息到行为聚合的topic中,由Kstream进行监听并处理。

  • 聚合逻辑是先按文章ID分组,然后累加各种行为数据到该文章对应的聚合对象上的

  • “聚合完成后通过.to()方法发送到HOT_ARTICLE_INCR_HANDLE_TOPIC”

  • 下游有专门的监听器处理聚合结果,更新mysql中的文章行为数据和Redis中的热点文章缓存

6. 性能优化

“在性能优化方面,我实现了多级缓存策略。热点文章数据缓存在Redis中,用户行为数据也进行缓存。同时使用异步处理,通过@Async注解和消息队列,避免阻塞主流程,提升了系统吞吐量。”

总结

通过这套技术架构,系统具备了高并发、高可用的特性。搜索响应时间控制在100ms以内,热点文章更新延迟不超过10秒。这个项目让我深入理解了分布式系统的设计原则和性能优化技巧。

面试技巧

  1. STAR法则:介绍每个技术点时,先说背景(Situation),再说任务(Task),然后说行动(Action),最后说结果(Result)

  2. 准备数据:记住一些关键数据,如响应时间、并发量等

  3. 准备问题:当面试官问”为什么选择这个技术”时,要能说出技术选型的考虑

  4. 承认不足:如果某个技术点不熟悉,可以诚实地说”这个功能我主要负责了架构设计,具体实现细节还需要进一步学习”

记住,面试时语速要适中,表达要清晰,重点突出你的技术思考能力和解决问题的能力!

项目难点

1.实时热点文章的计算

一开始,我想的方案就是,用户的每次行为都直接记录到数据上,然后通过定时任务去获取数据库中所有的文章的行为进行分值计算,然后再更新到redis热点文章的zset中。

但这么做的缺点很明显,就是数据库压力比较大,然后每次定时的计算所有文章的热度值,排好序后取出前10到redis的zset中,以热度值为score,不仅耗时,更多的还是说,有些文章可能这个时间段内没人访问,但还是进行了分值计算。所以这个方案很差。

然后我的解决方案就是,我采用了Kafka的Stream流式分析,把用户的这个行为数据通过kafka发送给Stream进行聚合处理,按文章分组,分别计算点赞数、评论数、浏览数和收藏,然后每隔10秒发送监听器,让监听器把这些数据库当中,然后每更新完一条文章,就去计算当前文章的热度值,然后去redis中进行热度值比对,如果当前文章已经是热点文章,那么更新热度值,否则判断是否大于热点文章的最低热点热度值,如果大于,就进行替换。

这就是我项目中遇到的难点,主要就是实现了减少了数据库访问的压力,以及提高热点文章实时更新的效率。

2.分布式延迟队列的设计

三、自我介绍

面试官您好,我叫李鼎烨,是杭州电子科技大学计算机科学与技术专业的27届本科生,我在校期间主要学习语言是java,像java基础知识,java虚拟机、java并发编程都有过系统的学习,然后像常用的数据库,像Mysql和Redis也都有过系统的学习,然后像java常用的一套框架,spring还有其他的一些中间件,比如消息队列什么的,都有过系统的学习。

然后我简历上的项目网上的一个开源项目,但是有对这个项目做过优化,像生活优选的话,就是