北京云车易购科技 Vibe Coding 实习一面
1. 请介绍你的实习经历
2. 慢 SQL 你会怎么定位,如何判断瓶颈在索引、锁、排序还是回表
答案:
定位慢 SQL 我一般不会直接上来就加索引,而是先看慢查询日志、执行频率、平均耗时、P95/P99、扫描行数和返回行数。如果扫描行数远大于返回行数,通常要重点看索引和过滤条件;如果扫描不多但耗时很长,就要看锁等待、磁盘 IO、临时表、文件排序或下游连接池。
具体会用 EXPLAIN 或 EXPLAIN ANALYZE 看执行计划,包括 type、key、rows、filtered、Extra。如果出现 Using filesort、Using temporary、ALL、回表过多,就要进一步分析索引设计。对于线上问题,还要结合 show processlist、InnoDB 锁等待、事务持续时间和 buffer pool 命中率。
EXPLAIN ANALYZE SELECT id, device_id, status, created_at FROM work_order WHERE factory_id = 1001 AND status = 'PENDING' AND created_at >= '2026-04-01' ORDER BY created_at DESC LIMIT 20;
如果这个 SQL 频繁用于分页查询,可以考虑建立联合索引:
CREATE INDEX idx_factory_status_time ON work_order(factory_id, status, created_at DESC);
但索引顺序要结合等值条件、范围条件和排序字段设计。比如 factory_id、status 是等值过滤,created_at 既做范围又做排序,放在后面比较合适。优化后还要看是否减少了扫描行数和 filesort,而不是只看有没有走索引。
3. 索引明明建了但没生效,常见原因有哪些
答案:
索引没生效不一定是 MySQL “不用索引”,很多时候是索引对当前查询不划算。常见原因包括:不符合最左前缀、字段上使用函数或表达式、隐式类型转换、模糊查询前置 %、范围条件后面的索引列无法继续用于有序定位、低选择性字段导致优化器认为全表扫描更便宜、统计信息不准、返回列太多导致回表成本高。
还有一种很隐蔽的是字符集或排序规则不一致,比如两个表 join 的字段类型看起来一样,但字符集不同,可能导致索引利用异常。线上排查时要对照表结构、字段类型、字符集、索引基数和执行计划,不能只看 SQL 表面。
-- 隐式类型转换,可能导致索引效果变差 SELECT * FROM user_device WHERE device_code = 123456; -- 如果 device_code 是 varchar,应该这样写 SELECT * FROM user_device WHERE device_code = '123456';
对于函数导致索引失效:
-- 不推荐:对索引列做函数计算 SELECT * FROM work_order WHERE DATE(created_at) = '2026-04-20'; -- 推荐:改成范围查询 SELECT * FROM work_order WHERE created_at >= '2026-04-20 00:00:00' AND created_at < '2026-04-21 00:00:00';
4. Redis 缓存和 MySQL 数据不一致,你会怎么处理
答案:
缓存和数据库不一致要先看业务能接受什么级别的一致性。大多数读多写少场景可以接受短暂最终一致;如果是库存、余额、审批状态这类强一致数据,就不能只依赖缓存。常见方案是写数据库后删除缓存,而不是写数据库后更新缓存,因为更新缓存可能引入并发覆盖和复杂的数据组装问题。
更稳的方式是延迟双删或基于消息队列异步删除。先更新数据库,再删除缓存;如果担心并发读把旧值写回缓存,可以延迟一段时间再删一次。对于极高一致性要求的场景,可以加版本号或逻辑时间戳,缓存里只接受更新版本的数据。
@Transactional
public void updateDeviceStatus(Long deviceId, String status) {
deviceMapper.updateStatus(deviceId, status);
redisTemplate.delete("device:" + deviceId);
mqTemplate.convertAndSend("cache.delete.exchange", "device.status", deviceId);
}
消息消费者做延迟删除:
public void onCacheDelete(Long deviceId) {
try {
Thread.sleep(500);
redisTemplate.delete("device:" + deviceId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
这个方案不是绝对强一致,但能覆盖大多数缓存旧值回写问题。如果业务不能接受任何旧读,就需要改成读写都走数据库事务或引入更严格的版本校验。
5. Redis 热 key、大 key、缓存穿透同时出现时怎么治理
答案:
热 key 会导致单个 Redis 节点压力过高,大 key 会导致网络传输、序列化、删除和阻塞问题,缓存穿透会让不存在的数据持续打到数据库。三个问题如果同时出现,一般说明缓存设计已经有结构性问题。
热 key 可以做本地缓存、热点拆分、多副本读、限流和预热。大 key 要拆结构,比如一个超大的 hash 或 list 拆成多个分片 key;删除大 key 用异步删除,避免阻塞主线程。缓存穿透可以用空值缓存、布隆过滤器和参数校验解决。
public DeviceInfo getDevice(String deviceId) {
if (!bloomFilter.mightContain(deviceId)) {
return null;
}
String key = "device:" + deviceId;
DeviceInfo cached = localCache.getIfPresent(key);
if (cached != null) {
return cached;
}
cached = redis.get(key, DeviceInfo.class);
if (cached != null) {
localCache.put(key, cached);
return cached;
}
DeviceInfo db = deviceMapper.selectById(deviceId);
if (db == null) {
redis.set(key, DeviceInfo.empty(), Duration.ofSeconds(30));
return null;
}
redis.set(key, db, Duration.ofMinutes(10));
localCache.put(key, db);
return db;
}
大 key 处理示例:
-- 非阻塞删除 UNLINK big:device:history:1001
如果是设备历史数据这种天然大集合,不应该全部塞一个 key,可以按设备和日期拆分:
device:history:{deviceId}:{yyyyMMdd}
6. 你自己写的代码和 AI 生成代码如何分工,比例怎么控制
答案:
我不会按固定比例分工,而是按风险分工。低风险、模式化、重复性强的部分可以让 AI 多参与,比如 DTO、VO、Mapper、单测模板、接口文档、异常枚举、SQL 草稿、日志分析脚本。高风险部分必须自己主导,比如事务边界、权限校验、状态机、并发控制、幂等、消息一致性和数据修复脚本。
一般流程是我先写设计和关键接口,让 AI 根据上下文补实现草案;然后我审查生成代码,修改边界条件和异常处理,再让 AI 补充测试。AI 更像副驾驶,不是最终提交者。真正上线的代码必须经过人工 review、测试和 CI。
适合 AI: - 重复 CRUD - 单测样例 - 文档注释 - 参数校验模板 - 日志分析脚本 必须人工主导: - 分布式事务 - 并发写入 - 权限边界 - MQ 消费幂等 - 数据库变更 - 灰度和回滚逻辑
7. AI 生成代码出问题,你会怎么处理
答案:
先看问题是在需求理解、上下文缺失、代码实现、边界条件还是测试不足。如果是需求理解错了,需要重新补充约束,比如输入输出、异常场景、并发场景、依赖接口语义。如果是上下文缺失,就把相关类、表结构、调用链、报错栈补齐,而不是让 AI 继续猜。
处理上不会直接让 AI 重写整块代码,而是先最小化复现问题,写一个失败用例,再让 AI 或自己修复。修完以后要跑相关测试和回归。对于生产问题,还要看是否需要回滚、补偿数据和增加防线。
@Test
void shouldNotCreateDuplicateTaskWhenRetry() {
String requestId = "req-001";
taskService.createTask(requestId, "device-check");
taskService.createTask(requestId, "device-check");
int count = taskMapper.countByRequestId(requestId);
assertEquals(1, count);
}
如果 AI 之前生成的代码没有幂等保护,就要补唯一键或幂等表:
ALTER TABLE diagnosis_task ADD UNIQUE KEY uk_request_id(request_id);
8. 如何对 AI 生成代码做质量检查,流程是什么
答案:
质量检查不能只靠人工看一眼。我的流程一般是:先做 diff 范围控制,确认 AI 没有改无关文件;然后做编译、单测、静态扫描、依赖漏洞扫描;再做业务 review,重点看事务、异常、空值、并发、日志、权限和幂等;最后进入测试环境做接口回归和灰度。
对于 AI 生成代码,还会额外看有没有“看似合理但实际不存在”的 API、有没有吞异常、有没有硬编码、有没有把敏感信息写日志、有没有破坏原有兼容性。AI 很容易写出能编译但语义不对的代码,所以必须用测试和 review 把关。
mvn clean verify mvn test -Dtest=DeviceTaskServiceTest mvn spotbugs:check mvn dependency-check:check
CI 里可以加质量门禁:
quality_gate: min_line_coverage: 80 block_on_high_vulnerability: true block_on_compile_warning: false require_code_review: true
9. 对 AI 生成代码会做哪些测试,不只是单元测试
答案:
会分层测试。单元测试验证函数和服务逻辑,集成测试验证数据库、Redis、MQ 和外部接口协作,契约测试验证接口输入输出兼容性,回归测试验证历史功能不被破坏,压测验证性能和资源占用,故障注入验证超时、重试和降级。
AI 生成代码尤其要测边界条件,比如空输入、重复请求、并发请求、下游超时、消息重复、缓存不存在、数据库唯一键冲突。很多 AI 代码只覆盖 happy path,真正上线出问题通常都在异常路径。
@Test
void shouldRollbackWhenMqSendFailed() {
doThrow(new RuntimeException("mq failed"))
.when(mqClient).send(any());
assertThrows(RuntimeException.class, () -> {
orderService.createAndNotify(new CreateOrderCommand("u1", "sku1"));
});
assertEquals(0, orderMapper.countByUserId("u1"));
}
并发测试也很重要:
@Test
void shouldCreateOnlyOneRecordUnderConcurrentRequest() throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(20);
CountDownLatch latch = new CountDownLatch(20);
for (int i = 0; i < 20; i++) {
pool.submit(() -> {
try {
taskService.createOnce("idem-001");
} finally {
latch.countDown();
}
});
}
latch.await();
assertEquals(1, taskMapper.countByIdemKey("idem-001"));
}
10. 使用 AI 工具时,你有什么比别人更稳定的方法
答案:
我不会直接问“帮我实现某某功能”,而是先让 AI 明确边界。比如先给它角色、代码上下文、接口定义、表结构、异常语义、性能要求和不允许修改的文件。生成前先让它输出方案和影响面,确认后再生成代码。生成后再让它按 checklist 自查一次,但最终还是人工判断。
比较有效的是把需求写成规格说明,而不是自然语言愿望。比如明确输入、输出、幂等键、失败重试、事务边界、日志字段、测试用例。AI 在明确约束下表现稳定很多。
请只修改 DeviceTaskService 和对应测试类。 要求: 1. createTask 必须基于 requestId 幂等 2. 数据库写入和任务状态更新在同一事务 3. MQ 发送失败时任务状态置为 WAIT_RETRY 4. 不允许吞异常 5
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

