曹操出行 后端开发实习 一面:ConcurrentHashMap 分片与充值幂等
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 封号 skill 的误封处理是怎么做的?
- 用 ConcurrentHashMap 拆分线程池的具体实现是怎样的?在 ConcurrentHashMap 里存了什么、切片切的是什么、这样做有什么用?高并发下会出现什么问题,为什么?
- ConcurrentHashMap 比 HashMap 有哪些优点,解决了什么问题,底层是怎么实现的?
- 充值模块的高并发怎么解决幂等?考虑过用分布式锁解决吗?
- Redis 的两种持久化分别有什么特点?
- 项目里哪些数据存了向量、哪些存了别的?分别存什么、为什么?
- MCP 和 function call 有什么区别?
- 记忆提取的流程讲一下(要和 RAG 流程区分开),长期记忆是怎么提取的?
《参考解析》
ConcurrentHashMap 分片线程池的出发点是降低锁竞争,坑在分片本身。 JDK 8 的 ConcurrentHashMap 是数组加链表/红黑树,写操作先对桶头节点做 CAS,冲突时用 synchronized 锁桶头,扩容支持多线程协助迁移(sizeCtl 协调),size 用 baseCount 加 CounterCell 分散计数。拿它当「分片容器」存放多个线程池或任务队列,本质是用 key 的哈希选桶做静态分片:好处是不用自己写分片锁、并发读写天然安全;问题是分片数固定、key 分布不均会产生热点,扩容 rehash 会让「哪个 key 落在哪个分片」的映射变化、分片与线程的绑定失效,也没法按负载动态调整,还会把线程池对象和业务数据混在一张表里、生命周期难以管理。更稳的做法是显式分片数组加取模、一致性哈希,或直接用成熟的分片执行框架,把「分片规则」和「存储结构」解耦。
幂等的落点是「唯一约束加状态机」,分布式锁只是手段之一。 充值这类写操作要先用业务幂等键(用户 ID 加订单号或请求号)在数据库建唯一索引,重复请求直接命中唯一键冲突并按已处理返回;再配合状态机(待支付、处理中、成功或失败)让重复回调只能推进一次。分布式锁(Redis 的 SET NX PX 加唯一 value,释放时用 Lua 校验持有者)能挡住并发重复执行,但它只保证互斥、不保证「执行过就不再执行」,锁超时、续租失败、主从切换都可能让两个请求同时进入临界区,所以锁必须和唯一索引或幂等表一起用。工程上还要区分接口幂等与消息幂等(消费端去重表),并考虑失败补偿与对账,否则漏掉的请求没人发现。
Redis 两种持久化:RDB 是快照,AOF 是命令日志。 RDB 按时间或变更量 fork 子进程写全量二进制快照,文件小、恢复快,适合备份与冷启动,但两次快照之间的数据会丢,fork 在大内存实例上会造成明显的延迟抖动。AOF 追加写命令,appendfsync 有 always、everysec、no 三档,在安全与性能之间取舍,everysec 最多丢约一秒;文件会持续膨胀,靠 AOF rewrite 压缩(同样是 fork)。生产上常见两者都开:RDB 做备份与快速恢复,AOF 缩小丢失窗口;4.0 之后还可以用混合持久化,把 RDB 头加增量 AOF 放在同一个文件里。回答时补一句主从与哨兵切换下持久化对数据丢失的影响,能显出深度。
MCP 与 function call 不在一个层级:前者是协议,后者是模型的一种输出能力。 function call(tool use)是模型按 schema 输出「要调用哪个工具、参数是什么」,由宿主应用执行后把结果回灌;工具怎么定义、怎么发现、怎么接入,全靠各家用例自己约定。MCP(Model Context Protocol)是客户端与服务端之间的标准化连接协议,用 JSON-RPC 暴露 tools、resources、prompts,一次接入可被多个宿主复用,还带能力协商、传输方式(stdio、HTTP 加 SSE 或 streamable)与生命周期管理。可以说 MCP 把「工具的供给」从写死在应用里变成可插拔的服务,而 function call 仍然是模型侧触发调用的机制。
记忆提取和 RAG 的区别在「写什么」,不在「取什么」。 RAG 是查询时检索外部文档拼进上下文,知识本身不变;长期记忆要解决的是从对话里判断哪些信息值得持久化——事实(身份、长期偏好)、情景(做过什么、结论如何)、以及时效性。流程一般是:对话切分、候选抽取(规则或小模型)、重要性与稳定性打分、去重与冲突合并(同一字段新值覆盖旧值,保留来源与时间戳)、写入结构化存储(用户档案表加向量库)、检索时按当前 query 召回相关记忆并与 RAG 结果分区注入。难点是误记(把模型猜测写成事实)、过期不失效、以及用户要能查看和删除。至于「哪些存向量、哪些存原文」:需要语义召回的经验片段进向量库,结构化字段和原文证据进关系库,向量里只存 id 和 embedding,避免同一份内容两处维护。