第十六节 面试问题-高级版

亮子 | 2025-12-15 08:21:23 | 277 | 0 | 0 | 0

1、如何知道一个SQL有没有使用索引?

通过获取该SQL的执行计划判断是否使用索引。
使用explain关键字,后边跟上SQL语句即可查询该SQL的执行计划。
在执行计划中,通过key、key_len可以知道是否使用索引及使用的哪个索引。
通过Extra可以判断是否实现覆盖索引。
当Extra的值是Using index时表示使用了覆盖索引。

2、数据库索引的使用原则都有哪些?

1). 针对于数据量较大,且查询比较频繁的表建立索引。
2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
3). 尽量选择区分度高的列作为索引(性别字段不适合索引),尽量建立唯一索引,区分度越高,使用索引的效率越高。
4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

3、如何设计数据库表?

  1. 先理业务:明确业务场景、数据流转和访问频率,梳理实体(如用户、订单)及关系(一对一/一对多/多对多);
  2. 遵循范式:优先满足3NF(字段原子化、非主键字段依赖主键),避免冗余;特殊场景(如报表)可适度反范式(冗余字段)提升查询效率;
  3. 字段设计
  • 类型匹配(如手机号用char/varchar,不用int),长度最小化;
  • 必选字段设非空,默认值合理(如状态默认0);
  • 主键优先自增/雪花ID,避免业务字段当主键;
  • 核心字段加注释,便于维护;
  1. 约束与索引:加主键/外键/唯一约束保证数据完整性,按需建索引(参考索引使用原则);
  2. 扩展性:预留通用字段(如create_time/update_time/operator),大字段(如文本)拆分到子表;
  3. 性能考量:大表分库分表,冷热数据分离,避免单表数据量过大。

精简总结

先理业务定实体关系,遵3NF适度反范式,字段类型合理+约束齐全,按需索引,预留扩展,兼顾性能。

4、B树与B+树的区别

B树(B-tree)和B+树(B+ tree)是两种常见的自平衡树数据结构,通常应用于数据库索引的设计。它们在一些方面有相似之处,但也有一些重要的区别。
以下是B树和B+树的主要区别:
1. 节点结构:
- B树: B树的内部节点既包含键值,也包含子节点的指针。每个节点中的键值用于在节点之间进行搜索,子节点的指针用于导航。
- B+树: B+树的内部节点只包含键值,不包含实际的数据,只有叶子节点包含实际的数据和指向下一个叶子节点的指针。
2. 叶子节点:
- B树: B树的叶子节点存储实际的数据。
- B+树: B+树的叶子节点存储实际的数据,并且形成一个有序链表,便于范围查询。
3. 范围查询:
- B树: B树的内部节点和叶子节点都包含键值和数据,范围查询时可能需要遍历多个节点不适合范围查询。
- B+树: B+树的范围查询效率更高,因为范围查询只需要遍历叶子节点的有序链表。
4. 数据分布:
- B树: B树的数据分布在整棵树中,每个节点都包含一部分数据。
- B+树: B+树的数据只存储在叶子节点,内部节点只包含键值。这样的结构有助于提高磁盘读取效率,减少树的高度。
5. 查询性能:
- B树: B树在查找时可能需要跳跃多个节点,但由于节点中包含数据,有时可以在更早的阶段找到查询的结果。在mongodb中用的是B树,方便查询单条记录。
- B+树: B+树的查询性能通常比B树更好,特别是在范围查询时,因为只需要遍历叶子节点的链表。

5、数据库连接池工作原理是什么?

核心回答(简洁版)
- 核心逻辑:预先创建一定数量的数据库连接,存于 “池” 中,应用需连接时从池取、用完归还,避免频繁创建 / 销毁连接的性能开销;

  • 关键机制:
    (1)初始化:创建最小连接数,放在空闲队列;
    (2)申请连接:空闲则直接分配,无则判断是否达最大连接数,未达则新建,达则等待(超时抛异常);
    (3)归还连接:重置连接状态(如清空事务),放回空闲队列,而非关闭;
    维护:定期清理空闲超时的连接,补充最小连接数,保证连接可用性;
    (4)核心参数:最小空闲数、最大连接数、等待超时、空闲超时,控制连接池大小和连接生命周期。

  • 精简总结
    预创建连接存池,按需分配、用完归还,避免频繁创建销毁;通过核心参数管控连接数和生命周期,提升数据库访问效率。

6、在linux系统部署java项目时,常用的linux命令有哪些?

核心回答(简洁版)

1. 基础操作/文件管理

  • ls:查看目录文件(ls -l详单、ls -a含隐藏);
  • cd:切换目录(cd /usr/local);
  • pwd:显示当前路径;
  • mkdir/rmdir:创建/删除目录;
  • rm:删除文件/目录(rm -rf强制删除);
  • cp/mv:复制/移动文件;
  • cat/less/more:查看文件内容(大文件用less/more);
  • vim:编辑文件(vim app.yml)。

