AI全栈开发一面面经加总结
对于多线程场景下使用HashMap怎么保证线程安全?
可以使用 ConcurrentHashMap 和 HashTable,但是HashTable虽然是线程安全的但是是在每个方法上加 synchronized 性能太差了,所以我一般会使用 ConcurrentHashMap. JDK8 的 ConcurrentHashMap是使用 CAS + synchronized 实现的锁颗粒度更下,锁的时候只会锁某个桶
寻伴项目中的ES + Canal 数据同步
为什么会丢数据?
这点没回答上来。 其实数据丢失和RabbitMQ数据丢失的场景差不多

数据丢失可以分为 MySQL同步 binlog 到 canal这一阶段丢失,可能是 Canal 同步到ES 时数据丢失 上游:Canal 因故障、积压、停机时间过长,binlog 过期或Canal重启恢复位点错误 解决办法:延长 binlog 保留时间(MySQL 5.7默认是永久保留,MySQL 8.0默认保留三天)。监控 Canal 延迟,延迟高就警报 中游:Canal/adapter 异常退出,位点未正确持久化 解决办法:重启必须从上次位点恢复。也可以开启位点持久化 下游:ES写入失败(mapping冲突、索引问题等) 解决办法:进行有限重试,每次重试时间间隔越来越长,超过最大重试次数之后就落入补偿表,开启定时任务来补偿失败的消息
幂等怎么实现?
生成全局唯一业务ID,保存到Redis中,每次处理之前先查Redis中是否有该业务ID,有的话就直接丢弃 其实也可以使用去重表+ 状态机
文档切分(RAG项目)
切分的依据是什么?
我是用的是递归切分,按照 章节-> 小节 ->段落->句子. 最后使用句子进行切分的时候就要使用重叠chunk切分了,overlap = chunk_size 的 10% ~ 20% .这样做不能保证语义完全不丢,它做的是尽量少丢
其他的文档切分策略
固定长度切分:按字符/Token 切分。优点是速度快,缺点是语义不完整 语义切分:根据段落、句子边界进行划分。优点是语义完善,缺点是计算速度慢 递归切分:章节-> 小节 ->段落 保留层级干系,检索更准 重叠chunk切分:相邻 chunk 重叠 overlap = chunk_size 的 10% ~ 20%
RAG项目的联网搜索怎么触发的?
当本地检索结果在相似度、数量或语义覆盖度上不足以支撑问题回答时,会触发联网搜索。通常不仅依赖相似度阈值,还会结合文档覆盖度和模型判断来决定是否需要外部检索。
怎么防止检索到的文档出现重复?
在 ETL(数据集成)阶段严格把控,相似度高的文档只能入库一个
你在数据中台项目中怎么保证数据导出不OOM的?
使用 Apache POI 的SXSSF 流失写入,只在内存中维护固定窗口,超过窗口大小后将旧数据放入到临时文件中
临时文件怎么管理?
导出任务化,每个导出任务生成独立文件,提供下载,同时通过定时任务清理过期文件,防止磁盘堆积。 对于 SXSSF 产生的临时文件,导出结束之后需要手动调用 dispose() 手动清理
导出的并发处理
将并发导出做成异步任务。用户发起导出后先创建任务记录,再提交到线程池执行。线程池负责排队,我会设置核心线程数、最大线程数和队列容量;同时再用 Semaphore 限制真正执行导出的任务数,避免数据库和磁盘 IO 被打满。导出过程中采用分页查库和 SXSSF 流式写入,减少内存占用。任务完成后回写文件地址,并定时清理过期文件;如果线程池和队列都满了,就直接拒绝新任务,防止系统雪崩。
ETL怎么做增量同步?怎么保证数据不重复
增量同步一般通过游标实现,比如递增ID或者时间戳字段。
如果只涉及新增可以用ID,但如果有更新操作更适合用更新时间字段。
为了避免数据丢失,通常会做时间窗口回退,并结合幂等写入避免重复。
什么是时间窗口回退?
where update_time > (上次时间 - 5秒)
用户推荐
冷启动问题怎么解决(新用户)
推荐最近活跃度高或者参与度高的用户,根据用户注册时填写的兴趣标签做初步匹配
数据倾斜问题怎么解决
数据倾斜主要是指某些热门标签或者条件导致查询集中在少量数据上,比如热门兴趣标签会命中过多用户。 说白了就是召回的用户过多,可以减小粗找回查找的范围等等
第一分散查询压力,比如通过增加筛选维度(地区、活跃度等)来缩小结果集;
第二是限制召回数量,对高频标签只取 Top N;
第三是打散或加权处理,避免某些标签权重过高导致推荐结果单一
怎么去重?
我的推荐是:单路召回 + 多特征加权排序,对用户之间的距离、兴趣重合度进行打分,一般情况下不会出现重复用户
如果使用多路召回的方式的话,可以这样去重
第一是在召回阶段,尽量避免重复数据进入候选集,比如通过 ES 查询时控制条件组合;
第二是在结果融合阶段,基于用户ID进行去重,比如用 HashSet 或 Map 做过滤。
