项目中最复杂的业务场景?
“教师科研成果(论文/专利/软著)登记系统:
审核流数据拆到 3 张不同业务表;
多表联合查询+级联更新,最大 SQL 关联 11 张表;
数据量 6 万条,接口 2 s → 0.3 s;
对接学校财务系统:手动+批量导入数据格式混乱,写 3 层数据清洗策略(正则+字典映射+权重打分),清洗准确率 99.7%,财务对账零差错。
SQL 优化日常注意点?
- 统一走索引:EXPLAIN 检查 type ≥ range,key 非 NULL;
- 最左前缀;LIKE ‘%xx’ 强制改写成 LIKE ‘xx%’ 或全文索引;
- 子查询改 JOIN,避免 SELECT *;
- 隐式类型转换禁用,列与参数同类型;
- 深分页用覆盖索引+ID 游标分页替换 LIMIT 100000,10;
- 高频统计走聚合中间表或 Redis,降低 90% 查询压力。
索引在什么情况下会失效?
- 违反最左前缀;
- LIKE ‘%xx’ 或 LIKE ‘%xx%’;
- 列上包函数或隐式转换(如 WHERE DATE(gmt_create)=…);
- OR 两侧列未全部索引;
- 低选择性列(性别、状态)单独建索引;
- MySQL 成本估算认为全表更快(<20% 结果集)。
后端开启异步的 4 种常用方式?
- @Async + @EnableAsync + 线程池(Spring);
- CompletableFuture.supplyAsync(…);
- @EventListener 事件驱动(Spring ApplicationEvent);
- MQ 消息队列(RocketMQ/Rabbit)彻底解耦。
缓存挡板在哪个节点写入?
- 读场景:先查缓存→miss→查 DB→回写缓存(Cache-Aside)。
- 写场景:双删策略——① 删缓存→② 更新 DB→③ 延迟 500 ms 再删(防并发脏读)。
- 首次预热:项目启动完成时,通过 ApplicationRunner 把热点榜单全表 scan 写缓存,避免第一个用户慢。
大文件上传① 用户等待久;② 浏览器 5 min 超时;③ 连接池被长时间占用。解决方案?
- 前端分片 5 MB/片,多线程上传,携带 chunkNumber+totalChunks+identifier;
- 后端异步:接收后立刻返回 202 Accepted + 上传 ID;
- 合并层:所有片传完发 /merge 请求,后端用 RandomAccessFile 按 offset 合并,合并完成回调前端;
- 秒传:先算 identifier(MD5),Redis 查已存在直接返回成功;
- 重传:片级失败重试 3 次,identifier+chunkNumber 做幂等;
- 连接池:上传接口单独走 Undertow 长连接 300 s,不影响业务连接池。
多线程合并文件时如何保证数据一致?
- 标识:文件维度 UUID + 总片数;
- 片顺序:本地文件名 UUID_${chunkNumber}.part;
- 合并前校验:收到片数=总片数 且 每片 MD5 匹配;
- 合并时用 synchronized(identifier.intern()) 或分布式锁;
- 合并完删除分片、写 Redis 完成标记,防重复合并。
如果发现内存溢出,应该怎么排查
- 查看关键报错信息,如java.lang.OutOfMemoryError。
- 使用内存映射分析工具对Dump出来的堆储存快照进行分析,分析清楚是内存溢出还是泄漏。
- 如果是内存溢出,则进一步通过工具查看泄漏对象到GC roots的引用链,修复应用程序中的内存泄漏。
- 如果不存在泄漏,则先检查代码中是否有死循环,递归等,再考虑增加堆内存的大小。