汉安数智面试-cnblog
汉安数智面试10题完整标准答案(可直接背诵)
1、SQL中 IN 可以用什么优化?(全新补全)
IN 的核心问题:当 IN 后面集合量大、子查询数据多时,容易走全表扫描、生成临时表、IO 高、效率差。常见优化方案有 4 种:
① 小数据用 BETWEEN 替代 IN:连续数值场景,BETWEEN 索引命中率更高、开销更小。
② 大数据/子查询用 EXISTS 替代 IN:
IN 是先执行子查询、再匹配外层;EXISTS 是先遍历外层表、逐行判断是否存在匹配数据。
外层表小、子查询结果大时,EXISTS 性能远优于 IN。
实战例子(一眼看懂区别)
场景:查询「有过订单的用户」
用户表 user(1000 条,小表)
订单表 order(100 万条,大表)
【IN 写法(低效)】
SELECT * FROM user
WHERE id IN (SELECT user_id FROM order);
原理:先把 100 万条订单全部查出来生成临时结果集,再和用户表匹配,数据量大极耗 IO。
【EXISTS 写法(高效)】
SELECT * FROM user u
WHERE EXISTS (SELECT 1 FROM order o WHERE o.user_id = u.id);
原理:遍历外层小表用户,每遍历一个用户,就去大订单表匹配到第一条就立刻终止,不会查全量数据,极大提升效率。
总结口诀(面试必背)
大结果集用 EXISTS,小连续数值用 BETWEEN,复杂关联用 JOIN,坚决不用 NOT IN。
③ 用 JOIN 联表查询替代 IN 子查询(最优常用):
把 IN 的匹配逻辑改成 INNER JOIN / LEFT JOIN,数据库可以高效走索引关联,避免临时表和全扫,是生产最常用的 IN 优化手段。
④ 超大 IN 集合拆分、分批查询:
IN 元素过万时性能暴跌,可以拆分批次查询、或者导入临时表关联查询,避免单次 IN 过大导致超时。
补充禁忌:禁止 NOT IN(有 NULL 会查不到数据、极易踩坑),统一用 NOT EXISTS 替代。
2、线程锁有哪些?(全新补全)
Java 面试常答、工作常用锁分两大类:内置锁(悲观锁)、JUC 显式锁,外加细分锁机制:
1)synchronized 内置锁
底层依赖 JVM 实现,可锁方法、代码块;自动加锁释放、不用手动控制;属于可重入、悲观、独占锁。
2)ReentrantLock 可重入显式锁
JUC 手动锁,功能比 synchronized 更强:可手动加解锁、可超时锁、可中断锁、支持公平/非公平锁。
3)读写锁 ReentrantReadWriteLock
读多写少场景专用:读锁共享、写锁独占,大幅提升并发读性能(比如缓存、配置查询)。
4)自旋锁(通俗详解+例子)
核心大白话:不睡觉、一直重试
普通锁(synchronized):拿不到锁就阻塞休眠,线程挂起、让出CPU,等别人释放锁再唤醒。
缺点:休眠、唤醒需要系统调度,开销大。
自旋锁:拿不到锁不阻塞、不休眠,就在原地循环空转、不断尝试抢锁,直到抢到为止。
生活化例子:
排队上厕所:
1. 普通悲观锁:没位置我直接蹲旁边睡觉,有人出来再喊我(要唤醒,慢)。
2. 自旋锁:没位置我不睡觉,一直站门口反复看、反复问有没有空位,一旦有人出来我立刻进去(极快)。
适用场景(面试必背):
锁持有时间非常短、竞争不激烈。
因为等待时间很短,空转的CPU开销 < 线程休眠唤醒的开销。
缺点:
如果别人锁持有很久,自旋会一直空跑、白白占用CPU,导致CPU飙高。
面试一句话总结:
自旋锁是循环抢锁、不阻塞、不空歇,短时等待性能极高,长时等待浪费CPU。
5)悲观锁 & 乐观锁(数据库层面,带通俗解释+实战例子)
一、悲观锁(顾名思义:极度悲观)
核心思想:我默认别人一定会抢我的数据、一定会改。所以我一查数据就直接上锁,别人不能读也不能改,等我事务结束才释放。
实现方式:SELECT ... FOR UPDATE
实战例子(库存扣减):
我要扣商品库存,我先加锁锁住这条库存数据,别人排队等着改,杜绝超卖。
特点:独占、安全、并发低、性能差,适合并发不高、数据冲突多的场景。
二、乐观锁(顾名思义:极度乐观)
核心思想:我默认别人不会改我的数据,所以我全程不上锁,读写随便搞,只在最后更新的时候校验一下有没有被别人改过。
实现方式:版本号 version / 时间戳
实战例子(版本号机制):
1、查询数据时,拿到 version=1;
2、业务处理逻辑;
3、更新时校验:update set stock=stock-1,version=version+1 where id=1 and version=1;
4、如果此时别人已经改了数据,version 变成2,我的更新失败,重试即可。
特点:无锁、并发高、性能好,适合高并发、冲突少的场景(电商、订单系统主流)。
面试绝杀总结:
悲观锁:先锁再操作,防冲突,牺牲性能换安全;
乐观锁:先操作后校验,无阻塞,高性能高并发首选。
6)分段锁(通俗详解+例子)
核心原理:不锁整张表、不锁整个容器,只锁当前操作的一小段。
通俗类比(秒懂):
如果整个厕所只有一把锁(全局锁):一个人进去,所有人都得等,并发极差,效率极低。
分段锁就是:把厕所分成16个小隔间,每个隔间一把锁。
你只用锁住你正在用的那一间,别人可以正常用其他隔间,互不影响,并发直接拉满。
经典场景:JDK1.7 ConcurrentHashMap
它把整个大集合分成 16个Segment分段:
1、修改数据时,只锁当前这一段;
2、其他15段数据照常读写,不会被阻塞;
3、相比 Hashtable 锁住整个集合,分段锁极大减小了锁粒度,大幅提升并发性能。
面试一句话总结:
分段锁就是化整为零、分段加锁,减小锁竞争范围,是高并发场景优化锁性能的核心手段。
3、点餐覆盖是什么意思?(全新补全,业务标准答案)
这是外卖/点餐系统经典业务名词,点餐覆盖 = 新下单/改单覆盖旧的未结算订单内容,核心场景:
1、用户下单后、商家未接单/未出餐时,用户重新下单、或者修改菜品、规格、数量,系统用最新订单数据覆盖旧订单,旧订单作废,以新单为准。
2、覆盖规则:只覆盖未支付、未接单、未完成的有效订单;已接单、已出餐、已结算订单不允许覆盖,只能走退单、改单流程。
业务目的:避免用户重复下单、冗余脏订单,保证同一桌/同一用户同一时段只有一条有效待处理订单。
4、前端到后端数据库完整业务场景(登录模块)【你的版本已大幅完善、修正漏洞】
我以用户登录完整链路,从前端、网络、后端 MVC、权限、缓存、数据库,给你一套标准面试完整版:
1. 前端层
用户输入账号密码,前端做基础非空、格式校验,通过 Ajax/axios 异步 POST 请求,把表单数据传给后端接口。
2. 网络与拦截层
请求经过 Nginx 转发、全局拦截器,到达后端 SpringMVC 的 DispatcherServlet 前端控制器,统一拦截所有请求。
3. 路由匹配
DispatcherServlet 根据 URL 路径匹配 @RequestMapping,定位到对应的 LoginController 控制器方法。
4. Controller 层(参数校验、调用业务)
接收账号密码,做参数校验,调用 UserService 登录业务方法。
5. Service 层(核心业务逻辑)
① 查询 Redis 缓存,判断用户是否已登录、Token 是否有效,有效直接返回登录成功;
② 缓存未命中,调用 Mapper 查询数据库用户信息;
③ 判断用户名是否存在,不存在返回账号错误;
④ 对前端明文密码做 MD5/SM3 加密,和数据库密文密码比对;
⑤ 密码一致则登录成功,不一致返回密码错误;
⑥ 登录成功后,JWT 生成唯一 Token,存入 Redis(设置过期时间),返回给前端。
6. Dao/Mapper 层
通过 Mybatis 执行 SQL,查询用户表账号、加密密码、状态等数据,返回给业务层。
7. 数据库层
存储用户基础信息,密码密文存储,字段加索引保证查询速度。
8. 前端响应处理
前端拿到 Token,存入 localStorage / cookie,后续所有接口请求携带 Token,后端拦截器校验登录态。
亮点补充(面试加分):整条链路实现了前端校验、后端幂等、缓存提速、密码加密、无状态登录、权限拦截的完整闭环。
5、开发一个项目的流程、模块如何衔接?(全新补全,面试标准流程)
完整项目开发流程,从 0 到 上线,分为 6 步,同时讲清模块衔接:
1)需求分析 & 评审
梳理业务需求、输出原型图、需求文档,确定功能模块(登录、订单、用户、支付等),前后端统一接口文档。
2)数据库设计
设计表结构、字段、索引、主键、外键、关联关系,确定分表、字典、状态字段,提前规避慢查询。
3)架构搭建 & 模块拆分
搭建项目脚手架、统一返回结果、异常处理、全局拦截、工具类。
按分层拆分模块:Controller 接口层、Service 业务层、Dao 数据层、Entity 实体层、Utils 工具层。
模块衔接规则:
Controller 只接收请求、不写业务;
Service 处理核心业务、事务控制、模块调用;
Dao 只负责数据库 CRUD;
各层单向调用,解耦清晰。
4)功能迭代开发
按模块逐个开发、接口联调,前后端对接测试。
5)测试优化
单元测试、接口测试、压力测试、SQL 优化、代码规范检查、Bug 修复。
6)部署上线 & 运维监控
打包、部署服务器、配置 Nginx、日志监控、异常告警、线上迭代维护。
6、AOP 具体原理(你的版本深度完善、标准化)
AOP 全称面向切面编程,核心思想:不修改原有业务代码的前提下,对方法进行统一增强。
1、底层原理
基于 Java 动态代理 + 反射机制 实现:
目标类有接口用 JDK 动态代理;无接口用 CGLIB 字节码代理。
2、核心组件
① 切面 @Aspect:定义切面类,存放所有增强逻辑;
② 切入点 @Pointcut:通过 execution 表达式匹配需要拦截的所有目标方法;
③ 通知:前置、后置、返回后、异常、环绕通知,在方法执行前后做增强。
3、执行流程
程序调用目标方法 → Spring 识别切入点匹配 → 生成代理对象 → 通过反射拦截方法执行 → 执行自定义切面逻辑(日志、权限、事务、限流)→ 执行原有业务方法。
4、实际用途(面试加分)
统一日志记录、统一权限校验、事务控制、接口限流、性能监控、参数脱敏。
7、Bean 的生命周期 + 获取 Bean 实例的方式(全新补全,高频面试题)
一、Spring Bean 完整生命周期(标准 6 步)
1. 实例化:Spring 扫描注解,通过反射创建 Bean 空对象;
2. 属性填充:自动装配 @Autowired、依赖注入;
3. 初始化前置处理:执行 Bean 后置处理器前置方法;
4. 初始化:执行 InitializingBean、自定义 init-method;
5. 初始化后置处理:执行 Bean 后置处理器后置方法;
6.销毁:容器关闭时执行 DisposableBean、destroy-method。
二、获取 Bean 实例的 4 种方式****(附实战代码例子)
1. 注解注入(@Autowired、@Resource,项目最常用)
Spring 自动注入容器中的Bean,无需手动创建,日常开发主流用法。
@Service
public class UserService {
// 自动从Spring容器中获取UserMapper Bean实例
@Autowired
private UserMapper userMapper;
}
2. 手动获取(ApplicationContext上下文getBean())
适用于非Spring管理的普通类,手动从容器中获取Bean实例。
// 获取Spring上下文
ApplicationContext context = SpringContextUtil.getApplicationContext();
// 根据Bean类型获取实例
UserService userService = context.getBean(UserService.class);
// 根据Bean名称获取实例
UserService userService2 = (UserService) context.getBean("userService");
3. 工厂方式(FactoryBean自定义创建Bean)
用于自定义复杂Bean的创建逻辑,Mybatis整合Spring核心就是利用FactoryBean创建MapperBean。
public class CustomBeanFactory implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
// 自定义Bean创建、初始化逻辑
User user = new User();
user.setUsername("test");
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
// 注册工厂Bean
@Bean
public CustomBeanFactory customBeanFactory(){
return new CustomBeanFactory();
}
4. 配置类注册(@Bean注解手动注册实例)
用于第三方类、无法添加注解的类,手动将对象注入Spring容器。
@Configuration
public class BeanConfig {
// 手动创建Bean并交给Spring容器管理
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
8、SQL 常见优化方式(你的版本大幅扩充、系统化)
我整理面试最全、最稳的一套答案:
1、索引优化(核心)
高频查询字段建索引;遵循最左匹配原则;避免索引失效(like %前缀、函数运算、隐式类型转换、or 滥用)。
2、SQL 语句写法优化
禁止 select *,只查需要字段;
大 IN 改 JOIN/EXISTS;
避免子查询嵌套过深;
分页必须加 limit,避免全量返回。
3、表结构优化
字段合理设计、避免大字段查询;冷热数据分离;大表分库分表、分区表。
4、查询逻辑优化
优先过滤再关联;小表驱动大表;减少多表联查;合理使用临时表。
5、缓存优化
高频查询数据放 Redis,减少数据库查询压力。
6、执行计划优化
通过 explain 分析 SQL,定位全表扫描、索引失效、临时表、文件排序问题。
9、数据库索引类型(你的版本完善、全覆盖)
面试必答 5 类索引:
1. 主键索引(PRIMARY)
唯一、非空、一张表只能一个,查询速度最快。
2. 唯一索引(UNIQUE)
字段值唯一、可空,用于手机号、账号等唯一字段。
3. 普通索引(INDEX)
最基础索引,无唯一限制,用于普通查询提速。
4. 联合/复合索引
多个字段组合索引,遵循最左前缀原则,避免重复建多个单索引。
5. 全文索引
用于文章、内容长文本模糊检索。
补充面试加分:底层结构都是 B+ 树,索引本质是空间换时间。
10、大模型/智能体幻觉原因、影响回答质量的因素(你的版本全面升级)
幻觉就是模型无依据编造答案、乱答、答非所问,核心原因 + 影响因素如下:
一、产生幻觉的核心原因
1. 模型本身概率生成机制:大模型是基于概率预测下一个字,只会通顺、不一定正确,知识盲区会自动编造。
2. RAG 检索不准/检索缺失:向量库匹配相似度低、检索不到有效知识,模型只能凭固有知识回答,产生错误。
3. 上下文窗口有限:超长对话上下文丢失,信息不全导致乱答。
4. 训练数据过时、脏数据:训练数据存在错误、老旧、偏差数据。
二、影响智能体回答质量的所有因素
1. Prompt 质量:指令是否清晰、约束是否明确、是否限定回答范围;
2. RAG 检索质量:向量分词、向量化模型、召回策略、相似度阈值;
3. 上下文对话状态:对话轮次过多、信息遗忘;
4. 模型能力:底座模型参数、微调精度;
5. 知识库质量:知识是否准确、是否更新、是否去重纠错;
6. 后处理机制:是否有答案校验、引用溯源、拒答机制。
(注:部分内容可能由 AI 生成)

浙公网安备 33010602011771号