日期:2026-09-21
总题数:3 题
方向覆盖:线程池优化、Vibe Coding、架构设计
场景:支付回调线程池 corePoolSize=50 / maxPoolSize=100 / LinkedBlockingQueue(2000) / CallerRunsPolicy。线上突发下游风控接口 RT 从 50ms 飙到 3s,队列迅速被打满,CallerRuns 反压到 Tomcat worker,导致全站支付接口 RT 全部升高到 5s。
3 个问题:
首先第一个问题,下游一个慢接口拖垮全站支付的核心原因是,它出现异常后,将拒绝策略选择抛售给主线程。这时候由于 Tomcat 自身或者是外部某一个外部容器的自身的线程池被占满,导致对外提供其他能力的接口无法正常返回。所有的外部容器的线程全部被回调任务的线程占领,导致整个线程无法正常使用,所以拖慢主业务流程的线程。 进行三方调用的时候,设置任务请求超时。在线程池的调用远程 HTTP 请求的时候,这块请求基本上设,怎么可能设到 3 秒呢?基本上是 500 毫秒就到头了,500 毫秒就结束。然后如果有问题,然后进行一次重试,顶天是两次重试或者三次重试就到头了,不会说让它一直等待。三次重试没有结果就抛出异常或者记录日志,或者是调降级服务,给出一些默认的结果,然后直接执行任务返回。另外就是回调任务应该是走自己的异步线程线程池,而不是说去占用主线程的线程池。然后它的前置的设置的处理。不应该压到主线程上,因为它本身是一个异步的回调的服务,压到主线程上意义不大。你可以自定义线程池,或者是抛出异常,然后外部进行处理。抛出异常,外部进行处理,比如说抛个异常,然后记录扔到 MQ,后续再异步的扫表,扫 MQ 信息,去把这个所有的回调执行完。 熔断的话,熔断肯定是放在三方调用那,而不是放在整个的回调服务那。熔断之后把它的三方进行降级,降级之后直接返回结果,然后把熔断记录,每个回调记录一下,这些记录是没有处理的,后续异步通过计划任务也好,通过其他的方式也好,把这些任务统一的处理掉,扫表处理掉,最终保证业务的正常运行。 熔断选择指标的话,推荐还是选错误率吧。RT 的话不稳定,因为容易出现波动。RT 这你定多少 RT?超过一个 RT,它突然出现问题了,某一个波动一下,你整个就熔断了吗?这个是很不合理的。当整个服务出现了一定比例的,比如说 10%、15%。这个比例之后,证明这个服务的可用性发生了明显的问题。这时候我们再去进行整个的熔断。但是一般也不会是 15 这个比例,应该会更高一些,20% 左右,20 至 30。
优点:
不足:
反压链路全貌:
用户请求 → Tomcat worker (200) → 支付回调入口
↓
提交到业务线程池 pool:
core=50, max=100, queue=2000, CallerRuns
↓
50 个核心线程都去调风控 API
↓
风控 RT 从 50ms → 3s
↓
50 个线程全部阻塞在 socket.read()
↓
新任务进入 → 走 LinkedBlockingQueue 堆积
↓
2000 个队列位置被打满(不到 10 秒)
↓
扩容到 maxPool=100,100 个线程也阻塞
↓
再进来的任务 → 触发 CallerRunsPolicy
↓
【关键】CallerRuns = 谁提交谁跑
↓
提交方是 Tomcat worker 线程!
↓
worker 线程被强制跑回调任务,阻塞 3 秒
↓
Tomcat 200 个 worker 逐渐耗尽
↓
全站接口排队等 worker → RT 全线飙升
三个致命的设计错配:
| 设计点 | 问题 | 后果 |
|---|---|---|
LinkedBlockingQueue(2000) 容量大 | 队列不打满,maxPool 永远不扩容,50 个核心线程长期阻塞 | 反应滞后,堆积到爆才扩容 |
CallerRunsPolicy | 反压策略选错场景 | 污染 Tomcat 主线程 |
| 任务里直接调下游同步接口 | 没有超时兜底 | 一个下游慢就所有任务卡住 |
核心认知:拒绝策略决定的是"任务满的时候谁买单",CallerRuns 买单的人是提交方,如果提交方是宝贵的资源(Tomcat worker、WebFlux event loop),那就是自杀。
// 回调场景:允许异步补偿,绝不能污染上游
ThreadPoolExecutor pool = new ThreadPoolExecutor(
32, // core: 按机器核数 * 2
64, // max: 不超过 core * 4
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500), // 队列小一点,尽早暴露问题
new ThreadPoolExecutor.AbortPolicy(), // 关键:快速失败,不反压
new ThreadFactoryBuilder()
.setNameFormat("pay-callback-%d")
.build()
);
// 提交任务的代码
try {
pool.submit(callbackTask);
} catch (RejectedExecutionException e) {
// 队列满了怎么办?
mqProducer.send("callback-retry-topic", taskPayload); // 扔 MQ 异步补偿
metrics.increment("callback.rejected"); // 埋点
}
三种方案对比:
| 方案 | 代码 | 特点 | 缺点 |
|---|---|---|---|
| RPC 超时 | httpClient.setReadTimeout(500) | 简单 | 只解决下游不响应,不解决任务本身复杂耗时 |
| Future.get(timeout) | 见下方代码 | 调用方主动放弃 | 任务还在跑,资源占用不释放 |
| CompletableFuture.orTimeout | 见下方代码 | 优雅 + 支持异常降级 | 需要 11+ |
// 方案 2:Future.get(timeout) —— 老代码兼容
Future<CallbackResult> f = pool.submit(() -> {
RiskResult r = riskClient.check(order); // 下游 3s
return process(r);
});
try {
CallbackResult result = f.get(2, TimeUnit.SECONDS);
// 用 result
} catch (TimeoutException e) {
f.cancel(true); // 尽力中断,不保证生效
fallbackService.defaultResult(); // 降级
}
// 方案 3:CompletableFuture.orTimeout —— 推荐
CompletableFuture<CallbackResult> future = CompletableFuture
.supplyAsync(() -> process(riskClient.check(order)), pool)
.orTimeout(2, TimeUnit.SECONDS) // 2s 内没完成就 TimeoutException
.exceptionally(ex -> {
metrics.increment("callback.timeout");
return FallbackResult.DEFAULT; // 异常降级
});
CallbackResult result = future.join(); // 阻塞拿结果
关键区别:
// 用 Resilience4j 为例
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 错误率 > 50% 熔断
.slowCallRateThreshold(80) // 慢调用占比 > 80% 熔断
.slowCallDurationThreshold(Duration.ofSeconds(2)) // 单请求 > 2s 算慢
.waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断 30s
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.slidingWindowSize(50) // 最近 50 个请求
.minimumNumberOfCalls(20) // 至少 20 个请求才判断,避免小样本
.build();
CircuitBreaker cb = CircuitBreaker.of("risk-client", config);
// 使用
RiskResult result = circuitBreakerExecute(cb, () -> {
try {
return riskClient.check(order);
} catch (Exception e) {
throw new CallException(e); // 转成熔断能识别的异常
}
}, order -> {
// fallback
metrics.increment("risk.fallback");
return RiskResult.APPROVE_WITH_REVIEW; // 转人工审核
});
熔断状态机:
错误率>50% 或 慢调用>80%
[Closed] ────────────────────────→ [Open]
↑ │
│ 半开试探通过 │ 30s 后
│ ↓
[Half-Open] ←───────────────────────┘
│
│ 半开探测失败
↓
[Open]
@Service
public class PaymentCallbackService {
private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
32, 64, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
new ThreadFactoryBuilder().setNameFormat("pay-cb-%d").build(),
new ThreadPoolExecutor.AbortPolicy()
);
private final CircuitBreaker cb = CircuitBreaker.of("risk", config);
private final MqProducer mq;
private final Metrics metrics;
public CompletableFuture<CallbackResult> handleAsync(CallbackPayload payload) {
try {
return CompletableFuture
.supplyAsync(() -> doProcess(payload), pool)
.orTimeout(3, TimeUnit.SECONDS)
.exceptionally(ex -> {
metrics.increment("callback.failed");
mq.send("callback-retry", payload); // 失败补偿
return CallbackResult.PENDING;
});
} catch (RejectedExecutionException e) {
mq.send("callback-retry", payload);
metrics.increment("callback.rejected");
return CompletableFuture.completedFuture(CallbackResult.PENDING);
}
}
private CallbackResult doProcess(CallbackPayload p) {
RiskResult risk = Call.decorateChecked(cb, () -> riskClient.check(p))
.recover(ex -> RiskResult.APPROVE_WITH_REVIEW); // 熔断降级
return orderService.update(p, risk);
}
}
| 策略 | 行为 | 适用场景 | 反例 |
|---|---|---|---|
| CallerRuns | 提交方跑 | 提交方是空闲业务线程且任务不能丢 | 提交方是 Tomcat worker(本题) |
| Abort | 抛异常 | 任务可重试/可补偿(本题推荐) | 任务丢了不可恢复 |
| Discard | 静默丢弃 | 埋点类可丢任务 | 支付、扣款 |
| DiscardOldest | 丢最老的 | 实时性 > 完整性(如埋点流) | 顺序性任务 |
| 自定义 | 落库/MQ | 金融场景最佳实践 | — |
| 概念 | 保护对象 | 触发条件 | 动作 |
|---|---|---|---|
| 限流 | 自己 | QPS 超过阈值 | 拒绝多余请求 |
| 熔断 | 下游 | 错误率/RT 超过阈值 | 停止调用下游 |
| 降级 | 用户体验 | 熔断/异常时 | 返回默认值/缓存/兜底 |
三者关系:熔断 + 降级通常成对使用(熔断挡住流量,降级给出兜底),限流独立。
场景:团队 6 人,3 人主用 Cursor/Claude Code。3 个月后:
+400/-200 暴涨3 个问题:
从返回的给出的结果看,首先,单测通过率降低。这里边单测,并且很多大量测试只是 mock 掉被 mock 对象,表明单元测试根本没有实际的价值。这里边首先第一个就是单测存在大量的不合理问题,设计不合理,没能覆盖标准,没能覆盖到边界,测试设置不合理。其次,Review 时间缩短了,不合理对吧?Review 时间在 AI 介入之后,Review 时间其实应该大量的增加,而不是缩短,因为编程时间本身变短了,这时候需要更多的 Review 去观测整个设计方案。另外就是大量的代码变更。这个本身是不合理的。在 AI Vibe Coding 的阶段,其实应该给它设定增减范围,尽量只能修改自己相关的内容,每次修改前应该进行设定,而不是大范围的去修改,有和没的都进行修改。线上事故方面,SQL 的审查,这个就是在 code review 方面没有做详细的审核,这个方面需要单独的进行去审核关注。
优点:
不足:
这是这道题典型的"只分析症状不下诊断、只讲感受不给方案"。面试里这样答基本挂了,因为 Vibe Coding 主题考察的是你作为技术负责人能不能建立治理机制,不是让你描述现象。
| 环节 | 传统流程 | AI 时代为什么失效 |
|---|---|---|
| 开发阶段 | 编码 + 单测 + 自测 | AI 让代码生成极快,测试没有跟上速度,变成"应付 CI"的僵尸测试 |
| Review 阶段 | 人肉 Review 30min 看设计 | PR 变大 + Review 疲劳 → 变成"扫一眼 CI 绿灯就 approve",Reviewer 变成了橡皮章 |
| 测试/发布阶段 | 集成测试 + 灰度 | AI 生成的代码"看起来很对",骗过了静态测试,只有到线上真实流量才炸 |
最核心的一句话:AI 把"写代码"从瓶颈变成了非瓶颈,但整个研发流程的其他环节没有同步加速,导致"产出-审查"严重失衡。
| 指标 | 定义 | 采集方式 | 参考阈值 |
|---|---|---|---|
| 有效测试率 | 断言数 / 测试方法数,且排除全 mock 的测试 | 静态扫描:@Mock 数量 ≥ 5 且无真实断言 → 判为无效 | < 30% 触发告警 |
| PR 变更熵 | 修改文件数 × 平均每文件行数 | Git diff 统计 | 单 PR 变更 > 500 行强制拆分 |
| 线上故障归因 | 每月 P2+ 故障中"AI 生成代码引入"的占比 | 故障复盘时打标签 | > 20% 需专项治理 |
加分指标:
开发者提交 PR
│
▼
┌───────────────────────────┐
│ Gate 1: AI 标注 & 元数据 │ 软门禁
│ - PR 描述必须标注 AI 占比 │ - 未标注可合并但打标签
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ Gate 2: 静态扫描 & 安全 │ ★ 硬门禁
│ - SQL AST 分析(危险 SQL) │ - Block 合并
│ - 敏感信息扫描 │
│ - 依赖漏洞 │
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ Gate 3: PR 大小检查 │ ★ 硬门禁
│ - 单 PR > 500 行强制拆分 │ - Block 合并
│ - 单文件 > 200 行改动预警 │
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ Gate 4: 测试质量门禁 │ ★ 硬门禁
│ - 新增代码必须有新增测试 │ - Block 合并
│ - 测试有效率高(非全 mock) │ - Block 合并
│ - 覆盖率不下降(增量>70%) │
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ Gate 5: AI Reviewer │ 软门禁(辅助)
│ - Claude/GPT 二次 review │ - 意见仅供参考
│ - 生成风险清单 │
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ Gate 6: 人工 Review │ ★ 硬门禁
│ - 至少 1 人 approve │ - Block 合并
│ - AI 生成 > 50% 的 PR │ - 需 2 人 approve
│ 需 2 人 approve │
└───────────────────────────┘
│
▼
Merge
硬门禁的核心思路:凡是"人容易忽略但机器能检查的",一律机器拦;凡是"必须人来判断意图"的(业务正确性、架构合理性),机器给提示但人来决定。
场景:日均订单量 800 万,微信/支付宝/银联 3 个渠道 + 自建优惠券系统。当前对账发现:
3 个问题:
T 加一的对账核心是要保证数据的完全的准确,将当天产生的问题进行排查,能明确上一日产生了哪些问题,有什么不一致的。所以 T 加一的对账基本上是核心数据是对比,然后对比整个总数的差异情况,增量在多少,一天的对比差异在多少,最终得出一个当日的差异。然后 T 加零实时监控的话,就是对比一定时间范围内,然后对账系统的数据跟实时系统的数据存在哪些不符合预期的数据。G 加一基本上是用作归档,把归档数据进行一个统计,统计和当前实时数据进行对比,看具体差距有多少。然后得出差异差异量,然后进行去看具体的哪存在问题,是中台对比不成,还是说哪存在了数据上的差异,我就去寻找差异信息的具体条数。这个其实是相当于对存量数据的历史归档数据区进行搜索和查询。比如说 7+1 的数据,之前的数据都已经归档了,在归档数据内进行当天已经确定结。 差错账户设计,差错账户具体指什么?这块没有描述清楚,暂时不确认。但是自动修复的边界在哪里?比如说其实是渠道成功了,我方订单状态未处理,这种可以自动修复。但是我方退款成功了,渠道显示失败了,然后超时后又成功了,这种其实也是可以自动处理的。优惠券回滚失败,用户重复领取,这个需要人工介入去查看具体什么原因导致的重复领取,是否是幂等性的,是否幂等方面没做处理,这块需要人工介入查看。核心就是最后的状态能修复到一致的,简单的通过逻辑判断能修复到一致的,就可以自动修复。但是需要有多种情况去复查的,这种就必须走人工。 重复领取密等失失败,我觉得核心原因是在于用户发放定发放优惠券的数量状态,这块其实可以在每个阶段发放的时候,做一个唯一校验也好,做一个状态机也好,就是他接收了这个类似的优惠券吗?如果接收了就不可以再接收,发放的时候做这方面判定,然后做一个唯一标识,就发放的过程中做唯一标识,防止重复领取、重放。 兜底监控的话,其实可以放优惠券放出比例、放出数和当年活跃用户数,然后人均优惠券数这种监控去进行判定,具体哪些数据存在问题。 果的数据的对比。然后 T+0 的实时数据的话,就是对当天内产生变动的数据来进行处理。
优点:
不足:
总体评价:这道题属于高阶题,需要展现"能把方案设计到可落地"的能力。目前回答停留在"知道大概方向"的层面,缺少工程细节。
┌─────────────────────────────────────────────────────────┐
│ T+0 实时差异监控 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ 支付回调 │───→│ CDC 变更 │───→│ 实时对账引擎 │ │
│ │ 消息流 │ │ 捕获 │ │ (Flink/自研) │ │
│ └──────────┘ └──────────┘ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 差异判定 & 告警 │ │
│ │ - 状态不一致 │ │
│ │ - 金额不一致 │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ 写差错表 + 触发告警/自动修复 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ T+1 全量对账 │
│ │
│ 每日 02:00 开始 │
│ ┌──────────────────────────────────────┐ │
│ │ 1. 拉取各渠道账单文件(T-1 日数据) │ │
│ │ - 微信/支付宝/银联 官方对账文件 │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 2. 与本地订单表全量比对 │ │
│ │ - 按 order_id 逐笔匹配 │ │
│ │ - 比对:金额、状态、时间 │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 3. 输出差异报表 │ │
│ │ - 渠道成功我方失败 │ │
│ │ - 我方成功渠道失败 │ │
│ │ - 金额不一致 │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 4. 差异进入差错表 │ │
│ │ - 分级:自动修复 / 人工审核 │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
T+0 vs T+1 对比:
| 维度 | T+0 实时 | T+1 全量 |
|---|---|---|
| 数据源 | 业务事件流 + CDC | 渠道官方账单文件 |
| 时延 | 秒级 | 次日凌晨 |
| 覆盖度 | 覆盖已处理交易 | 覆盖全部交易(含掉单) |
| 主要作用 | 及时发现 新差异 | 兜底发现 漏单/漏对 |
| 修复方式 | 自动修复优先 | 人工审核为主 |
为什么必须双轨:
-- 差错表
CREATE TABLE reconciliation_error (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id VARCHAR(64) NOT NULL,
channel VARCHAR(16) NOT NULL, -- wechat/alipay/unionpay
error_type VARCHAR(32) NOT NULL, -- CHANNEL_SUCCESS_LOCAL_FAIL / REFUND_TIMEOUT / COUPON_DUPLICATE
error_level TINYINT NOT NULL, -- 1:自动修复 2:人工审核 3:资损升级
channel_data JSON, -- 渠道侧数据
local_data JSON, -- 我方数据
status TINYINT DEFAULT 0, -- 0:待处理 1:自动处理中 2:已自动修复 3:待人工 4:已人工处理 5:已关闭
process_log JSON, -- 处理流水
created_at DATETIME,
resolved_at DATETIME
);
-- 差异处理表(记录每次修复动作)
CREATE TABLE error_resolution_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
error_id BIGINT NOT NULL,
action VARCHAR(64), -- AUTO_RETRY_REFUND / MANUAL_APPROVE / MANUAL_REJECT
operator VARCHAR(32), -- system / user_id
before_state JSON,
after_state JSON,
result TINYINT, -- 0:失败 1:成功
created_at DATETIME
);
自动修复 vs 人工审核边界:
| 场景 | 自动/人工 | 理由 |
|---|---|---|
| 渠道成功、我方未终态 | 自动 | 补一次订单终态即可,无资损风险 |
| 我方退款成功、渠道退款超时 | 自动 | 渠道会重试到成功,只需等确认 |
| 我方退款成功、渠道最终失败 | 自动重试 → 人工 | 先重试 3 次,仍失败人工介入 |
| 金额不一致 | 人工 | 涉及资损,必须人工核对 |
| 优惠券重复领取 | 人工 | 涉及资损,需评估是否追回 |
| 渠道账单缺失某笔 | 人工 | 渠道可能漏出账,需联系渠道 |
核心原则:
自动修复流程:
差异写入差错表 (status=0, level=1)
↓
定时任务扫描 level=1 的待处理记录
↓
执行修复动作(补订单终态 / 触发退款 / 等渠道回调)
↓
修复结果写回 resolution_log
↓
成功 → status=2, 关闭
失败 → 重试计数 +1
- 重试 < 3 → 继续等待下次扫描
- 重试 ≥ 3 → status=3, 升级人工
三种典型根因:
| 根因 | 表现 | 修复方案 |
|---|---|---|
| 幂等 Key 设计错误 | 用 order_id 做幂等,但一个 order 可以领多次 | 用 user_id + coupon_template_id + time_window 做幂等 |
| 并发控制缺失 | 快速点击导致重复 | 数据库唯一索引 + 分布式锁双重保障 |
| 事务边界错误 | 库存扣减和发放不在同一事务 | 本地事务 + 本地消息表保证最终一致 |
根因定位流程:
1. 拿到重复领取的用户 ID 和优惠券 ID
↓
2. 查数据库:这几笔发放记录的时间戳
- 如果间隔 < 100ms → 并发问题
- 如果间隔 > 1s → 幂等 Key 或事务问题
↓
3. 查日志:是否走了同一个请求 ID
- 是同一个请求 → 幂等 Key 失效
- 不同请求 ID → 并发问题
↓
4. 查代码:发放接口的幂等实现
- 是否有唯一索引?
- 是否用了分布式锁?
- 锁的粒度对不对?
修复方案(三层防护):
@Service
public class CouponService {
/**
* 第一层:分布式锁 - 防并发
*/
@RedisLock(key = "coupon:receive:{userId}:{couponId}", expire = 5)
public Coupon receiveCoupon(Long userId, Long couponId) {
return doReceive(userId, couponId);
}
/**
* 第二层:数据库唯一索引 - 防重复
*/
private Coupon doReceive(Long userId, Long couponId) {
// 唯一索引:user_id + coupon_template_id
// 如果重复,会抛 DuplicateKeyException
try {
couponRecordMapper.insert(new CouponRecord(userId, couponId, ...));
} catch (DuplicateKeyException e) {
return getCouponRecord(userId, couponId); // 返回已有记录
}
return issueCoupon(userId, couponId);
}
/**
* 第三层:状态机校验 - 防状态异常
*/
private Coupon issueCoupon(Long userId, Long couponId) {
CouponTemplate template = templateMapper.selectById(couponId);
// 校验用户是否已达到领取上限
if (userCouponMapper.countByUser(userId, couponId) >= template.getMaxPerUser()) {
throw new BusinessException("已达领取上限");
}
// 校验库存
if (templateMapper.decrementStock(couponId) == 0) {
throw new BusinessException("库存不足");
}
return couponRecordMapper.selectByUser(userId, couponId);
}
}
兜底监控:
| 指标 | 告警阈值 | 说明 |
|---|---|---|
| 单用户领券数 | > 模板上限的 1.5 倍 | 可能幂等失效 |
| 单用户每秒领券次数 | > 5 | 可能并发问题 |
| 总发放数 vs 总库存 | 发放 > 库存 95% | 可能超发 |
| 人均券数 / 活跃用户 | 环比上涨 > 20% | 业务指标异常 |
| 同一 coupon_template_id 领取失败率 | > 10% | 可能有 bug |
实时监控告警:
-- 每小时跑一次,发现异常立即告警
SELECT user_id, COUNT(*) as cnt
FROM coupon_record
WHERE coupon_template_id = ?
AND created_at > NOW() - INTERVAL 1 HOUR
GROUP BY user_id
HAVING cnt > (SELECT max_per_user FROM coupon_template WHERE id = ?);
| 轮次 | 方向 | 难度 | 表现 | 得分 |
|---|---|---|---|---|
| R1 | 线程池优化 | 进阶 | 反压链路抓得准,但任务级超时概念混淆 | 7/10 |
| R2 | Vibe Coding | 进阶 | 现象分析有感觉,但缺量化指标和流水线设计 | 5/10 |
| R3 | 架构设计 | 高阶 | 有方向感但缺工程细节,差错账户概念不清 | 5/10 |
面试官:今天 3 题,方向覆盖线程池优化、Vibe Coding、架构设计。建议按 R1 → R2 → R3 顺序。
候选人:开始
面试官:[R1 题目 - 线程池反压 + 任务级超时 + 熔断降级]
候选人:
R1 答:CallerRuns 反压到 Tomcat worker 导致全站 RT 上升。下游调用设 500ms 超时,重试 2-3 次。回调任务走独立线程池不占主线程。熔断放在三方调用那,不放整个回调服务。熔断选错误率不选 RT,错误率 20-30% 触发熔断。
面试官:[R1 点评]
候选人:讲解
面试官:[R1 完整讲解 - 反压链路图 + 线程池参数 + 任务级超时三方案 + 熔断三指标状态机 + 完整代码]
候选人:继续
面试官:[R2 题目 - AI 代码可信度治理]
候选人:
R2 答:单测大量 mock 掉被测对象说明测试不合理。Review 时间应该增加而不是缩短。PR 大小要约束,AI 应该只修改自己相关内容。线上事故是 SQL 审查不足,需要单独审核。
面试官:[R2 点评]
候选人:继续
面试官:[R2 完整讲解 - 三环节失效分析 + 三个量化指标 + 六层准入流水线]
候选人:继续
面试官:[R3 题目 - 支付对账 + 差错账户 + 幂等失效]
候选人:
R3 答:T+1 核心保证数据准确,对比总数差异。T+0 实时监控对比时间范围内数据。差错账户没概念,但自动修复边界能判断:简单一致性问题自动修,涉及资损走人工。优惠券重复领取根因是唯一性校验和状态机缺失。兜底监控人均券数、发放比例。
面试官:[R3 点评]
面试官:[R3 完整讲解 - 双轨对账方案图 + 差错账户表结构 + 自动修复流程 + 幂等三种根因 + 三层防护代码 + 兜底监控]
面试官:[面试总结 - 汇总表 + 强项 + 待提升 + 建议]
文档生成时间:2026-09-21