汉安数智面试-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 生成)

posted @ 2026-07-17 22:38  alij  阅读(3)  评论(0)    收藏  举报