2. 进程/端口管理

  • ps:查看进程(ps -ef | grep java查Java进程);
  • jps:JDK自带,快速查Java进程(jps -l显主类);
  • kill:终止进程(kill -9 进程ID强制杀死);
  • netstat:查端口占用(netstat -tulpn | grep 8080);
  • ss:替代netstat,更高效(ss -tulpn | grep 8080)。

3. 日志/运行相关

  • tail:实时查看日志(tail -f app.log);
  • grep:过滤内容(grep "ERROR" app.log查错误日志);
  • nohup:后台运行Java程序(nohup java -jar app.jar &);
  • chmod:修改权限(chmod 755 start.sh);
  • df/du:查磁盘空间(df -h总览、du -sh目录大小)。

4. 压缩/解压

  • tar:打包解压(tar -zxvf app.tar.gz解压、tar -zcvf app.tar.gz app/打包)。

精简总结

部署核心:文件管理(ls/cd/vim)、进程端口(ps/jps/netstat/kill)、日志查看(tail/grep)、后台运行(nohup)、解压(tar),覆盖项目部署全流程。

7、Stream流的常用方法都有哪些?

核心回答(简洁版)

1. 中间操作(返回流,可链式调用)

  • filter(Predicate):过滤元素(如filter(user -> user.getAge()>18));
  • map(Function):元素转换(如map(User::getName)转字符串);
  • flatMap(Function):扁平化流(如拆分集合为单个元素);
  • sorted():自然排序/sorted(Comparator):自定义排序;
  • distinct():去重;
  • limit(long):限制返回元素数;
  • skip(long):跳过前N个元素。

2. 终止操作(结束流,返回结果)

  • forEach(Consumer):遍历元素(如forEach(System.out::println));
  • collect(Collector):收集结果(如Collectors.toList()/toMap()/groupingBy());
  • count():统计元素个数;
  • anyMatch(Predicate):任意元素匹配则返回true;
  • allMatch(Predicate):所有元素匹配则返回true;
  • noneMatch(Predicate):无元素匹配则返回true;
  • findFirst():获取第一个元素(返回Optional);
  • findAny():获取任意元素(返回Optional);
  • max(Comparator)/min(Comparator):获取最大/最小值(返回Optional);
  • reduce():归约(如求和reduce(0, Integer::sum))。

精简总结

中间操作:过滤(filter)、转换(map)、排序(sorted)、去重/限制/跳过;
终止操作:遍历(forEach)、收集(collect)、统计(count)、匹配(anyMatch)、取值(findFirst)、归约(reduce)。

8、MQ怎么避免重复消费?

核心回答(简洁版)

1. 核心思路

让消费逻辑**幂等**(多次消费结果一致)+ 消费端/服务端做**消费状态校验**,避免重复处理。

2. 具体方案

  • 幂等设计(核心)
  • 唯一标识:用消息唯一ID(如业务单号、MQ自带msgId),消费前查Redis/数据库是否已处理;
  • 数据库防重:基于唯一键/主键做INSERT(重复则报错),或UPDATE(先查后更加乐观锁);
  • 分布式锁:消费前用Redis/ZooKeeper加锁,锁释放前只处理一次。
  • MQ服务端机制

  • 确认机制:消费成功后才向MQ返回ACK(避免MQ重发),失败则重试(控制重试次数);
  • 消息过期:设置消息TTL,超期不再投递;
  • 顺序消费:部分MQ(如RocketMQ)支持顺序消费,减少乱序导致的重复感知。
  • 消费端控制

  • 限流重试:避免短时间大量重试引发重复;
  • 本地事务:重要业务结合本地消息表,保证“消费+业务操作”原子性。

精简总结

核心是消费逻辑幂等(唯一ID/数据库防重),配合消费成功ACK确认、控制重试次数,从根源避免重复消费的影响。

9、MQ怎么避免消息丢失?

核心回答(简洁版)

1. 生产端防丢失

  • 确认机制:开启MQ生产者确认(如Kafka的acks=all、RocketMQ的同步发送+发送结果回调),确保消息成功投递到MQ服务端;
  • 重试机制:生产失败时设置合理重试次数,避免网络抖动导致丢失;
  • 本地缓存/日志:关键消息先落本地表,投递成功后标记,失败则重发(本地消息表方案)。

2. 服务端防丢失

  • 持久化:开启MQ持久化(Kafka刷盘、RocketMQ/ RabbitMQ持久化到磁盘),避免MQ宕机丢失内存中的消息;
  • 集群部署:部署多副本/集群(如Kafka副本、RabbitMQ镜像队列),单个节点故障不丢消息;
  • 避免消息堆积:监控队列长度,及时扩容,防止服务端溢出丢消息。

