一、网络协议基础
1.1 HTTP协议
Q1: HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3有什么区别?
答案:
HTTP/1.0 (1996年)
特点:
1. 短连接
- 每次请求都要建立新的TCP连接
- 请求完成后立即关闭连接
流程:
请求1: 建立连接 → 发送请求 → 接收响应 → 关闭连接
请求2: 建立连接 → 发送请求 → 接收响应 → 关闭连接
请求3: 建立连接 → 发送请求 → 接收响应 → 关闭连接
问题:
- 每次建立连接需要TCP三次握手 (耗时100ms+)
- 关闭连接需要四次挥手
- 性能极差!
示例:
加载一个网页 (1个HTML + 10张图片 + 5个CSS + 5个JS)
- 需要建立21次TCP连接
- 总耗时: 21 × 100ms = 2100ms (光是建立连接就要2秒!)
HTTP/1.1 (1997年,至今仍广泛使用)
改进点:
1. 长连接 (Keep-Alive)
默认开启持久连接:
Connection: keep-alive
流程:
建立连接 → 请求1 → 响应1 → 请求2 → 响应2 → 请求3 → 响应3 → 关闭连接
优势:
- 一次TCP连接可以发送多个请求
- 减少连接建立和关闭的开销
- 性能提升5-10倍!
配置:
nginx:
keepalive_timeout 65; # 连接空闲65秒后关闭
keepalive_requests 100; # 单个连接最多100个请求
2. 管道化 (Pipelining)
定义: 不等待响应,连续发送多个请求
HTTP/1.0:
请求1 → 等待响应1 → 请求2 → 等待响应2 → 请求3 → 等待响应3
HTTP/1.1 (管道化):
请求1 → 请求2 → 请求3 → 响应1 → 响应2 → 响应3
问题:
- 队头阻塞 (Head-of-Line Blocking)
- 响应必须按请求顺序返回
- 如果响应1慢,响应2和响应3也被阻塞
实际:
- 浏览器默认不开启管道化 (bug太多)
- 实际作用有限
3. 分块传输 (Chunked Transfer Encoding)
定义: 响应数据分块发送,不需要预先知道总长度
示例: 动态生成的内容
HTTP/1.1 200 OK
Transfer-Encoding: chunked
5\r\n
Hello\r\n
6\r\n
World\r\n
0\r\n
\r\n
应用场景:
- 流式响应 (Server-Sent Events)
- 大文件下载
- 动态内容生成
4. 更多缓存策略
HTTP/1.0: Expires
HTTP/1.1: Cache-Control, ETag, If-None-Match, If-Modified-Since
示例:
# 强缓存
Cache-Control: max-age=3600 # 缓存1小时
# 协商缓存
ETag: "abc123"
If-None-Match: "abc123" # 服务器返回304 Not Modified
HTTP/2 (2015年)
核心改进:
1. 二进制分帧 (Binary Framing)
HTTP/1.1: 文本协议
GET /index.html HTTP/1.1\r\n
Host: example.com\r\n
\r\n
HTTP/2: 二进制协议
+-------+-------+-------+-------+
| Frame Type | Flags | Stream ID |
+-------+-------+-------+-------+
| Payload |
+-------------------------------+
优势:
- 解析更高效 (机器友好)
- 压缩率更高
- 更少的错误
2. 多路复用 (Multiplexing)
HTTP/1.1:
连接1: 请求1 → 响应1 (阻塞)
连接2: 请求2 → 响应2 (阻塞)
连接3: 请求3 → 响应3 (阻塞)
HTTP/2:
单连接: 请求1、请求2、请求3 → 响应1、响应2、响应3 (并发,不阻塞)
实现:
- 每个请求分配一个Stream ID
- 帧(Frame)带Stream ID
- 接收方根据Stream ID重组响应
示例:
浏览器加载网页 (100个资源):
- HTTP/1.1: 6个并发连接,需要17轮 (100/6)
- HTTP/2: 1个连接,1轮即可完成!
性能提升: 50-80%!
3. 头部压缩 (HPACK)
HTTP/1.1:
每次请求都发送完整的头部:
GET /api/user HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: application/json
Cookie: session=abc123...
(约1-2KB)
HTTP/2:
使用HPACK压缩算法:
- 静态表: 常见头部(如:method, :path)预定义
- 动态表: 之前出现过的头部,用索引代替
- 霍夫曼编码: 进一步压缩
示例:
请求1:
:method: GET
:path: /api/user
cookie: session=abc123
请求2 (只发送差异):
:path: /api/product (只有path变化,其他用索引)
压缩率: 80-90%,头部从2KB → 200B!
4. 服务器推送 (Server Push)
定义: 服务器主动推送资源,不等客户端请求
传统流程:
客户端请求 index.html → 服务器返回 →
客户端解析,发现需要 style.css → 请求 style.css → 服务器返回
HTTP/2流程:
客户端请求 index.html →
服务器返回 index.html + 主动推送 style.css, script.js
代码示例 (Node.js):
const http2 = require('http2');
const server = http2.createSecureServer(options);
server.on('stream', (stream, headers) => {
if (headers[':path'] === '/index.html') {
// 推送CSS
stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
pushStream.respondWithFile('/path/to/style.css');
});
// 推送JS
stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
pushStream.respondWithFile('/path/to/script.js');
});
// 返回HTML
stream.respondWithFile('/path/to/index.html');
}
});
优势:
- 减少往返时间 (RTT)
- 提升首屏加载速度
问题:
- 可能推送用户已缓存的资源 (浪费带宽)
- 浏览器支持有限
- 实际使用较少
5. 流优先级 (Stream Priority)
定义: 为不同请求设置优先级,优先处理重要资源
示例:
优先级高: HTML, CSS (阻塞渲染)
优先级中: JavaScript
优先级低: 图片
浏览器请求:
HTML (权重256)
CSS (权重220)
JS (权重147)
Image (权重110)
服务器优先返回HTML和CSS,再返回JS和Image
提升首屏渲染速度!
HTTP/2的局限性:
1. TCP队头阻塞 (TCP Head-of-Line Blocking)
- HTTP/2解决了HTTP层的队头阻塞
- 但TCP层仍有队头阻塞
原理:
TCP是字节流,必须按顺序交付
- 丢包1: 包1丢失,包2和包3已到达
- TCP必须等包1重传后,才能交付包2和包3
- 即使包2和包3属于不同的HTTP/2 Stream
结果:
- 弱网环境(丢包率5%),HTTP/2性能不如HTTP/1.1
- HTTP/1.1多连接,一个连接丢包不影响其他连接
- HTTP/2单连接,一个包丢失阻塞所有Stream
2. 连接建立慢
- TCP三次握手
- TLS握手 (4次)
- 总共: 2-3个RTT (200-300ms)
HTTP/3 (2022年,基于QUIC协议)
核心改进:
1. 基于UDP
HTTP/1.1, HTTP/2: 基于TCP
HTTP/3: 基于UDP + QUIC协议
为什么用UDP?
- TCP协议固化在操作系统内核,难以修改
- UDP灵活,可以在用户空间实现QUIC协议
- QUIC = Quick UDP Internet Connections
2. 解决TCP队头阻塞
QUIC的多路复用:
- 每个Stream独立,互不影响
- Stream1丢包,只阻塞Stream1
- Stream2和Stream3继续传输
TCP vs QUIC:
TCP:
丢包 → 重传 → 等待 → 阻塞所有数据
QUIC:
Stream1丢包 → 重传Stream1
Stream2正常传输 (不阻塞)
Stream3正常传输 (不阻塞)
性能提升:
- 弱网环境 (丢包率5%): HTTP/3比HTTP/2快30-50%
3. 0-RTT连接建立
传统HTTPS (HTTP/2):
RTT1: TCP SYN → SYN-ACK → ACK
RTT2: TLS Client Hello → Server Hello
RTT3: TLS Key Exchange → Change Cipher Spec
RTT4: 发送HTTP请求 → 接收响应
总耗时: 3-4个RTT (300-400ms)
QUIC (HTTP/3):
首次连接:
RTT1: Client Hello (QUIC + TLS) → Server Hello + 数据
RTT2: 接收数据
后续连接 (0-RTT):
RTT0: 直接发送加密数据 (使用之前的密钥)
总耗时: 0-1个RTT (0-100ms)
性能提升: 连接建立速度提升3-4倍!
4. 连接迁移 (Connection Migration)
TCP连接标识:
(源IP, 源端口, 目标IP, 目标端口)
问题:
- 手机从WiFi切换到4G
- IP地址变化
- TCP连接断开,需要重新建立
QUIC连接标识:
Connection ID (64位随机数)
优势:
- 手机从WiFi切换到4G
- Connection ID不变
- 连接继续保持,无需重建!
应用场景:
- 移动网络切换
- NAT重绑定
- 负载均衡IP变化
5. 改进的拥塞控制
TCP拥塞控制:
- 慢启动, 拥塞避免, 快速重传, 快速恢复
- 在内核实现,难以优化
QUIC拥塞控制:
- 在用户空间实现
- 可以快速迭代和优化
- 更精确的RTT测量
- 更好的丢包恢复
算法:
- BBR (Bottleneck Bandwidth and RTT)
- Cubic
- Reno
性能:
- 吞吐量提升10-30%
- 延迟降低20-40%
HTTP版本对比总结:
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|
| 连接 | 短连接 | 长连接 | 长连接 | 长连接 (基于UDP) |
| 并发 | 串行 | 串行 (可管道) | 多路复用 | 多路复用 |
| 队头阻塞 | HTTP层+TCP层 | HTTP层+TCP层 | 仅TCP层 | 无 |
| 头部压缩 | 无 | 无 | HPACK | QPACK |
| 协议格式 | 文本 | 文本 | 二进制 | 二进制 |
| 传输层 | TCP | TCP | TCP | QUIC (UDP) |
| 连接建立 | 3个RTT | 3个RTT | 3个RTT | 0-1个RTT |
| 性能提升 | 基准 | 5-10倍 | 50-80倍 | 100-150倍 |
实际应用建议:
1. HTTP/1.1 → HTTP/2迁移 (强烈推荐)
- 性能提升明显
- 无需修改应用代码
- 只需升级Web服务器 (Nginx 1.9.5+)
- 浏览器兼容性好 (95%+)
配置示例 (Nginx):
server {
listen 443 ssl http2; # 开启HTTP/2
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
2. HTTP/2 → HTTP/3迁移 (谨慎)
- 需要浏览器支持 (Chrome 87+, Firefox 88+)
- 需要Web服务器支持 (Nginx 1.25.0+, Caddy 2.0+)
- 弱网环境提升明显
- 强网环境提升有限
配置示例 (Nginx):
server {
listen 443 quic reuseport; # HTTP/3 (QUIC)
listen 443 ssl http2; # HTTP/2回退
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
add_header Alt-Svc 'h3=":443"; ma=86400'; # 告知浏览器支持HTTP/3
}
3. 优化建议
- 强网环境: HTTP/2足够
- 弱网环境: HTTP/3提升明显
- 移动端: HTTP/3优先 (连接迁移)
- CDN: 优先支持HTTP/3 (Cloudflare, Fastly等已支持)
常见问题:
Q: HTTP/2一定比HTTP/1.1快吗?
A: 不一定
- 强网环境: HTTP/2快50-80%
- 弱网环境 (丢包率>5%): 可能更慢 (TCP队头阻塞)
- 建议: 同时支持HTTP/2和HTTP/1.1,让浏览器选择
Q: 需要修改代码吗?
A: 不需要
- HTTP协议对应用透明
- 只需升级Web服务器
- 应用代码无需修改
Q: 是否需要HTTPS?
A: HTTP/2理论上支持HTTP,但主流浏览器只支持HTTPS
- Chrome, Firefox, Safari只支持HTTP/2 over TLS
- 事实上,HTTP/2 = HTTPS
Q: HTTP/3什么时候能普及?
A: 已经在普及中
- Google, Facebook, Cloudflare已大规模使用
- 浏览器支持率: 90%+ (2024)
- 预计2-3年内成为主流
1.2 HTTPS与安全
Q2: HTTPS的加密流程是怎样的?如何防止中间人攻击?
答案:
HTTPS = HTTP + SSL/TLS
加密流程 (TLS 1.2):
阶段1: TCP三次握手
客户端 → SYN → 服务器
服务器 → SYN-ACK → 客户端
客户端 → ACK → 服务器
耗时: 1个RTT (50-100ms)
阶段2: TLS握手
步骤1: Client Hello
客户端 → 服务器
内容:
- TLS版本: TLS 1.2
- 支持的加密套件: [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ...]
- 客户端随机数: Random_C (用于后续生成密钥)
- 支持的压缩算法
- SNI (Server Name Indication): example.com (支持虚拟主机)
作用:
- 告知服务器客户端能力
- 协商加密算法
步骤2: Server Hello + Certificate + Server Hello Done
服务器 → 客户端
内容:
- TLS版本: TLS 1.2
- 选择的加密套件: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- 服务器随机数: Random_S
- 服务器证书 (包含公钥)
- Server Hello Done
作用:
- 确定使用的TLS版本和加密套件
- 发送证书给客户端验证
步骤3: 客户端验证证书
客户端验证:
1. 证书是否在有效期内
2. 证书域名是否匹配
3. 证书是否被吊销 (CRL或OCSP)
4. 证书是否由受信任的CA签发
证书链验证:
example.com证书 (服务器证书)
↓ 签发者
中间CA证书
↓ 签发者
根CA证书 (浏览器内置)
验证过程:
1. 用中间CA的公钥验证服务器证书的签名
2. 用根CA的公钥验证中间CA证书的签名
3. 根CA证书是浏览器内置的受信任证书
如果验证失败:
- 浏览器显示"您的连接不是私密连接"
- 用户可以选择继续或退出
步骤4: Client Key Exchange
客户端 → 服务器
客户端生成:
- Pre-Master Secret (随机数)
客户端用服务器公钥加密:
- Encrypted Pre-Master Secret
发送给服务器:
- 服务器用私钥解密,得到Pre-Master Secret
双方都有:
- Random_C (客户端随机数)
- Random_S (服务器随机数)
- Pre-Master Secret (预主密钥)
双方各自计算:
Master Secret = PRF(Pre-Master Secret, "master secret", Random_C + Random_S)
再从Master Secret派生:
- 客户端写密钥 (Client Write Key)
- 服务器写密钥 (Server Write Key)
- 客户端写MAC密钥
- 服务器写MAC密钥
- 客户端写IV (初始化向量)
- 服务器写IV
作用:
- 双方协商出对称密钥
- 后续使用对称加密通信 (AES)
步骤5: Change Cipher Spec + Finished
客户端 → 服务器
内容:
- Change Cipher Spec: 后续消息使用协商的密钥加密
- Finished: 握手完成,发送加密的握手摘要
服务器 → 客户端
内容:
- Change Cipher Spec
- Finished
作用:
- 确认握手完成
- 开始加密通信
阶段3: 加密通信
客户端 → 服务器: GET /index.html HTTP/1.1 (AES加密)
服务器 → 客户端: HTTP/1.1 200 OK (AES加密)
使用对称加密 (AES):
- 速度快 (比RSA快1000倍)
- 安全性高 (AES-128足够安全)
完整流程时间消耗:
TCP握手: 1 RTT (50ms)
TLS握手: 2 RTT (100ms)
HTTP请求: 1 RTT (50ms)
总耗时: 4 RTT (200ms)
优化:
- TLS 1.3: 1 RTT握手 (节省50ms)
- 0-RTT: 重用会话密钥 (节省100ms)
- HTTP/3: 0-1 RTT连接 (节省150-200ms)
为什么需要非对称加密 + 对称加密?
只用对称加密 (AES):
问题: 密钥如何安全传输?
- 客户端和服务器如何协商密钥?
- 明文传输密钥 → 被窃听,不安全
只用非对称加密 (RSA):
问题: 性能太差
- RSA加密速度是AES的1/1000
- 大量数据加密,CPU负载极高
混合加密 (推荐):
1. 非对称加密 (RSA): 传输对称密钥 (只传一次,数据量小)
2. 对称加密 (AES): 传输应用数据 (速度快)
兼顾安全性和性能!
如何防止中间人攻击?
攻击场景:
正常流程:
客户端 ←→ 服务器
中间人攻击:
客户端 ←→ 攻击者 ←→ 服务器
攻击者操作:
1. 拦截客户端请求
2. 转发给服务器
3. 拦截服务器响应
4. 转发给客户端
5. 窃听和篡改所有数据!
防御措施:
1. 证书验证 (核心防御)
攻击者伪造证书:
客户端: 请求 example.com
攻击者: 返回伪造的证书 (攻击者自己的公钥)
客户端验证:
1. 检查证书签名
2. 发现证书不是受信任的CA签发
3. 浏览器警告: "您的连接不是私密连接"
4. 攻击被阻止!
关键:
- 根CA证书是浏览器内置的,攻击者无法伪造
- 证书签名无法伪造 (需要CA的私钥)
2. 证书绑定 (Certificate Pinning)
定义: 客户端预先知道服务器证书或CA
实现:
# iOS
let session = URLSession(
configuration: .default,
delegate: CertificatePinner(),
delegateQueue: nil
)
class CertificatePinner: NSObject, URLSessionDelegate {
let expectedCertHash = "sha256/AAAAAAAAAA..."
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge) {
// 验证证书哈希
if getCertHash(challenge.protectionSpace.serverTrust) == expectedCertHash {
// 通过
} else {
// 拒绝连接
}
}
}
优势:
- 即使CA被攻破,也能防御
- 金融App常用
劣势:
- 证书更新需要发布App
- 证书过期导致App无法使用
3. HSTS (HTTP Strict Transport Security)
定义: 强制使用HTTPS,防止降级攻击
响应头:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
含义:
- max-age: 1年内强制HTTPS
- includeSubDomains: 包括所有子域名
- preload: 加入浏览器预加载列表
攻击场景:
用户访问: http://example.com (HTTP)
攻击者拦截,不转发到HTTPS
攻击者返回伪造的HTTP页面
有HSTS:
用户第一次访问: https://example.com
服务器返回: Strict-Transport-Security: max-age=31536000
浏览器记住: example.com只能用HTTPS
后续访问: 用户输入http://example.com
浏览器自动转换: https://example.com
攻击者无法拦截!
HSTS Preload:
- 浏览器内置HSTS域名列表
- 首次访问就强制HTTPS
- 提交到: https://hstspreload.org/
4. 检查证书吊销状态
CRL (Certificate Revocation List):
- 证书吊销列表
- 浏览器定期下载
- 检查证书是否被吊销
问题:
- CRL文件可能很大 (MB级)
- 更新不及时
OCSP (Online Certificate Status Protocol):
- 在线证书状态协议
- 浏览器实时查询证书状态
- 请求: 证书序列号
- 响应: Good / Revoked / Unknown
问题:
- 隐私泄露 (CA知道用户访问哪些网站)
- 性能开销 (额外的网络请求)
OCSP Stapling:
- 服务器定期查询OCSP
- 服务器在TLS握手时附带OCSP响应
- 客户端直接验证,无需额外请求
优势:
- 性能好 (无额外请求)
- 隐私保护 (CA不知道用户访问)
配置 (Nginx):
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/ca-cert.pem;
5. 防止SSLStrip攻击
攻击原理:
用户访问: http://bank.com
攻击者拦截,返回: HTTP页面 (不转HTTPS)
用户输入密码,攻击者窃听
防御:
1. HSTS: 强制HTTPS
2. HSTS Preload: 首次访问就HTTPS
3. 用户警觉: 检查地址栏是否有锁图标
常见问题:
Q: 为什么HTTPS慢?
A: 相比HTTP,HTTPS慢在:
1. TLS握手: 额外2个RTT (100ms)
2. 加密解密: CPU开销 (1-5%,可忽略)
优化:
1. TLS 1.3: 减少到1个RTT
2. 会话复用: 0-RTT
3. HTTP/2: 复用连接
4. CDN: 减少网络延迟
Q: 证书为什么要钱?
A: 证书本身免费 (Let's Encrypt)
- 付费证书提供额外服务:
- EV证书: 绿色地址栏,企业认证
- 通配符证书: *.example.com
- 保险: 证书被攻破,CA赔偿
Q: 自签名证书可以用吗?
A: 可以,但:
- 浏览器会警告 "不安全"
- 无法防止中间人攻击
- 只适合开发环境
- 生产环境必须用受信任的CA签发
Q: HTTP/2必须用HTTPS吗?
A: 理论上不需要,但:
- 浏览器只支持HTTP/2 over TLS
- 事实上,HTTP/2 = HTTPS
二、性能优化
2.1 数据库性能优化
Q3: 除了SQL优化,还有哪些数据库性能优化手段?
答案:
1. 连接池优化
问题:
每次请求都建立新连接:
1. 建立TCP连接 (三次握手)
2. MySQL认证
3. 执行SQL
4. 关闭连接 (四次挥手)
总耗时: 建立连接10ms + SQL执行5ms + 关闭连接10ms = 25ms
其中: 连接管理占80%的时间!
解决方案:
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
# 连接池配置
engine = create_engine(
"mysql://user:pass@localhost/db",
poolclass=QueuePool,
pool_size=10, # 常驻连接数
max_overflow=20, # 峰值额外连接数
pool_timeout=30, # 等待连接超时时间
pool_recycle=3600, # 连接回收时间 (避免MySQL wait_timeout)
pool_pre_ping=True, # 使用前检查连接是否有效
echo_pool=True # 打印连接池日志
)
流程:
1. 请求到来,从连接池获取连接 (1ms)
2. 执行SQL (5ms)
3. 归还连接到连接池 (1ms)
总耗时: 7ms (相比25ms,提升3.5倍!)
连接池大小设置:
- pool_size: CPU核数 × 2 (如8核 → 16个连接)
- max_overflow: pool_size × 2
- 理由: MySQL连接是线程,过多连接导致上下文切换
监控:
- 活跃连接数
- 等待连接数
- 连接使用率
2. 读写分离
架构:
写请求
应用 ───────────→ 主库 (Master)
│ │
│ │ binlog同步
│ ↓
└─读请求─→ 从库1 (Slave1)
├→ 从库2 (Slave2)
└→ 从库3 (Slave3)
实现:
from sqlalchemy import create_engine
# 主库 (写)
master_engine = create_engine("mysql://user:pass@master-db/db")
# 从库 (读)
slave_engines = [
create_engine("mysql://user:pass@slave1-db/db"),
create_engine("mysql://user:pass@slave2-db/db"),
create_engine("mysql://user:pass@slave3-db/db"),
]
import random
def get_db_session(read_only=False):
if read_only:
# 随机选择一个从库 (负载均衡)
engine = random.choice(slave_engines)
else:
engine = master_engine
return Session(bind=engine)
# 使用
# 写操作
with get_db_session(read_only=False) as session:
session.execute("INSERT INTO users ...")
# 读操作
with get_db_session(read_only=True) as session:
users = session.query(User).all()
注意事项:
1. 主从延迟
- 写入主库后立即从从库读取,可能读不到
- 解决: 写后读走主库,或延迟读取
2. 负载均衡策略
- 随机: 简单,但可能不均衡
- 轮询: 均衡,但需要状态维护
- 权重: 根据从库性能分配
- 最少连接: 动态均衡
3. 从库故障
- 健康检查: 定期ping从库
- 自动摘除: 故障从库移出列表
- 自动恢复: 从库恢复后重新加入
3. 分库分表
垂直分库:
单体数据库:
users, orders, products, payments, logistics
拆分:
用户库: users
订单库: orders
商品库: products
支付库: payments
物流库: logistics
优势:
- 降低单库压力
- 不同业务独立扩展
- 故障隔离
劣势:
- 无法join
- 分布式事务
水平分表:
场景: 订单表1亿条记录,查询慢
分表策略:
orders_0: order_id % 10 = 0
orders_1: order_id % 10 = 1
...
orders_9: order_id % 10 = 9
路由规则:
def get_table_name(order_id):
table_index = order_id % 10
return f"orders_{table_index}"
查询:
order_id = 12345
table_name = get_table_name(12345) # orders_5
sql = f"SELECT * FROM {table_name} WHERE order_id = 12345"
优势:
- 单表数据量降低10倍
- 查询速度提升10倍
劣势:
- 无法按其他字段查询 (如user_id)
- 需要查询所有分表,聚合结果
分库分表中间件:
ShardingSphere (推荐):
- 支持分库分表
- 读写分离
- 分布式事务
- 数据加密
- 影子库 (压测)
配置示例:
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/db0
ds1:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/db1
rules:
sharding:
tables:
orders:
actual-data-nodes: ds$->{0..1}.orders_$->{0..9}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: mod
sharding-algorithms:
mod:
type: MOD
props:
sharding-count: 10
4. 缓存优化
查询缓存 (MySQL Query Cache) - 已废弃
问题:
- 表任何更新,所有缓存失效
- 并发写入场景,缓存失效频繁
- MySQL 8.0已移除
不推荐使用!
应用层缓存 (Redis) - 推荐
import redis
redis_client = redis.Redis(host='localhost', port=6379)
def get_user(user_id):
# 1. 查询缓存
cache_key = f"user:{user_id}"
cached_user = redis_client.get(cache_key)
if cached_user:
return json.loads(cached_user)
# 2. 缓存未命中,查询数据库
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
# 3. 写入缓存
redis_client.setex(cache_key, 3600, json.dumps(user))
return user
性能:
- Redis查询: 1ms
- MySQL查询: 10ms
- 提升10倍!
缓存策略:
- 热点数据: TTL较长 (1小时)
- 冷数据: TTL较短 (5分钟)
- 更新频繁: 不缓存或TTL极短
5. 批量操作优化
问题:
# 插入1000条记录
for i in range(1000):
db.execute("INSERT INTO users (name) VALUES (?)", f"user{i}")
问题:
- 1000次网络往返
- 1000次SQL解析
- 1000次事务提交
- 总耗时: 1000 × 10ms = 10秒!
解决方案1: 批量插入
# 方式1: 单条SQL,多个VALUES
values = []
for i in range(1000):
values.append(f"('user{i}')")
sql = f"INSERT INTO users (name) VALUES {','.join(values)}"
db.execute(sql)
# 方式2: executemany (推荐)
users = [(f"user{i}",) for i in range(1000)]
db.executemany("INSERT INTO users (name) VALUES (?)", users)
性能:
- 1次网络往返
- 1次事务提交
- 总耗时: 50ms
- 提升200倍!
解决方案2: 批量更新
# 方式1: CASE WHEN
UPDATE users
SET status = CASE
WHEN id = 1 THEN 'ACTIVE'
WHEN id = 2 THEN 'INACTIVE'
WHEN id = 3 THEN 'ACTIVE'
END
WHERE id IN (1, 2, 3)
# 方式2: ON DUPLICATE KEY UPDATE
INSERT INTO users (id, name, status) VALUES
(1, 'Alice', 'ACTIVE'),
(2, 'Bob', 'INACTIVE'),
(3, 'Charlie', 'ACTIVE')
ON DUPLICATE KEY UPDATE
name = VALUES(name),
status = VALUES(status)
6. 索引优化
覆盖索引:
-- 查询
SELECT id, name, age FROM users WHERE age = 25;
-- 普通索引
INDEX(age)
执行过程:
1. 在age索引中找到age=25的记录ID: [1001, 1002, 1003]
2. 回表查询: 根据ID到主索引获取name字段
3. 回表3次,性能差!
-- 覆盖索引
INDEX(age, name)
执行过程:
1. 在(age, name)索引中找到age=25的记录
2. 索引已包含id, name, age,无需回表!
3. 性能提升10倍!
应用:
- 分页查询
- 统计查询
- 频繁查询的字段组合
前缀索引:
-- 字段太长,索引占用空间大
email VARCHAR(255), INDEX(email) -- 索引大小: 255字节
-- 前缀索引
INDEX(email(10)) -- 只索引前10个字符,大小: 10字节
优势:
- 索引大小降低25倍
- 内存占用少
- B+树高度降低,查询更快
劣势:
- 区分度降低
- 无法用于ORDER BY, GROUP BY
- 需要选择合适的前缀长度
选择前缀长度:
SELECT
COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) AS selectivity_5,
COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) AS selectivity_10,
COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) AS selectivity_15
FROM users;
选择区分度>0.9的最短前缀
7. 配置优化
InnoDB缓冲池 (Buffer Pool)
作用:
- 缓存数据页和索引页
- 减少磁盘IO
配置:
innodb_buffer_pool_size = 8G # 服务器内存的60-70%
监控:
SHOW STATUS LIKE 'Innodb_buffer_pool%';
关键指标:
- Innodb_buffer_pool_read_requests: 读请求总数
- Innodb_buffer_pool_reads: 从磁盘读取次数
- 命中率 = 1 - (reads / read_requests)
- 目标: >99%
慢查询日志
配置:
slow_query_log = ON
long_query_time = 1 # 超过1秒记录
log_queries_not_using_indexes = ON # 记录未使用索引的查询
分析:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
最大连接数
配置:
max_connections = 500 # 最大连接数
监控:
SHOW STATUS LIKE 'Threads_connected'; # 当前连接数
SHOW STATUS LIKE 'Max_used_connections'; # 历史最大连接数
调优:
- Max_used_connections接近max_connections → 增加max_connections
- 连接数长期很低 → 检查连接泄漏
总结:
| 优化手段 | 性能提升 | 复杂度 | 适用场景 |
|---|---|---|---|
| 连接池 | 3-5倍 | 低 | 所有场景 (必须) |
| 读写分离 | 2-3倍 | 中 | 读多写少 |
| 分库分表 | 10倍+ | 高 | 海量数据 |
| 缓存 | 10-100倍 | 中 | 热点数据 |
| 批量操作 | 100倍+ | 低 | 批量写入 |
| 索引优化 | 10-1000倍 | 中 | 所有场景 |
| 配置优化 | 1.5-2倍 | 低 | 所有场景 |
优化顺序:
- SQL优化 (索引、查询改写)
- 连接池优化
- 缓存优化
- 读写分离
- 分库分表 (最后考虑)
(未完待续...本文档包含网络协议和数据库优化部分,后续还有服务器优化、前端优化等内容)