恒生电子后端一面面经
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 你在实习时候遇到的困难是什么?
- 怎么解决 AI 幻觉,举例子。
- 分布式事务的实现。
- Spring Boot 注解的作用。
- @Autowired 和 @Resource 的区别。
- 事务什么时候使用,作用是什么?
- 文件系统批量读,一个文件到达之后进行高效写,有 200 个线程,其中一个是兜底线程要最后执行,怎么保证兜底线程在最后执行?
- 数据库 MySQL 多表连接有几种方式?
- 多表查询,七八张表连表查询怎么提高查询性能?
- 面试官介绍了一下项目组情况,偶尔写前端能接受吗?
- 反问项目组的安排情况,现在是不是全部都按照项目工作流动分组?
《参考解析》
分布式事务怎么答才算到位。面试官问「实现」时想听的是方案谱系和取舍,而不是名词堆砌。按一致性强度分两类。强一致路线:两阶段提交(2PC)由协调者先让所有参与者预提交并锁资源,全部成功再统一提交,问题是同步阻塞、协调者单点、失败后要人工介入,数据库层的 XA 就是它的实现。柔性事务路线是工程主流:TCC 把业务拆成 Try(预留资源)、Confirm(确认)、Cancel(回滚)三个阶段,锁粒度由业务控制、性能好,代价是要写三个接口并处理空回滚与悬挂;本地消息表把「业务操作 + 消息落库」放在同一个本地事务里,再由后台任务或 MQ 投递并消费;Saga 把一个长事务拆成一串本地事务,失败时按反向补偿回滚,适合流程长、跨多个服务的场景;Seata 的 AT 模式则是自动化的两阶段,用全局锁加 undo log 做反向补偿,业务几乎零侵入。答题时补上最关键的一点:无论哪种方案,消费端都必须幂等(唯一键去重、状态条件更新),并且要有对账兜底——「最终一致」意味着中间态一定存在,能说清中间态怎么被容忍才算真懂。
Spring 注解、@Autowired 与 @Resource。Spring Boot 的注解按职责分组讲最清晰:启动与配置有 @SpringBootApplication(由 @Configuration、@EnableAutoConfiguration、@ComponentScan 合成)、@Configuration、@Bean、@Value、@ConfigurationProperties、@Conditional*;组件注册有 @Component、@Service、@Repository、@Controller、@RestController;依赖注入有 @Autowired、@Qualifier、@Resource;Web 层有 @RequestMapping 及其派生、@RequestBody、@PathVariable;事务有 @Transactional。@Autowired 是 Spring 提供的,默认按类型(byType)注入,注入点可以是构造器、setter、字段,找不到唯一候选时用 @Qualifier 指定名字,required = false 允许为空,它还有 @Primary 这种优先级机制。@Resource 是 JSR-250 标准注解,默认按名字(byName)注入,名字取字段名或 name 属性,找不到名字才退回按类型。实践上推荐构造器注入:依赖不可变、便于测试、能在启动时就暴露循环依赖。
事务的使用场景与失效场景。事务的作用是把一组写操作变成一个原子单元,满足 ACID;用它的典型场景是「多张表或多个步骤必须同生共死」的业务,比如下单扣库存同时写订单、转账扣款与入账。要讲的重点其实是失效场景:同一个类内部方法自调用不走代理,@Transactional 不生效;方法不是 public 时基于 JDK 代理的默认配置不拦截;异常被 catch 吞掉或抛的是检查异常(默认只对 RuntimeException 和 Error 回滚,要配 rollbackFor)也不会回滚;多数据源或跨服务调用时本地事务管不了别人;把长耗时的远程调用放进事务里会让连接长时间被占,最好拆成「先本地落库 + 异步补偿」。传播行为也常被追问:REQUIRED(默认,有则加入、无则新建)、REQUIRES_NEW(挂起外层另起一个,独立提交回滚)、NESTED(保存点,外层回滚会带上内层)。原则是隔离级别和传播行为都只在必要时调整,默认值最符合直觉也最不容易踩坑。
AI 幻觉怎么解决。这题要落到工程手段而不是态度表态。第一层是限制生成范围:把业务口径写进语义层或知识库,模型只做「选指标 + 填参数」,不允许自由发挥业务定义;第二层是给证据并要求引用,RAG 返回的每个片段带来源 ID,回答里必须标出引用,答案里的数字要能溯源到具体数据行;第三层是结构化约束与校验,用 JSON Schema、枚举、工具调用把输出限制在可校验的形态,再用规则校验(数值范围、量纲关系、字段存在性)挡住明显荒谬的结果;第四层是多重校验与交叉验证,同一问题用等价写法跑两次看是否一致、用不同数据源对账、对超阈值波动显式标注;第五层是交互兜底,置信度低或口径有歧义时反问澄清,而不是硬给一个数字;最后把每次问答和人工纠正沉淀成评测集做回归,用错误样本反推知识库缺了什么。举例时可以给一个具体的:模型把「月活」当成了「日活」,就通过在语义层固定指标口径、输出强制带指标 ID、结果与历史区间做合理性对比,把这类错误从「偶发」压成可发现、可拦截。
200 个线程 + 兜底线程最后执行。这题考的是并发协调。如果兜底线程的职责是「所有文件都处理完之后做收尾」(汇总、校验、合并、发通知),标准解法是让主线程等到工作线程全部结束再启动它,而不是让兜底线程自己去抢锁。可选手段有:CountDownLatch(主线程 await,200 个工作线程各自 countDown,归零后执行兜底);CyclicBarrier 或 Phaser(可复用的屏障,适合分批多轮);ExecutorService 提交任务后先 shutdown() 再 awaitTermination;CompletableFuture.allOf(futures).thenRun(...)(最简洁,天然表达「全部完成后执行」)。要注意几个坑:兜底逻辑必须能感知失败——某个线程抛异常时 CountDownLatch 仍会归零,所以必须收集每个任务的异常,或者用 Future.get() 逐个检查,否则「兜底」会在有文件失败的情况下照常执行;200 个线程同时读文件时更该用固定大小的线程池加有界队列(200 是任务数不是线程数),避免上下文切换与 IO 争抢;如果收尾要写回同一份输出,还得保证写入的可见性与顺序。把「怎么等」和「失败怎么办」两件事都答出来,这题就完整了。
MySQL 多表连接与七八张表连表的优化。连接方式先答全:INNER JOIN 取两侧都匹配的行,LEFT JOIN 保留左表全部行、右表不匹配补 NULL,RIGHT JOIN 反之,FULL OUTER JOIN(MySQL 不直接支持,要用 UNION 模拟)保留两侧全部行,CROSS JOIN 是笛卡尔积,另外「逗号连接 + WHERE 过滤」等价于内连接但可读性差。七八张表连查的性能优化要分几个层面:一是减少连接的表数,很多宽表查询其实可以先用子查询各自聚合再连接,或者把常用维度冗余到主表;二是让连接键都有索引,并且驱动表要选过滤后结果集小的那张,MySQL 8.0 有 hash join,但驱动表选择仍显著影响性能;三是避免在大结果集上连查后再分页,先用子查询或覆盖索引把主键集合缩小,再回表连维度表;四是避免在索引列上用函数或隐式类型转换,会让索引失效;五是用 EXPLAIN 看实际的访问类型、命中索引、扫描行数与 Extra 里的 filesort / temporary,逐条消掉;六是高频固定报表考虑用汇总表或缓存,而不是每次都连八张表。面试时能说清「先缩小再连接、连接键必有索引、用执行计划验证」这三条,比背一堆技巧更有说服力。