3. 消费端防丢失

  • 手动ACK:关闭自动确认,消费业务逻辑执行完成后,再向MQ返回ACK(RabbitMQ的basic.ack、Kafka的手动提交offset);
  • 消费失败重试:消费异常时重试(控制次数),或死信队列兜底,避免直接丢弃;
  • 幂等消费:即使重试,也保证结果一致(兜底措施)。

精简总结

生产端确认+重试、服务端持久化+集群、消费端手动ACK+失败重试,三端联动堵住消息丢失环节。

10、服务器CPU持续飙高,排查方案与思路?

核心回答(简洁版)

1. 定位高CPU进程

  • top命令实时查看CPU占用(按P按CPU排序),找到PID最高的进程;
  • 若为Java进程,用jps确认进程,再用top -Hp <PID>查看进程内高CPU线程(按P排序);
  • 线程ID转16进制(printf "%x\n" <线程ID>),结合jstack <PID> | grep -A 20 <16进制线程ID>查看线程堆栈,定位代码块。

2. 分析进程/线程行为

  • Java进程
  • jstat -gcutil <PID> 1000查看GC情况(GC频繁/Full GC多会导致CPU高);
  • arthas工具(dashboard/thread命令)快速定位高CPU线程及关联代码;
  • 非Java进程
  • ps -mp <PID> -o THREAD,tid,time查看线程耗时;
  • 检查是否有死循环、频繁IO/计算、恶意进程。

3. 排查系统层面问题

  • vmstat/iostat查看系统负载、IO情况(IO瓶颈可能导致CPU等待升高);
  • 检查是否有定时任务(crontab -l)、脚本死循环、网络攻击(netstat -anp查异常连接);
  • 查看系统日志(/var/log/messages)、应用日志(如Java应用的error.log),排查异常报错。

4. 临时处理与根因修复

  • 临时:对非核心高CPU进程可临时kill -9(Java进程优先dump堆栈再处理),避免影响服务;
  • 根因:
  • 代码问题(死循环、频繁序列化/反射、无限递归):修复代码并重启;
  • GC问题:调整JVM参数(堆大小、垃圾收集器);
  • 系统/环境问题:升级硬件、优化配置、清理恶意进程。

精简总结

先定位高CPU进程→线程→代码/行为,再排查系统/GC/外部因素,临时止损后修复根因,结合工具(top/jstack/arthas)高效定位。

11、如何使用arthas工具排查服务器CPU飙高问题?

核心回答(简洁版)

1. 前提:安装&启动Arthas

  • 下载启动:curl -O https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar
  • 选择目标Java进程(输入进程编号,回车),进入Arthas交互界面。

2. 快速定位高CPU线程(核心步骤)

步骤1:全局看板看CPUTop线程

执行 dashboard,实时展示进程CPU/内存、线程占用,**按CPU列排序**,找到CPU占比最高的线程ID(TID)。

步骤2:精准定位高CPU线程及堆栈
  • 执行 thread -n 3:列出CPU占用Top3的线程,直接显示线程堆栈,定位到具体代码行(如死循环、频繁调用的方法);
  • 若需更详细:thread <线程ID>,查看该线程完整堆栈,明确代码入口。
步骤3:排查GC导致的CPU高

执行 jstat -gc <PID> 1000(Arthas内置该命令),查看GC次数/耗时:
- 若Young GC频繁(每秒多次)或Full GC持续触发,说明JVM堆配置不合理/内存泄漏,需结合 jmap 分析堆内存。

3. 进阶:追踪方法执行(定位耗时代码)

  • 若线程堆栈未直接定位,用 trace <类全限定名> <方法名>:追踪方法调用耗时,看是否有频繁调用/慢调用;
  • 示例:trace com.example.service.UserService queryUser,输出方法内各子调用耗时,定位瓶颈。

4. 收尾:退出Arthas

执行 stopquit,避免残留进程。

精简总结

Arthas排查核心:dashboard 找高CPU线程 → thread -n N 看堆栈定位代码 → (可选)trace 追踪方法耗时,快速定位死循环/GC异常/慢方法等CPU高根因。

12、java内存溢出怎么解决?

核心回答(简洁版)

1. 先定位溢出类型与原因

  • 核心工具
  • 启动参数加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump.hprof,溢出时自动生成堆快照;
  • jmap -dump:format=b,file=dump.hprof <PID>手动导出堆快照;
  • 用MAT/JProfiler分析快照,定位大对象、内存泄漏(如未释放的集合、线程池、静态引用)。
  • 常见溢出类型
  • 堆溢出(OOM: Heap space):对象创建过多/内存泄漏;
  • 栈溢出(StackOverflowError):递归过深/方法栈帧过大;
  • 元空间溢出(OOM: Metaspace):类加载过多(如动态代理、热部署);
  • 直接内存溢出:NIO直接内存使用过量,未释放。

