文章目录
- 一、BigDecimal 底层实现与数据精确性
- 二、float 和 double 底层原理
- 三、分布式锁实现方式
- 四、JVM 内存模型(线程共享 vs 私有)
- 五、JVM 调优
- 六、volatile 关键字
- 七、JMM 内存模型与共享变量
- 八、反射获取类对象与实例
- 九、String、StringBuffer、StringBuilder 区别
- 十、SQL 执行顺序与 where/having 区别
- 十一、数据库连接类型
- 十二、Spring 事务原理
- 十三、Redis 分布式锁缺陷
- 十四、JVM 优化参数
- 十五、MVCC 核心机制
- 十六、Spring 注解注入对象
- 十七、线程安全问题与解决方案
- 十八、== 与 equals 区别
- 十九、跨域问题与解决
- 二十、常见非检查异常
- 二十一、Redis Zset 使用场景
- 二十二、订单支付后的智能推荐
- 二十三、代码性能优化方向
- 二十四、JDBC 与 MyBatis 流程
- 二十五、Git 冲突解决流程
- 二十六、HTTP 状态码分类
- 二十七、TCP 与 UDP 区别
- 二十八、Spring AOP 应用场景
- 二十九、常见数据结构
- 二十九、常见数据结构(续)
- 常见非检查异常
- XXL-JOB 用来做什么?
- 版本控制常用工具
- 为什么不把登录信息存入 Cookie,而是使用 Redis?
- 数据查询慢的排查
- SQL 调优方向
- 什么是跨域?如何实现跨域?
- `@Autowired` 和 `@Resource` 的区别
- `#{}` 和 `${}` 的区别
- 乐观锁和悲观锁
- Redis 实现分布式锁及缺陷
- 数据库三大范式
- JVM 优化参数
- 索引失效场景
- CAS(比较与交换)
- Nacos 配置文件存储位置
- 事务的隔离级别
- Redis Zset 的使用场景
- 缓存雪崩、击穿、穿透
- 开发文档维护与保存
- 登录自动续期实现
- 为什么要使用线程池和 `CompletableFuture` 异步编排
- JVM 内存结构
- 左连接(LEFT JOIN)查询
- Spring 常用注解(Controller 层)
- 多线程工作原理
- Git 常用命令及冲突解决
- Docker 打包和运行 JAR 镜像
- Nginx 的作用及负载均衡
- 百万级表连表查询优化
- RabbitMQ 的优点和缺点
- Oracle 与 MySQL 的区别
- 前端技术配合调试
- `static` 关键字
- 对集合的理解
- 线程池的核心参数和工作原理
- 多线程实现方式
- `synchronized` 和 `ReentrantLock` 的区别
- MyBatis 大致流程
- `char` 和 `varchar` 的区别
- 临时表
- Linux 常见命令
- Spring 注解放入 IOC 容器
- MyBatis 缓存问题
- 100 万个字符串查找
- Lua 脚本在 Redis 中的作用
- Redis Hash 在项目中的用法
- MyBatis 不用 `#{}` 防止 SQL 注入
- SQL 调优案例
- 数组快速排序
- T1/T2/T3线程顺序执行
- Redis与数据库一致性
- AOP切面应用场景
- 策略模式 vs 工厂模式
- Seata AT模式
- 异常与错误区别
- MongoDB vs Redis
- LRU缓存淘汰策略
- 分布式锁实现
- 适合存入Redis的数据
- G1垃圾回收算法
- CPU飙升排查
- OOM排查
- 事务传播行为
- 事务失效场景
- 获取类注解
- Linux后台进程命令
- 查找并删除文件
- 运行Java程序并设置内存
- **1. 不使用同步锁共享变量**
- **2. Spring Cloud 常用组件**
- **3. `@Transactional` 注解及事务使用**
- **4. Redis 实现签到功能**
- **5. Spring 线程池参数**
- **6. CompletableFuture 与线程池的区别**
- **7. 为什么要使用 CompletableFuture**
- **8. RabbitMQ 的几种模式**
- **9. 布隆过滤器初始容量设置**
- **10. 分布式事务实现方式**
- **11. 未知类型对象互相转换**
- **12. HashMap 扩容机制**
- **13. 多线程中子线程无法获取主线程 ID**
- **14. 内存泄漏定位与解决**
- **15. MinIO 的优点与分布式特性**
- **16. 相同表结构复制数据到新表**
- **Linux 常用命令**
- **聚簇索引 vs 非聚簇索引**
- **MySQL 存储引擎**
- **MySQL 调优**
- **索引失效场景**
- **事务隔离级别**
- **JVM 新生代 & 老年代**
- **对象头存储位置**
- **Redis 数据类型**
- **Redisson**
- **线程池 + 异步编排**
- **synchronized vs ReentrantLock**
- **volatile**
- **HashMap put 的底层原理**
- **MQ 的使用场景**
- **Spring DI & IOC**
- **BeanFactory vs FactoryBean**
- **循环依赖**
- **Spring 事务传播**
- **OpenFeign 负载均衡**
- **count(1) vs count(列名) vs count(*)**
- **JVM 调优**
- **AOP 注解**
- **Redis 持久化**
- **xxl-job**
- **@Async 异步**
- **Sentinel 熔断**
一、BigDecimal 底层实现与数据精确性
-
底层实现:
- 使用
int[] val
数组存储数字的每一位。 int scale
表示小数点位置(如scale=2
表示两位小数)。- 支持任意精度计算,避免浮点数精度丢失。
- 使用
-
数据精确性保障:
- 避免二进制浮点数误差(如
0.1 + 0.2 != 0.3
)。 - 提供
add
、subtract
、multiply
等精确运算方法。 - 可指定舍入模式(如
ROUND_HALF_UP
四舍五入)。
- 避免二进制浮点数误差(如
二、float 和 double 底层原理
- IEEE 754 标准:
float
:32位(1符号位 + 8指数位 + 23尾数位)。double
:64位(1符号位 + 11指数位 + 52尾数位)。
- 精度问题:
- 无法精确表示某些十进制小数(如
0.1
)。 - 适合科学计算,但金融场景需使用
BigDecimal
。
- 无法精确表示某些十进制小数(如
三、分布式锁实现方式
实现方式 | 原理 | 缺陷 |
---|---|---|
Redis SET NX | 原子性设置键值 | 需处理死锁、节点故障 |
ZooKeeper | 临时顺序节点 | 依赖第三方服务 |
数据库乐观锁 | 更新时判断版本号 | 性能较低 |
etcd | 分布式一致性存储 | 部署复杂 |
四、JVM 内存模型(线程共享 vs 私有)
区域 | 是否共享 | 说明 |
---|---|---|
方法区 | ✅ | 存储类元数据、常量池(JDK8后移至元空间) |
堆 | ✅ | 存放对象实例 |
虚拟机栈 | ❌ | 存储局部变量、方法调用栈 |
本地方法栈 | ❌ | 服务 Native 方法 |
程序计数器 | ❌ | 记录当前线程执行指令地址 |
五、JVM 调优
- 参数调整:
-Xms/-Xmx
:设置堆内存初始/最大值。-XX:NewSize/-XX:MaxNewSize
:新生代大小。-XX:+UseG1GC
:启用 G1 垃圾回收器。
- 监控工具:
jstat
:查看 GC 状态。jmap
:生成堆转储文件。VisualVM
:可视化内存和线程分析。
六、volatile 关键字
- 特性:
- 内存可见性:写入后立即刷新到主内存。
- 禁止指令重排序:防止编译器优化。
- 适用场景:状态标志、DCL(双重检查锁定)。
七、JMM 内存模型与共享变量
- JMM 模型:
- 主内存:所有线程共享的变量存储。
- 工作内存:线程私有的缓存副本。
- 关联关系:
- 共享变量(如
static
字段)在主内存中存储。 - 线程操作变量需从主内存读取并写回。
- 共享变量(如
八、反射获取类对象与实例
// 获取类对象
Class<?> clazz = Class.forName("com.example.MyClass");
// 获取实例
Object instance = clazz.getDeclaredConstructor().newInstance();
九、String、StringBuffer、StringBuilder 区别
类型 | 线程安全 | 可变性 | 性能 |
---|---|---|---|
String | ❌ | 不可变 | 最低 |
StringBuffer | ✅ | 可变 | 中等 |
StringBuilder | ❌ | 可变 | 最高 |
十、SQL 执行顺序与 where/having 区别
- 执行顺序:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
- where vs having:
where
:过滤行(作用于原始数据)。having
:过滤分组(作用于聚合结果)。
十一、数据库连接类型
类型 | 说明 | 示例 |
---|---|---|
内连接 (INNER JOIN) | 返回匹配行 | SELECT * FROM A INNER JOIN B ON A.id = B.id |
左外连接 (LEFT JOIN) | 返回左表全量,右表匹配行 | SELECT * FROM A LEFT JOIN B ON A.id = B.id |
右外连接 (RIGHT JOIN) | 返回右表全量,左表匹配行 | SELECT * FROM A RIGHT JOIN B ON A.id = B.id |
十二、Spring 事务原理
- 基于 AOP 实现:
- 通过
@Transactional
注解标记事务边界。 - 底层使用
TransactionInterceptor
拦截方法调用。
- 通过
- 事务管理器:
PlatformTransactionManager
接口实现(如DataSourceTransactionManager
)。
十三、Redis 分布式锁缺陷
- 问题:
- 死锁:未设置过期时间。
- 节点故障:主从切换导致锁失效。
- 改进方案:
- 使用
RedLock
算法(多节点加锁)。 - Redisson 客户端封装分布式锁。
- 使用
十四、JVM 优化参数
参数 | 说明 |
---|---|
-Xms | 初始堆内存 |
-Xmx | 最大堆内存 |
-XX:+UseG1GC | 使用 G1 垃圾回收器 |
-XX:MaxMetaspaceSize | 设置元空间最大值 |
十五、MVCC 核心机制
- 多版本并发控制:
- 每个事务看到的数据快照不同(通过
Read View
)。 - 通过
undo log
保存历史版本数据。
- 每个事务看到的数据快照不同(通过
- 应用场景:InnoDB 引擎的可重复读隔离级别。
十六、Spring 注解注入对象
注解 | 作用 |
---|---|
@Component | 自动扫描注册 Bean |
@Service | 标识业务层组件 |
@Repository | 标识数据访问层组件 |
@Configuration | 标识配置类 |
@Bean | 在配置类中定义 Bean |
十七、线程安全问题与解决方案
- 产生原因:多个线程同时修改共享资源。
- 解决方式:
- 锁机制:
synchronized
、ReentrantLock
。 - 原子类:
AtomicInteger
、AtomicReference
。 - 不可变对象:
final
字段、String
。
- 锁机制:
十八、== 与 equals 区别
比较方式 | 说明 |
---|---|
== | 比较对象引用地址(或基本类型值) |
equals | 默认比较引用地址(可重写为内容比较,如 String ) |
十九、跨域问题与解决
- 原因:浏览器的同源策略限制。
- 解决方式:
- CORS:服务器设置响应头(如
Access-Control-Allow-Origin
)。 - JSONP:仅支持 GET 请求。
- Nginx 反向代理:将请求转发到同源地址。
- CORS:服务器设置响应头(如
二十、常见非检查异常
异常 | 说明 |
---|---|
NullPointerException | 空指针访问 |
ArrayIndexOutOfBoundsException | 数组越界 |
NumberFormatException | 类型转换失败 |
ConcurrentModificationException | 集合迭代时被修改 |
二十一、Redis Zset 使用场景
- 典型应用:
- 排行榜(如游戏积分排名)。
- 时间序列数据(如消息队列按时间排序)。
- 底层结构:跳跃表 + 哈希表,支持 O(log N) 插入/查询。
二十二、订单支付后的智能推荐
- Redis 应用:
- Zset:存储用户购买历史的评分。
- Hash:存储商品特征标签。
- Stream:实时推送推荐结果。
二十三、代码性能优化方向
- 算法优化:选择更高效的算法(如归并排序 vs 冒泡排序)。
- 减少 IO:批量读写、使用缓存。
- 并发编程:合理使用线程池、异步编排。
- 内存管理:避免内存泄漏、对象复用。
二十四、JDBC 与 MyBatis 流程
- JDBC:
- 加载驱动 → 建立连接 → 创建 Statement → 执行 SQL → 处理结果。
- MyBatis:
- 解析 XML/注解 → 生成 Mapper 接口 → 动态代理执行 SQL → 映射结果集。
二十五、Git 冲突解决流程
- 拉取最新代码:
git pull origin dev
。 - 解决冲突:手动编辑冲突文件(标记为
<<<<<<<
,=======
,>>>>>>>
)。 - 提交合并结果:
git commit
。
二十六、HTTP 状态码分类
类别 | 说明 | 常见状态码 |
---|---|---|
1xx | 信息性 | 100 Continue |
2xx | 成功 | 200 OK |
3xx | 重定向 | 301 Moved Permanently |
4xx | 客户端错误 | 404 Not Found |
5xx | 服务器错误 | 500 Internal Server Error |
二十七、TCP 与 UDP 区别
特性 | TCP | UDP |
---|---|---|
面向连接 | ✅ | ❌ |
可靠传输 | ✅ | ❌ |
有序性 | ✅ | ❌ |
适用场景 | 文件传输、网页浏览 | 视频通话、DNS 查询 |
二十八、Spring AOP 应用场景
场景 | 示例 |
---|---|
日志记录 | 方法调用日志 |
权限校验 | 拦截敏感操作 |
事务管理 | 控制事务边界 |
性能监控 | 统计方法执行时间 |
二十九、常见数据结构
数据结构 | 特点 | 应用场景 |
---|---|---|
数组 | 连续存储,随机访问快 | 缓存、固定大小数据 |
链表 | 动态扩容,插入删除快 | LRU 缓存 |
哈希表 | O(1) 查找 | 字典、缓存 |
二叉树 | 层次结构 | 文件系统、搜索树 |
二十九、常见数据结构(续)
数据结构 | 特点 | 应用场景 |
---|---|---|
栈 | 后进先出(LIFO)原则 | 表达式求值和语法解析、撤销操作 |
队列 | 先进先出(FIFO)原则 | 任务调度、消息队列 |
堆 | 完全二叉树的一种形式,分为最大堆和最小堆 | 实现优先级队列、图算法如Dijkstra’s shortest path |
图 | 节点通过边连接形成的集合 | 社交网络分析、路由算法 |
Trie(前缀树) | 字典树,用于快速检索字符串 | 自动完成、拼写检查 |
B树/B+树 | 平衡多路搜索树,适用于磁盘存储 | 数据库索引、文件系统 |
常见非检查异常
常见的非检查异常包括:
- NullPointerException:尝试访问一个
null
对象的方法或属性。 - ArrayIndexOutOfBoundsException:数组下标越界。
- ArithmeticException:算术错误(如除以零)。
- ClassCastException:类型转换错误。
- IllegalArgumentException:非法参数。
- NumberFormatException:字符串转换为数字时格式错误。
示例代码:
public class NonCheckedExceptionExample {
public static void main(String[] args) {
// NullPointerException
String str = null;
System.out.println(str.length()); // 抛出 NullPointerException
// ArrayIndexOutOfBoundsException
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // 抛出 ArrayIndexOutOfBoundsException
// ArithmeticException
int result = 10 / 0; // 抛出 ArithmeticException
}
}
XXL-JOB 用来做什么?
XXL-JOB 是一个分布式任务调度平台,主要用于:
- 分布式任务管理:支持任务在多个节点上调度和执行。
- 任务动态配置:无需重启服务即可修改任务参数。
- 任务失败重试:支持任务失败后的重试策略。
- 可视化界面:提供 Web 管理界面,方便监控和管理任务。
为什么选择 XXL-JOB 而不是 Spring 自带的调度:
- 分布式支持:Spring 自带的
@Scheduled
不适合分布式环境。 - 动态扩展:XXL-JOB 支持动态添加/删除任务,而 Spring 需要重启服务。
- 高可用性:XXL-JOB 提供任务失败重试和故障转移机制。
版本控制常用工具
常用工具:
- Git:分布式版本控制系统,支持分支管理、代码协作。
- SVN(Subversion):集中式版本控制系统,适合中小型团队。
- Mercurial:轻量级分布式工具,适合快速开发。
- TFS(Team Foundation Server):微软的版本控制和项目管理工具。
Git 常用命令:
git init
:初始化仓库。git add .
:添加所有文件到暂存区。git commit -m "message"
:提交更改。git push
:推送代码到远程仓库。git pull
:拉取远程仓库的最新代码。git merge
:合并分支。git branch
:管理分支。
解决冲突:
- 使用
git merge
合并分支时,若发生冲突,Git 会标记冲突文件。 - 手动编辑冲突文件,保留需要的代码。
- 使用
git add <file>
标记冲突已解决。 - 最后执行
git commit
完成合并。
为什么不把登录信息存入 Cookie,而是使用 Redis?
原因:
- 安全性:Cookie 存储在客户端,易被窃取;Redis 存储在服务端,更安全。
- 存储容量:Cookie 有大小限制(通常 4KB),而 Redis 可存储更大数据。
- 会话共享:在分布式系统中,Cookie 无法跨服务共享,而 Redis 可作为集中式会话存储。
- 自动续期:Redis 可通过设置过期时间(TTL)和刷新机制实现会话自动续期。
实现登录自动续期:
- 用户登录后,生成 Token 并存入 Redis,设置较短的过期时间。
- 每次请求时,若 Token 有效,则刷新其过期时间。
- 使用
EXPIRE
或PEXPIRE
命令更新 Redis 中的 Token 过期时间。
数据查询慢的排查
排查步骤:
- 查看执行计划:使用
EXPLAIN
分析 SQL 查询的执行路径。 - 索引优化:确保查询字段有合适的索引。
- 减少 JOIN:避免不必要的表连接。
- 分页优化:使用
LIMIT
和OFFSET
,但注意偏移量过大时的性能问题。 - 数据库配置:调整数据库连接池、缓存参数。
- 硬件资源:检查服务器 CPU、内存、磁盘 I/O 是否充足。
SQL 调优方向
- 索引优化:
- 添加缺失的索引。
- 避免过度索引(索引越多,写入性能越低)。
- 查询优化:
- 避免
SELECT *
,只查询需要的字段。 - 使用
WHERE
子句缩小查询范围。
- 避免
- 表结构优化:
- 拆分大表为小表(垂直分表)。
- 使用分区表(Partitioning)。
- 缓存:
- 使用 Redis 缓存高频查询结果。
- 使用数据库的查询缓存(如 MySQL 的 Query Cache)。
- 锁优化:
- 减少事务持有锁的时间。
- 使用乐观锁或悲观锁策略。
什么是跨域?如何实现跨域?
跨域(Cross-Origin)是指浏览器出于安全考虑,阻止网页从一个不同源(协议、域名、端口)请求资源。
实现跨域的方法:
-
CORS(Cross-Origin Resource Sharing):
- 在服务器响应头中添加
Access-Control-Allow-Origin
。 - 支持预检请求(Preflight Request)。
- 示例:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST
- 在服务器响应头中添加
-
JSONP(JSON with Padding):
- 通过
<script>
标签加载跨域资源,但仅支持 GET 请求。
- 通过
-
代理服务器:
- 前端请求同源的代理服务器,由代理服务器转发请求到目标服务器。
@Autowired
和 @Resource
的区别
特性 | @Autowired | @Resource |
---|---|---|
来源 | Spring 框架 | JSR-250 标准 |
注入方式 | 按类型注入 | 按名称注入(默认) |
适用场景 | 适用于 Spring 项目 | 适用于 Java EE 项目 |
必须配合 @Component | 是 | 否 |
示例:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Resource(name = "userDao")
private UserDao userDao;
}
#{}
和 ${}
的区别
在 MyBatis 中:
#{}
:预编译占位符,防止 SQL 注入,适用于参数绑定。${}
:字符串替换,直接拼接 SQL,可能导致 SQL 注入,适用于动态表名或列名。
示例:
<!-- 安全:使用 #{} -->
<select id="getUser" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 风险:使用 ${} -->
<select id="getUserByColumn" resultType="User">
SELECT * FROM users WHERE ${column} = #{value}
</select>
乐观锁和悲观锁
乐观锁:
- 假设数据不会冲突,提交时检查版本号或时间戳。
- 适用于读多写少的场景。
- 实现方式:使用版本号(
version
字段)或时间戳。
悲观锁:
- 假设数据会冲突,操作前加锁。
- 适用于写多读少的场景。
- 实现方式:使用
SELECT ... FOR UPDATE
或数据库锁机制。
示例(乐观锁):
UPDATE users SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = #{version};
Redis 实现分布式锁及缺陷
实现方式:
-
SETNX 命令:
SETNX lock_key 1
- 如果
lock_key
不存在,则设置成功,表示获取锁。
- 如果
-
RedLock 算法:
- 在多个 Redis 实例上尝试加锁,确保一致性。
缺陷:
- 网络分区:如果 Redis 节点不可达,可能无法释放锁。
- 死锁:如果客户端异常退出,锁未释放。
- 性能开销:频繁的锁操作可能影响性能。
改进方案:
- 使用
Redisson
等客户端库,自动处理锁的续期和释放。 - 设置合理的锁过期时间(TTL)。
数据库三大范式
-
第一范式(1NF):
- 属性不可再分。
- 示例:拆分
address
字段为street
,city
,state
。
-
第二范式(2NF):
- 在 1NF 基础上,消除部分依赖。
- 要求所有非主属性完全依赖主键。
-
第三范式(3NF):
- 在 2NF 基础上,消除传递依赖。
- 非主属性之间不能相互依赖。
JVM 优化参数
常用参数:
- 堆内存:
-Xms
:初始堆大小。-Xmx
:最大堆大小。
- 新生代:
-Xmn
:新生代大小。-XX:SurvivorRatio
:Eden 区与 Survivor 区的比例。
- GC 策略:
-XX:+UseG1GC
:启用 G1 垃圾收集器。-XX:+UseParallelGC
:启用并行垃圾收集器。
示例:
java -Xms512m -Xmx2g -XX:+UseG1GC -jar myapp.jar
索引失效场景
-
全模糊查询:
SELECT * FROM users WHERE name LIKE '%Tom%';
-
类型不匹配:
SELECT * FROM users WHERE id = '1'; -- id 是整数
-
使用函数:
SELECT * FROM users WHERE YEAR(create_time) = 2023;
-
OR 条件:
SELECT * FROM users WHERE id = 1 OR name = 'Tom';
CAS(比较与交换)
原理:
CAS 是一种无锁算法,通过比较当前值与预期值,若相等则更新为新值。
Java 中的实现:
AtomicInteger atomicInt = new AtomicInteger(0);
int expectedValue = atomicInt.get();
boolean success = atomicInt.compareAndSet(expectedValue, expectedValue + 1);
优点:
- 避免线程阻塞,提高并发性能。
- 适用于低竞争场景。
缺点:
- ABA 问题:值被修改后又改回原值,CAS 会误判。
- 高竞争场景下,CAS 可能导致循环开销。
Nacos 配置文件存储位置
Nacos 配置存储:
-
本地文件:
- 默认存储在
conf
目录下的application.properties
文件中。
- 默认存储在
-
数据库:
- 可配置使用 MySQL 存储配置信息,需在
application.properties
中指定数据库连接。
- 可配置使用 MySQL 存储配置信息,需在
-
云存储:
- 支持与阿里云、AWS 等云服务集成,存储配置信息。
事务的隔离级别
隔离级别 | 描述 | 问题 |
---|---|---|
Read Uncommitted | 允许读取未提交的数据 | 脏读 |
Read Committed | 只能读取已提交的数据 | 不可重复读 |
Repeatable Read | 确保多次读取结果一致 | 幻读 |
Serializable | 完全隔离,性能最差 | 无并发问题 |
Redis Zset 的使用场景
Zset(有序集合) 是 Redis 提供的一种数据结构,支持按分数排序。
应用场景:
-
排行榜:
ZADD leaderboard 100 user1 ZRANGE leaderboard 0 -1 WITHSCORES
-
延迟队列:
- 使用
ZRANGEBYSCORE
获取超时任务。
- 使用
-
去重计数:
- 结合
ZCARD
统计唯一用户。
- 结合
缓存雪崩、击穿、穿透
-
缓存雪崩:
- 现象:大量缓存同时失效,导致数据库压力激增。
- 解决方案:
- 随机过期时间。
- 热点数据永不过期。
-
缓存击穿:
- 现象:某个热点 Key 失效,大量请求直接访问数据库。
- 解决方案:
- 使用互斥锁(Mutex)。
- 使用空值缓存。
-
缓存穿透:
- 现象:请求的 Key 不存在,直接访问数据库。
- 解决方案:
- 使用布隆过滤器(Bloom Filter)。
- 缓存空值。
开发文档维护与保存
维护方式:
-
工具:
- Swagger:自动生成 API 文档。
- Markdown:编写技术文档。
- GitBook:构建在线文档。
-
版本控制:
- 将文档纳入 Git 管理,确保历史版本可追溯。
- 使用分支管理文档的开发和发布。
-
存储:
- 私有仓库:使用 GitLab、GitHub Enterprise 存储敏感文档。
- 云存储:使用 Notion、Confluence 等在线文档工具。
登录自动续期实现
实现步骤:
-
生成 Token:
- 用户登录后生成 JWT 或 UUID 作为 Token。
- 设置较短的过期时间(如 30 分钟)。
-
刷新 Token:
- 每次请求时,若 Token 有效,则生成新的 Token 并更新 Redis 中的过期时间。
-
存储 Token:
- 使用 Redis 存储 Token,设置 TTL(Time To Live)。
代码示例:
public String refreshToken(String oldToken) {
String newToken = UUID.randomUUID().toString();
redisTemplate.opsForValue().set("token:" + newToken, "user123", 30, TimeUnit.MINUTES);
return newToken;
}
为什么要使用线程池和 CompletableFuture
异步编排
线程池:
- 优点:
- 提高资源利用率,避免频繁创建和销毁线程。
- 控制并发线程数,防止资源耗尽。
- 核心参数:
corePoolSize
:核心线程数。maxPoolSize
:最大线程数。keepAliveTime
:空闲线程存活时间。workQueue
:任务队列。
CompletableFuture:
- 优点:
- 支持异步编排,简化多线程任务的组合。
- 提供
thenApply
、thenAccept
、thenRun
等方法链式调用。
- 示例:
CompletableFuture.supplyAsync(() -> "Hello") .thenApply(s -> s + " World") .thenAccept(System.out::println);
JVM 内存结构
内存区域:
-
堆(Heap):
- 存储对象实例和数组。
- 分为年轻代(Young Generation)和老年代(Old Generation)。
-
栈(Stack):
- 存储局部变量和方法调用。
- 每个线程私有。
-
方法区(Metaspace):
- 存储类信息、常量池、静态变量。
- Java 8 以后使用 Metaspace 替代永久代(PermGen)。
-
程序计数器:
- 记录当前线程执行的字节码行号。
左连接(LEFT JOIN)查询
定义:
左连接返回左表的所有记录,即使右表没有匹配的行。
示例:
SELECT users.id, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
查询事务隔离级别:
SELECT @@transaction_isolation;
Spring 常用注解(Controller 层)
注解 | 作用 |
---|---|
@RestController | 标记为 RESTful 控制器,返回值直接写入 HTTP 响应体。 |
@RequestMapping | 映射 HTTP 请求到方法。 |
@GetMapping / @PostMapping | 映射 GET/POST 请求。 |
@PathVariable | 从 URL 中提取变量。 |
@RequestBody | 接收 JSON/XML 请求体。 |
@RequestParam | 从查询参数中获取值。 |
@Autowired | 自动注入依赖。 |
多线程工作原理
线程生命周期:
- 新建(New):创建线程对象。
- 就绪(Runnable):等待 CPU 调度。
- 运行(Running):执行
run()
方法。 - 阻塞(Blocked):等待资源(如锁)。
- 终止(Terminated):执行完毕或异常退出。
上下文切换:
- 当线程阻塞或时间片用尽时,CPU 会切换到其他线程,保存当前线程的状态,并恢复下一个线程的状态。
Git 常用命令及冲突解决
常用命令:
git clone <url>
:克隆远程仓库。git add .
:添加所有修改到暂存区。git commit -m "message"
:提交更改。git push
:推送代码到远程仓库。git pull
:拉取远程仓库的最新代码。git branch
:查看分支。git checkout -b <branch>
:创建并切换分支。git merge <branch>
:合并分支。
解决冲突:
- 执行
git merge
后,Git 会标记冲突文件。 - 打开冲突文件,找到
<<<<<<<
,=======
,>>>>>>>
标记。 - 手动选择保留的代码。
- 使用
git add <file>
标记冲突已解决。 - 最后执行
git commit
完成合并。
Docker 打包和运行 JAR 镜像
步骤:
-
编写 Dockerfile:
FROM openjdk:8-jdk-alpine COPY myapp.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"]
-
构建镜像:
docker build -t myapp .
-
运行容器:
docker run -d -p 8080:8080 myapp
对 Docker 容器做存储:
-
数据卷(Volume):
docker run -v /host/path:/container/path myapp
-
绑定挂载(Bind Mount):
docker run -v /host/path:/container/path myapp
Nginx 的作用及负载均衡
Nginx 的作用:
- 反向代理:将客户端请求转发到后端服务器。
- 负载均衡:将流量分配到多个后端服务器。
- 静态资源服务:高效提供静态文件(HTML、CSS、JS)。
- 限流和缓存:限制请求频率,缓存响应内容。
负载均衡配置示例:
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
百万级表连表查询优化
优化策略:
-
索引优化:
- 为主键和外键添加索引。
- 使用复合索引覆盖查询条件。
-
分页处理:
- 使用
LIMIT
和OFFSET
,但注意偏移量过大时的性能问题。 - 使用游标分页(Cursor-based Pagination)。
- 使用
-
缓存:
- 使用 Redis 缓存高频查询结果。
- 使用数据库的查询缓存(如 MySQL 的 Query Cache)。
-
数据库分片:
- 将大表水平拆分到多个数据库实例中。
-
减少 JOIN:
- 避免不必要的表连接,或使用物化视图。
RabbitMQ 的优点和缺点
优点:
- 高可用性:支持集群部署,确保消息不丢失。
- 消息持久化:消息可持久化到磁盘,避免重启丢失。
- 灵活的路由:支持多种消息模式(Direct、Topic、Fanout)。
- 分布式系统解耦:生产者和消费者无需直接通信。
缺点:
- 性能瓶颈:在高吞吐量场景下,可能成为性能瓶颈。
- 消息堆积:消费者处理速度慢时,可能导致消息堆积。
- 运维复杂度:需要维护 RabbitMQ 集群和监控系统。
主要作用:
- 异步处理:解耦生产者和消费者。
- 流量削峰:平滑突发流量。
- 消息广播:向多个消费者发送消息。
Oracle 与 MySQL 的区别
特性 | Oracle | MySQL |
---|---|---|
事务处理 | 支持 ACID,适合金融系统 | 支持 ACID,但默认引擎 InnoDB |
锁机制 | 更复杂的锁粒度(行锁、表锁) | 行锁为主 |
性能 | 高性能,适合大型企业应用 | 轻量级,适合中小型应用 |
查询语法 | 支持 PL/SQL,语法复杂 | 支持标准 SQL,语法简单 |
存储过程 | 强大的存储过程支持 | 有限的存储过程支持 |
查询区别:
-
分页:
- Oracle:使用
ROWNUM
。 - MySQL:使用
LIMIT
。
- Oracle:使用
-
字符串连接:
- Oracle:使用
||
。 - MySQL:使用
CONCAT()
。
- Oracle:使用
前端技术配合调试
定位前端错误的步骤:
-
查看控制台(Console):
- 检查报错信息(如
Uncaught TypeError
)。 - 查看网络请求(Network Tab)。
- 检查报错信息(如
-
使用断点调试:
- 在 Chrome DevTools 中设置断点,逐步执行代码。
-
检查网络请求:
- 查看请求的 URL、参数、响应状态码。
- 检查 CORS 错误或 404/500 错误。
-
代码审查:
- 检查 JavaScript 语法错误。
- 检查 HTML/CSS 是否正确嵌套。
static
关键字
可以修饰的对象:
- 变量:类变量,属于类,所有实例共享。
- 方法:类方法,可以直接通过类名调用。
- 代码块:静态代码块,用于初始化类。
- 内部类:静态内部类,不依赖外部类实例。
特性:
- 静态变量:在类加载时初始化,生命周期与类相同。
- 静态方法:不能访问非静态成员,不能使用
this
。 - 静态代码块:只执行一次,用于初始化静态资源。
示例:
public class Example {
static int count = 0;
static {
System.out.println("Static block executed");
}
public static void main(String[] args) {
System.out.println(count);
}
}
对集合的理解
Java 集合框架:
类型 | 接口 | 实现类 | 特点 |
---|---|---|---|
List | List | ArrayList , LinkedList | 有序,允许重复 |
Set | Set | HashSet , TreeSet | 无序,不允许重复 |
Map | Map | HashMap , TreeMap | 键值对,键唯一 |
线程安全集合:
ConcurrentHashMap
:线程安全的 HashMap。CopyOnWriteArrayList
:线程安全的 List,适合读多写少的场景。
线程池的核心参数和工作原理
核心参数:
- corePoolSize:核心线程数。
- maximumPoolSize:最大线程数。
- keepAliveTime:空闲线程存活时间。
- workQueue:任务队列。
- threadFactory:线程工厂。
- handler:拒绝策略。
工作原理:
- 提交任务:
- 如果线程数 < corePoolSize,创建新线程。
- 如果线程数 >= corePoolSize 且队列未满,将任务加入队列。
- 如果队列满且线程数 < maximumPoolSize,创建新线程。
- 如果队列满且线程数 >= maximumPoolSize,执行拒绝策略。
线程安全性:
- 线程池本身是线程安全的,但任务中的共享资源需要手动同步。
多线程实现方式
-
继承
Thread
类:public class MyThread extends Thread { public void run() { System.out.println("Thread running"); } }
-
实现
Runnable
接口:public class MyRunnable implements Runnable { public void run() { System.out.println("Runnable running"); } }
-
使用
Callable
和Future
:public class MyCallable implements Callable<String> { public String call() { return "Result"; } }
synchronized
和 ReentrantLock
的区别
特性 | synchronized | ReentrantLock |
---|---|---|
可重入性 | 支持 | 支持 |
公平性 | 不支持 | 支持(可选) |
条件变量 | 不支持 | 支持(Condition ) |
中断响应 | 不支持 | 支持(tryLock() ) |
性能 | 低竞争下性能较好 | 高竞争下性能更好 |
示例:
// synchronized
synchronized (this) {
// critical section
}
// ReentrantLock
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}
MyBatis 大致流程
-
加载配置:
- 读取
mybatis-config.xml
和映射文件。 - 构建
SqlSessionFactory
。
- 读取
-
创建会话:
- 通过
SqlSessionFactory
创建SqlSession
。 SqlSession
提供数据库操作方法(select
,insert
等)。
- 通过
-
执行 SQL:
- 根据 Mapper 接口或 XML 映射文件生成 SQL。
- 参数绑定和结果映射。
-
返回结果:
- 将数据库结果集映射为 Java 对象。
char
和 varchar
的区别
类型 | 存储方式 | 空间占用 | 适用场景 |
---|---|---|---|
char(n) | 定长 | 始终占用 n 个字符空间 | 固定长度字符串,如性别 |
varchar(n) | 变长 | 实际存储长度 + 1~2 字节 | 可变长度字符串,如用户名 |
临时表
临时表 是数据库中用于临时存储中间结果的表,通常在会话结束后自动删除。
使用场景:
-
复杂查询:
- 将中间结果存储在临时表中,简化复杂查询。
- 示例:
CREATE TEMPORARY TABLE temp_users AS SELECT * FROM users WHERE status = 'active';
-
性能优化:
- 对临时表进行索引优化,提高查询性能。
Linux 常见命令
命令 | 功能 |
---|---|
ls | 列出目录内容 |
cd | 切换目录 |
mkdir | 创建目录 |
rm | 删除文件或目录 |
cp | 复制文件 |
mv | 移动或重命名文件 |
ps | 查看进程 |
top | 实时监控系统资源 |
grep | 查找文本 |
find | 查找文件 |
Spring 注解放入 IOC 容器
常用注解:
注解 | 作用 |
---|---|
@Component | 通用组件注解 |
@Service | 标记为业务层组件 |
@Repository | 标记为数据访问层组件 |
@Controller | 标记为控制层组件 |
@Configuration | 标记为配置类 |
@Bean | 在配置类中定义 Bean |
MyBatis 缓存问题
一级缓存:
- 范围:
SqlSession
级别。 - 特点:默认开启,缓存查询结果,避免重复查询。
二级缓存:
- 范围:
Mapper
级别。 - 特点:需要手动配置,适合读多写少的场景。
问题:
- 数据一致性:缓存可能导致数据不一致。
- 内存占用:缓存数据过多可能导致内存溢出。
100 万个字符串查找
推荐数据结构:
-
哈希表(HashMap):
- 时间复杂度 O(1),适合查找和插入。
- 适用于无序数据。
-
Trie 树:
- 适合前缀匹配和字符串查找。
- 适用于自动补全、拼写检查。
-
布隆过滤器(Bloom Filter):
- 用于判断元素是否存在于集合中。
- 优点:空间效率高,查询速度快。
- 缺点:存在误判率。
Lua 脚本在 Redis 中的作用
作用:
-
原子操作:
- Lua 脚本在 Redis 中是原子执行的,避免竞态条件。
-
复杂逻辑:
- 实现复杂的业务逻辑,如分布式锁、计数器。
示例:
-- 实现计数器
local current = redis.call("GET", KEYS[1])
if current then
current = tonumber(current)
current = current + 1
redis.call("SET", KEYS[1], current)
return current
else
redis.call("SET", KEYS[1], 1)
return 1
end
Redis Hash 在项目中的用法
应用场景:
-
存储对象:
- 将用户信息存储为 Hash,字段对应属性。
- 示例:
HSET user:1001 name "Alice" age 25
-
减少内存占用:
- Hash 结构比多个 String 更节省内存。
-
批量操作:
- 使用
HMSET
、HMGET
批量设置和获取字段。
- 使用
MyBatis 不用 #{}
防止 SQL 注入
方法:
-
使用拦截器(Interceptor):
- 自定义拦截器,过滤 SQL 中的危险字符。
-
使用 AOP:
- 在 Service 层使用 AOP 拦截 SQL 参数,进行合法性校验。
-
白名单校验:
- 对输入参数进行白名单校验,拒绝非法字符。
SQL 调优案例
案例 1:添加索引
- 问题:查询用户表时,
WHERE username = 'Tom'
很慢。 - 优化:为
username
字段添加索引。
案例 2:减少 JOIN
- 问题:查询涉及多个表连接,导致性能下降。
- 优化:将部分数据冗余到主表,减少 JOIN。
案例 3:分页优化
- 问题:分页查询时,
LIMIT 10000, 10
很慢。 - 优化:使用游标分页(Cursor-based Pagination)。
# 技术问题整理笔记
## Java实现阻塞队列
- **核心实现**:基于数组或链表的循环队列,结合线程同步机制(如`ReentrantLock`和`Condition`)。
- **关键组件**:
- 数组/链表存储元素
- `head`和`tail`指针管理队列头尾
- `count`计数器记录队列大小
- **线程安全**:
- 使用`put`和`take`方法实现阻塞操作
- `ArrayBlockingQueue`通过`ReentrantLock`控制并发,支持公平锁
- **示例代码**(伪代码):
```java
public class MyBlockingQueue<T> {
private final T[] items;
private int head, tail, count;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
public MyBlockingQueue(int capacity) {
items = (T[]) new Object[capacity];
}
public void put(T elem) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await();
}
items[tail] = elem;
tail = (tail + 1) % items.length;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
T elem = items[head];
items[head] = null;
head = (head + 1) % items.length;
count--;
notFull.signal();
return elem;
} finally {
lock.unlock();
}
}
}
数组快速排序
- PHP实现:
function quickSort($arr) { if (count($arr) <= 1) return $arr; $pivot = $arr[0]; $left = $right = []; for ($i = 1; $i < count($arr); $i++) { if ($arr[$i] < $pivot) $left[] = $arr[$i]; else $right[] = $arr[$i]; } return array_merge(quickSort($left), [$pivot], quickSort($right)); }
- 优化策略:
- 随机选择pivot(避免最坏情况O(n²))
- 三数取中法
- 小数组切换插入排序
T1/T2/T3线程顺序执行
- 解决方案:
- 使用
join()
:Thread t1 = new Thread(() -> System.out.println("T1")); Thread t2 = new Thread(() -> { try { t1.join(); } catch (InterruptedException e) {} System.out.println("T2"); }); Thread t3 = new Thread(() -> { try { t2.join(); } catch (InterruptedException e) {} System.out.println("T3"); }); t3.start(); t2.start(); t1.start();
- 使用
CountDownLatch
:CountDownLatch latch1 = new CountDownLatch(1), latch2 = new CountDownLatch(1); Thread t1 = new Thread(() -> { System.out.println("T1"); latch1.countDown(); }); Thread t2 = new Thread(() -> { try { latch1.await(); } catch (InterruptedException e) {} System.out.println("T2"); latch2.countDown(); }); Thread t3 = new Thread(() -> { try { latch2.await(); } catch (InterruptedException e) {} System.out.println("T3"); }); t1.start(); t2.start(); t3.start();
- 使用
Redis与数据库一致性
- 常见策略:
- 双写策略:先更新数据库,再更新缓存
- 失效策略:更新数据库后删除缓存
- 延迟双删:更新数据库后删除缓存,再延迟一段时间再次删除
- Canal客户端:
- 原理:监听MySQL binlog,捕获数据变更事件
- 实现步骤:
- 配置MySQL开启binlog
- 启动Canal服务,解析binlog生成JSON数据
- 客户端订阅Canal数据,更新Redis和数据库
AOP切面应用场景
- 典型场景:
- 日志记录(请求参数、执行时间)
- 权限校验(接口访问控制)
- 事务管理(方法级事务回滚)
- 性能监控(方法耗时统计)
- 缓存管理(读取/更新缓存)
策略模式 vs 工厂模式
- 策略模式:
- 用途:动态替换算法(如支付策略、排序策略)
- 结构:定义策略接口 + 多个实现类 + 上下文类
- 工厂模式:
- 用途:解耦对象创建(如数据库连接池)
- 结构:定义工厂接口 + 具体工厂类 + 产品接口/实现
Seata AT模式
- 核心流程:
- 一阶段(本地事务):
- 执行业务SQL,生成分支事务日志(undo log)
- 二阶段(全局提交/回滚):
- 提交:直接提交本地事务
- 回滚:根据undo log逆向执行SQL
- 一阶段(本地事务):
异常与错误区别
- 异常(Exception):
- 可捕获处理(如
IOException
,NullPointerException
) - 分类:
- Checked异常(编译时检查,如
IOException
) - Unchecked异常(运行时异常,如
ArrayIndexOutOfBoundsException
)
- Checked异常(编译时检查,如
- 可捕获处理(如
- 错误(Error):
- 不可恢复(如
OutOfMemoryError
,StackOverflowError
)
- 不可恢复(如
MongoDB vs Redis
- MongoDB:
- 用途:文档型数据库,适合存储复杂结构数据(如JSON)
- 优势:灵活的Schema、水平扩展、支持聚合查询
- Redis:
- 用途:内存缓存,适合高频读写场景(如计数器、Session存储)
LRU缓存淘汰策略
- 实现原理:
- 维护一个双向链表(记录访问顺序) + 哈希表(快速查找)
- 操作:
get
:访问元素时移动到链表头部put
:插入新元素时,若超出容量则删除链表尾部元素
- 阈值设置:
- 根据业务需求动态调整(如设置缓存最大容量为内存的50%)
分布式锁实现
- 常用方案:
- Redis:
- 使用
SETNX
命令 + 过期时间 - 示例:
SET key value NX PX 3000
- 使用
- Zookeeper:
- 创建临时顺序节点,最小节点获取锁
- 数据库:
- 通过唯一索引实现锁表
- Redis:
适合存入Redis的数据
- 高频读写:Session、Token、计数器
- 低一致性要求:缓存数据(如商品详情)
- 结构化数据:Hash、List(如消息队列)
- 临时数据:验证码、限时活动
G1垃圾回收算法
- 特点:
- 并行与并发收集
- 区域划分(Region)
- 优先回收垃圾最多的区域
- 参数设置:
-XX:+UseG1GC
启用G1-XX:MaxGCPauseMillis=200
设置最大停顿时间
CPU飙升排查
- 常见原因:
- 死循环或递归
- 高并发竞争锁
- 频繁GC
- 正则表达式失控
- 排查工具:
top
/htop
定位进程jstack
查看线程堆栈perf
分析CPU占用
OOM排查
- 步骤:
- 使用
jstat -gc
查看堆内存状态 - 生成堆转储文件:
jmap -dump:file=heap.bin <pid>
- 使用MAT工具分析堆内存泄漏
- 使用
事务传播行为
- 常见配置:
@Transactional(propagation = Propagation.REQUIRED)
:默认,加入现有事务@Transactional(propagation = Propagation.REQUIRES_NEW)
:新建事务@Transactional(propagation = Propagation.NEVER)
:禁止事务
事务失效场景
- 原因:
- 方法未被Spring代理(如私有方法)
- 数据库引擎不支持事务(如MyISAM)
- 抛出非运行时异常(需手动回滚)
- 多线程中调用事务方法
获取类注解
- Java代码:
AnnotatedElement element = ...; // 类/方法/字段对象 MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); if (annotation != null) { // 处理注解 }
Linux后台进程命令
- 获取后台进程:
ps aux | grep <process_name> jps -l # 查看Java进程
查找并删除文件
- 命令:
find /path/to/dir -name "*.log" -exec rm -f {} \;
运行Java程序并设置内存
- 命令:
java -Xms512m -Xmx2g -XX:NewSize=256m -XX:MaxNewSize=512m -jar app.jar
1. 不使用同步锁共享变量
- 无锁编程:通过原子操作(如CAS)或双buffer设计实现线程安全。
- 原子操作:使用
std::atomic
(C++)或AtomicInteger
(Java)避免锁。 - CAS(Compare-And-Swap):通过硬件指令实现无锁队列等数据结构。
- 双buffer设计(适用于一写多读场景):
- 使用两个指针(
ptr
和bak_ptr
)指向主数据和备份数据。 - 写线程更新备份数据后,交换指针完成更新,避免读写冲突。
- 使用两个指针(
- 原子操作:使用
2. Spring Cloud 常用组件
- 核心组件:
- Spring Cloud Config:集中式配置管理。
- Spring Cloud Discovery(如 Eureka、Consul):服务发现与注册。
- Spring Cloud Gateway:API网关,路由与负载均衡。
- Spring Cloud Load Balancer:客户端负载均衡。
- Spring Cloud Circuit Breaker(如 Hystrix):断路器,防止雪崩。
- Spring Cloud Sleuth:分布式链路追踪。
- Spring Cloud Stream:消息驱动微服务(支持 Kafka、RabbitMQ)。
- Spring Cloud Task:短暂任务的调度与监控。
3. @Transactional
注解及事务使用
- 事务注解属性:
propagation
:事务传播行为(如REQUIRED
,REQUIRES_NEW
)。isolation
:事务隔离级别(如READ_COMMITTED
,REPEATABLE_READ
)。timeout
:超时时间(秒)。rollbackFor
:指定回滚的异常类型。
- 事务传播行为:
REQUIRED
(默认):如果存在事务则加入,否则新建。REQUIRES_NEW
:总是新建事务,挂起当前事务(若有)。SUPPORTS
:有事务则加入,无事务则非事务执行。NEVER
:禁止事务执行,若存在事务则抛异常。
4. Redis 实现签到功能
- 数据类型:使用 BitMap(基于 Redis 的 String 类型实现)。
- 实现步骤:
- 按月存储:以
userId:yyyyMM
为 Key。 - 签到操作:
SETBIT key dayOfMonth-1 1
:标记某天签到。GETBIT key dayOfMonth-1
:查询某天是否签到。
- 统计签到天数:
BITCOUNT key
:统计当月签到次数。
- 按月存储:以
5. Spring 线程池参数
- 核心参数:
- corePoolSize:核心线程数(长期存活线程)。
- 设置依据:CPU密集型任务设为 CPU 核心数;I/O 密集型任务设为 CPU 核心数 × 2。
- maxPoolSize:最大线程数。
- 设置依据:通常为核心线程数的 2~3 倍,避免资源耗尽。
- queueCapacity:任务队列容量。
- 设置依据:根据系统负载调整,有界队列可防止内存溢出。
- keepAliveSeconds:非核心线程空闲存活时间(建议 60~120 秒)。
- corePoolSize:核心线程数(长期存活线程)。
- 队列触发条件:
- 当线程池中线程数 <
corePoolSize
,新任务直接分配线程。 - 当线程数 >=
corePoolSize
且队列未满,任务入队。 - 当队列满且线程数 <
maxPoolSize
,创建新线程。 - 当队列满且线程数 >=
maxPoolSize
,触发拒绝策略。
- 当线程池中线程数 <
6. CompletableFuture 与线程池的区别
- CompletableFuture:
- 基于 Future 的扩展,支持链式调用和异步编排。
- 更适合处理 异步任务依赖(如多个异步任务的结果组合)。
- 线程池:
- 用于 资源管理,控制线程的创建和复用。
- 更适合 批量任务调度(如固定任务分发)。
7. 为什么要使用 CompletableFuture
- 优势:
- 异步编排:通过
thenApply
、thenCompose
等方法实现任务链式处理。 - 异常处理:支持统一的异常捕获和处理。
- 多任务聚合:通过
allOf
或anyOf
处理多个异步结果。 - 减少阻塞:避免线程阻塞等待任务完成。
- 异步编排:通过
8. RabbitMQ 的几种模式
- 简单队列模式:生产者 → 队列 → 消费者(一对一)。
- 工作队列模式:多个消费者竞争消费队列消息。
- 发布/订阅模式:生产者 → 交换机(广播)→ 多个队列 → 消费者。
- 路由模式:通过路由键(Routing Key)绑定队列。
- 主题模式:基于通配符匹配路由键(如
*.error
)。
9. 布隆过滤器初始容量设置
- 初始容量:建议设置为 数据量的 1.5~2 倍,避免误判率过高。
- 误判率:通常设置为 0.1%~1%,具体值取决于哈希函数数量和位数组长度。
10. 分布式事务实现方式
- 常见方案:
- 两阶段提交(2PC):协调者 + 参与者,保证原子性。
- TCC(Try-Confirm-Cancel):业务层面补偿事务。
- Saga 模式:通过本地事务和补偿操作实现最终一致性。
- Seata:开源分布式事务框架,支持 AT、TCC、SAGA 模式。
11. 未知类型对象互相转换
- 解决方案:
- 反射机制:通过
Class
对象动态获取属性并赋值。 - Spring BeanCopier:
BeanCopier copier = BeanCopier.create(Source.class, Target.class, false); Target target = new Target(); copier.copy(source, target, null);
- Jackson/Gson:通过 JSON 序列化反序列化实现转换。
- 反射机制:通过
12. HashMap 扩容机制
- 扩容触发条件:
- 当
size > threshold
(容量 × 负载因子,默认 0.75)时触发扩容。
- 当
- 扩容过程:
- 创建新的数组(容量为原容量的 2 倍)。
- 重新计算每个键值对的哈希桶位置。
- 将旧数组的元素迁移至新数组。
13. 多线程中子线程无法获取主线程 ID
- 解决方案:
- InheritableThreadLocal:主线程设置值,子线程继承。
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); threadLocal.set("mainThreadId");
- 显式传递参数:在启动子线程时,将主线程 ID 作为参数传入。
- InheritableThreadLocal:主线程设置值,子线程继承。
14. 内存泄漏定位与解决
- 定位方法:
- 工具分析:使用 MAT(Memory Analyzer)、JProfiler 等工具分析堆内存。
- 日志跟踪:通过 GC 日志定位内存增长点。
- 常见原因:
- 长生命周期对象持有短生命周期对象引用。
- 未关闭的资源(如数据库连接、缓存未清理)。
- 解决方法:
- 显式释放资源。
- 使用弱引用(
WeakReference
)或软引用(SoftReference
)。
15. MinIO 的优点与分布式特性
- 优点:
- 高性能:基于 HTTP/2 的对象存储服务。
- 兼容 S3 API:支持 AWS S3 协议。
- 轻量部署:单节点部署或分布式集群。
- 是否分布式:是,支持多节点分布式部署,提供高可用性。
16. 相同表结构复制数据到新表
- SQL 语句:
INSERT INTO new_table (column1, column2, ...) SELECT column1, column2, ... FROM old_table;
- 说明:
new_table
和old_table
结构需一致。- 若表结构复杂,可先使用
CREATE TABLE new_table AS SELECT * FROM old_table
创建新表并复制数据。
Linux 常用命令
1. 解压缩
- tar 解压:
tar -xvf filename.tar -C /目标路径
- gzip 解压:
gunzip filename.gz
- zip 解压:
unzip filename.zip -d /目标路径
2. 创建文件
- 创建空文件:
touch filename
- 创建并写入内容:
echo "内容" > filename
3. 查看进程
- 查看所有进程:
ps -ef
- 按名称过滤进程:
ps -ef | grep 进程名
4. 增加执行权限
- 给文件添加执行权限:
chmod +x filename
5. 查询指定文件下所有 .java
文件
- 使用
find
命令:find /目录路径 -name "*.java"
6. vi
指令
- 打开文件:
vi filename
- 进入编辑模式:按
i
。 - 保存并退出:按
Esc
,输入:wq
。 - 强制退出:按
Esc
,输入:q!
。
聚簇索引 vs 非聚簇索引
聚簇索引(Clustered Index)
- 特点:
- 数据与索引存储在一起(InnoDB 的主键索引)。
- 叶子节点直接存储完整数据。
- 适用场景:主键查询效率高。
- 限制:每个表只能有一个聚簇索引。
非聚簇索引(Secondary Index)
- 特点:
- 叶子节点存储主键值(需回表查询数据)。
- 支持多个非聚簇索引。
- 适用场景:辅助字段查询(如
WHERE name = 'xxx'
)。
MySQL 存储引擎
常用引擎
- InnoDB:
- 支持事务、行级锁、外键。
- 默认使用聚簇索引。
- MyISAM:
- 不支持事务,但性能高。
- 使用非聚簇索引。
- Memory:
- 数据存储在内存中,适合临时表。
MySQL 调优
优化方向
- 索引优化:
- 为频繁查询的列创建索引。
- 避免全表扫描(如
SELECT *
)。
- 配置调优:
- 调整
innodb_buffer_pool_size
(建议物理内存的 70%-80%)。 - 优化日志文件大小(
innodb_log_file_size
)。
- 调整
- 硬件优化:
- 使用 RAID 5/6 提高 I/O 性能。
- 增加内存以减少磁盘 I/O。
索引失效场景
- 使用函数:如
WHERE YEAR(create_time) = 2025
。 - 类型转换:如
WHERE id = '123'
(id
是整数)。 - 模糊查询前导通配符:如
LIKE '%abc'
。 OR
条件:若其中一个条件未使用索引,全表扫描。NOT IN
和!=
:索引失效。- 联合索引未使用最左匹配:如联合索引
(a, b)
,查询WHERE b = 1
无效。
事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 虚读 | 说明 |
---|---|---|---|---|
读未提交 | ✅ | ✅ | ✅ | 最低隔离,性能高但数据一致性差。 |
读已提交 | ❌ | ✅ | ✅ | 防止脏读,但允许不可重复读。 |
可重复读 | ❌ | ❌ | ✅ | MySQL 默认级别,通过间隙锁防止幻读。 |
串行化 | ❌ | ❌ | ❌ | 最高隔离,性能最低,完全串行化事务。 |
JVM 新生代 & 老年代
新生代(Young Generation)
- 结构:
- Eden 区:新对象分配。
- Survivor 区(S0/S1):存活对象复制。
- 回收算法:复制算法(Minor GC)。
老年代(Old Generation)
- 存放对象:长期存活的对象(经过多次 Minor GC)。
- 回收算法:标记-整理(Mark-Compact)或标记-清除(Mark-Sweep)。
对象头存储位置
- 对象头(Object Header):
- Mark Word:存储对象的哈希码、GC 标志、锁状态等。
- 类型指针:指向类元数据(Class Metadata)。
- 数组长度(仅数组对象):记录数组长度。
- 存储位置:对象在堆内存的起始部分。
Redis 数据类型
- String:
- 存储键值对(如用户信息)。
- 底层:SDS(Simple Dynamic String)。
- Hash:
- 存储对象字段(如购物车)。
- 底层:小数据量用
ziplist
,大数据量用hashtable
。
- List:
- 有序集合(如消息队列)。
- 底层:小数据量用
ziplist
,大数据量用linkedlist
。
- Set:
- 无序不重复集合(如抽奖活动)。
- 底层:小集合用
intset
,大集合用hashtable
。
- Sorted Set:
- 带分数的有序集合(如排行榜)。
- 底层:
skiplist
+dict
。
Redisson
基本用法
- 引入依赖(Maven):
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.4</version> </dependency>
- 创建客户端:
Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config);
- 分布式对象:
- 分布式锁、队列、Map 等。
线程池 + 异步编排
线程池核心参数
- corePoolSize:核心线程数。
- maximumPoolSize:最大线程数。
- keepAliveTime:非核心线程空闲存活时间。
- workQueue:任务队列。
- threadFactory:线程工厂。
- handler:拒绝策略。
异步编排(CompletableFuture)
CompletableFuture.supplyAsync(() -> {
// 异步任务1
}).thenApply(result -> {
// 处理结果
}).exceptionally(ex -> {
// 异常处理
});
synchronized vs ReentrantLock
特性 | synchronized | ReentrantLock |
---|---|---|
可中断 | ❌ | ✅ |
超时等待 | ❌ | ✅ |
公平锁 | ❌ | ✅ |
条件变量 | ❌ | ✅ |
性能 | 低(JDK 1.6 优化后提升) | 高 |
代码简洁 | ✅ | ❌ |
volatile
- 作用:
- 保证可见性:修改后立即写入主存。
- 禁止指令重排序:通过内存屏障实现。
- 不保证原子性:如
volatile int count; count++
非原子操作。
HashMap put 的底层原理
- 哈希计算:
key.hashCode()
→(h ^ (h >>> 16)) & (n - 1)
。 - 哈希冲突解决:链表 + 红黑树(链表长度 ≥ 8 时转换)。
- 扩容机制:当
size > threshold
时,容量翻倍。 - 红黑树插入:树化后插入节点。
MQ 的使用场景
- 异步处理:如订单下单后发短信。
- 解耦:模块间通过消息队列通信。
- 削峰填谷:应对流量高峰(如秒杀)。
- 广播:一对多通知(如价格更新)。
Spring DI & IOC
- IOC(控制反转):由 Spring 容器管理对象生命周期和依赖关系。
- DI(依赖注入):通过
@Autowired
或 XML 配置注入依赖。
BeanFactory vs FactoryBean
- BeanFactory:Spring 容器接口,负责创建和管理 Bean。
- FactoryBean:自定义 Bean 工厂接口,实现
getObject()
返回实例。
循环依赖
- Spring 解决方式:
- 三级缓存:
singletonObjects
:一级缓存(完整 Bean)。earlySingletonObjects
:二级缓存(提前暴露的 Bean)。singletonFactories
:三级缓存(ObjectFactory)。
- AOP 代理对象:通过三级缓存提前暴露代理对象。
- 三级缓存:
Spring 事务传播
传播行为 | 说明 |
---|---|
REQUIRED | 有事务则加入,无事务则新建(默认)。 |
REQUIRES_NEW | 总是新建事务,挂起当前事务(若有)。 |
SUPPORTS | 有事务则加入,无事务则非事务执行。 |
NOT_SUPPORTED | 强制非事务执行,挂起当前事务(若有)。 |
MANDATORY | 必须有事务,否则抛异常。 |
NEVER | 必须无事务,否则抛异常。 |
NESTED | 在当前事务中嵌套子事务。 |
OpenFeign 负载均衡
- 实现方式:
- 集成 Ribbon 实现客户端负载均衡。
- 通过
@LoadBalanced
注解启用负载均衡策略(如轮询、随机)。
count(1) vs count(列名) vs count(*)
- count(*):统计所有行(包括
NULL
值)。 - count(1):统计所有行(等效于
count(*)
)。 - count(列名):仅统计该列非
NULL
的行。
JVM 调优
- 内存配置:
- 设置
-Xms
和-Xmx
为相同值避免动态扩容。 - 调整
NewRatio
控制新生代比例。
- 设置
- GC 选择:
- 低延迟场景:G1(Garbage-First)。
- 高吞吐场景:Parallel Scavenge。
- 监控工具:
jstat
:查看 GC 状态。jmap
:分析堆内存。jstack
:分析线程状态。
AOP 注解
- 常用注解:
@Aspect
:定义切面类。@Before
:前置通知。@After
:后置通知。@Around
:环绕通知。@Pointcut
:定义切点。
Redis 持久化
- RDB(快照):
- 定期将内存数据保存为
dump.rdb
文件。 - 优点:恢复速度快,文件紧凑。
- 缺点:可能丢失最后一次快照后的数据。
- 定期将内存数据保存为
- AOF(追加日志):
- 记录所有写操作命令。
- 优点:数据安全性高(可配置同步策略)。
- 缺点:文件体积大,恢复速度慢。
xxl-job
- 核心组件:
- 调度中心:管理任务和触发。
- 执行器:执行具体任务。
- 优点:
- 支持分布式任务调度。
- 提供可视化界面和日志追踪。
@Async 异步
- 配置步骤:
- 启用异步支持:
@EnableAsync
。 - 配置线程池:
@Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } }
- 使用注解:
@Async("taskExecutor") public void asyncMethod() { // 异步逻辑 }
- 启用异步支持:
Sentinel 熔断
- 核心功能:
- 流量控制:限制 QPS 或并发线程数。
- 熔断降级:失败率超过阈值时熔断。
- 系统负载保护:根据 CPU 使用率等指标自动降级。
- 使用场景:
- 微服务调用链中的异常保护。
- 高并发下的服务稳定性保障。