2. 针对性解决

  • 堆溢出
  • 内存泄漏:修复代码(如关闭流/连接、清理无用集合/静态引用、避免ThreadLocal未移除);
  • 正常溢出:调大堆内存(-Xms/-Xmx),但需结合服务器内存,避免过度分配;
  • 栈溢出
  • 减少递归深度,优化递归为循环;调大栈大小(-Xss,如-Xss1m),不建议过度调大;
  • 元空间溢出:调大元空间(-XX:MetaspaceSize/-XX:MaxMetaspaceSize),清理无用类加载器;
  • 直接内存溢出:限制直接内存大小(-XX:MaxDirectMemorySize),确保NIO缓冲区及时释放。

3. 通用优化

  • 代码层面:避免创建大对象、频繁创建临时对象,使用对象池复用;
  • JVM层面:优化GC策略(如G1/ZGC),减少Full GC频率;
  • 监控层面:接入Prometheus/Grafana监控内存使用,提前预警。

精简总结

先通过堆快照(MAT/JProfiler)定位溢出类型(堆/栈/元空间)和泄漏点,内存泄漏修代码,正常溢出适度调大对应JVM内存参数,同时优化代码和GC策略。

13、MySQL的优化思路有哪些?

核心回答(简洁版)

1. 索引优化(核心)

  • 优先给where/order by/group by字段建索引,遵循最左前缀;
  • 避免索引失效(不做字段函数/运算、隐式类型转换);
  • 用覆盖索引减少回表,定期清理无用索引,优化碎片化索引。

2. SQL语句优化

  • 避免select *,只查需要字段;
  • 减少join层级、子查询(改用关联查询),避免%xxx模糊查询;
  • 控制in的元素数量,大数量用join替代;
  • explain分析执行计划,优化全表扫描、文件排序等低效操作。

3. 数据库结构优化

  • 遵循3NF减少冗余,高频查询场景适度反范式;
  • 大字段拆分到子表,大表分库分表(水平/垂直);
  • 冷热数据分离,归档历史数据,避免单表数据量过大。

4. 配置优化

  • 调整缓存(innodb_buffer_pool_size,建议设为物理内存60%-80%);
  • 优化连接数(max_connections)、锁等待超时(innodb_lock_wait_timeout);
  • 调整IO相关参数(如innodb_flush_log_at_trx_commit,平衡性能与数据安全)。

5. 架构优化

  • 主从复制:读写分离,主库写、从库读,分担压力;
  • 分库分表:应对海量数据,用中间件(如Sharding-JDBC);
  • 缓存层:Redis缓存热点数据,减少DB查询;
  • 集群部署:避免单点故障,提升并发能力。

6. 运维优化

  • 定期分析慢查询日志(开启slow_query_log),优化慢SQL;
  • 定期做EXPLAIN ANALYZE验证SQL执行效率;
  • 监控数据库负载、连接数、锁等待,及时扩容或调整。

精简总结

核心:索引+SQL优化;进阶:结构+配置调优;兜底:架构(读写分离/分库分表)+ 运维监控,从语句到架构分层优化。

14、MySQL 从几百万数据中分页查询,如何进行优化?

1.对查询列创建联合索引,采用覆盖索引先查询出id,再拿id查询聚集索引,.
2.考虑使用缓存来存储查询结果,避免频繁地访问数据库。
3.使用分页标识符(如记录的唯一 ID)来优化 LIMIT,每次查询记住上一次查询的最后一行的标识符,然后在下一次查询时使用 WHERE 条件 来获取下一页的数据。

15、MQ消息堆积应该怎么解决?

监控告警:第一时间发现堆积。
紧急扩容:增加消费者实例和并发度,快速止血。
检查队列数:确保队列数 >= 消费者数,否则扩容无效。
定位瓶颈:通过日志、监控分析是消费慢、生产快,还是中间件问题。
优化消费端:优化代码、数据库、调用链路。
控制生产端:必要时对生产端限流。
容量规划:根据业务量合理设置分区/队列数,预留缓冲区。
建立熔断与降级:保证系统韧性。

16、你项目中的难点是什么?

17、请做一个自我介绍

面试官您好,我是xxx。我老家是xxx的,毕业以后就一直从事java后端开发工作,到现在也有五年时间了。参与过ERP、MES、SAAS、电商等类型的项目。我最近做的项目是 xxx 。这个项目主要有xxx等模块,实现了。。。。功能。其中我负责的是。。。。。等功能的开发。这个项目的技术框架是SpringCloud微服务架构,微服务使用的是Springboot,持久层用的是。。。。。。。这是我的大概情况,您看您还想了解哪些情况?