签入版本
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
# 性能调优
|
||||
|
||||
## 概述
|
||||
|
||||
Flask 应用的性能调优涉及 Gunicorn Worker 配置、数据库连接池、Redis 缓存策略、慢查询优化等多个层面。合理的调优能显著提升并发处理能力和响应速度。
|
||||
|
||||
:::tip 性能调优原则
|
||||
1. 先测量,后优化:用数据说话,避免盲目调参
|
||||
2. 瓶颈优先:找到最大瓶颈,集中优化
|
||||
3. 渐进式:每次只调整一个参数,观察效果
|
||||
:::
|
||||
|
||||
## Gunicorn Worker 配置
|
||||
|
||||
### Worker 数量
|
||||
|
||||
```bash
|
||||
# 单 Worker(开发环境)
|
||||
python app.py
|
||||
|
||||
# 多 Worker(生产环境)
|
||||
gunicorn -w 4 -b 0.0.0.0:8021 app:app
|
||||
```
|
||||
|
||||
:::tip Worker 数量公式
|
||||
推荐 Worker 数量 = `CPU 核心数 * 2 + 1`
|
||||
|
||||
例如 4 核 CPU:`workers = 4 * 2 + 1 = 9`
|
||||
|
||||
查看 CPU 核心数:
|
||||
```bash
|
||||
# Linux
|
||||
nproc
|
||||
|
||||
# Python
|
||||
import multiprocessing; print(multiprocessing.cpu_count())
|
||||
```
|
||||
:::
|
||||
|
||||
### 启动方式选择
|
||||
|
||||
| 方式 | 适用场景 | 特点 |
|
||||
|------|---------|------|
|
||||
| `python app.py` | 开发调试 | 单进程,自动重载 |
|
||||
| `gunicorn -w N` | 生产部署 | 多进程,无自动重载 |
|
||||
| `gunicorn --threads N` | 生产部署 | 多线程模式 |
|
||||
|
||||
### Gunicorn 配置示例
|
||||
|
||||
```bash
|
||||
pip install gunicorn
|
||||
|
||||
gunicorn app:app \
|
||||
--workers 4 \
|
||||
--bind 0.0.0.0:8021 \
|
||||
--timeout 120 \
|
||||
--keep-alive 5 \
|
||||
--max-requests 1000 \
|
||||
--max-requests-jitter 50
|
||||
```
|
||||
|
||||
:::tip max-requests 作用
|
||||
`--max-requests 1000` 表示每个 Worker 处理 1000 个请求后自动重启,防止内存泄漏累积。`--max-requests-jitter 50` 添加随机抖动,避免所有 Worker 同时重启。
|
||||
:::
|
||||
|
||||
### Gunicorn 配置文件
|
||||
|
||||
创建 `gunicorn.conf.py` 配置文件:
|
||||
|
||||
```python
|
||||
# gunicorn.conf.py
|
||||
import multiprocessing
|
||||
|
||||
# 绑定地址
|
||||
bind = "0.0.0.0:8021"
|
||||
|
||||
# Worker 数量
|
||||
workers = multiprocessing.cpu_count() * 2 + 1
|
||||
|
||||
# Worker 类型
|
||||
worker_class = "sync"
|
||||
|
||||
# 线程数(worker_class="gthread" 时生效)
|
||||
threads = 2
|
||||
|
||||
# 超时时间
|
||||
timeout = 120
|
||||
|
||||
# 保持连接
|
||||
keepalive = 5
|
||||
|
||||
# 最大请求数(防止内存泄漏)
|
||||
max_requests = 1000
|
||||
max_requests_jitter = 50
|
||||
|
||||
# 日志
|
||||
accesslog = "-"
|
||||
errorlog = "-"
|
||||
loglevel = "info"
|
||||
```
|
||||
|
||||
使用配置文件启动:
|
||||
|
||||
```bash
|
||||
gunicorn -c gunicorn.conf.py app:app
|
||||
```
|
||||
|
||||
## 数据库连接池
|
||||
|
||||
SQLAlchemy 连接池配置(在 `config/database.py` 中):
|
||||
|
||||
```python
|
||||
# 连接池配置
|
||||
SQLALCHEMY_POOL_SIZE = 20 # 连接池大小
|
||||
SQLALCHEMY_MAX_OVERFLOW = 10 # 超出池大小的最大连接数
|
||||
SQLALCHEMY_POOL_TIMEOUT = 30 # 获取连接超时(秒)
|
||||
SQLALCHEMY_POOL_RECYCLE = 3600 # 连接回收时间(秒)
|
||||
```
|
||||
|
||||
:::warning 连接池大小
|
||||
连接池总连接数 = `pool_size + max_overflow`。需确保不超过数据库的 `max_connections` 限制,否则会报连接数超限错误。
|
||||
|
||||
MySQL 查看最大连接数:
|
||||
```sql
|
||||
SHOW VARIABLES LIKE 'max_connections';
|
||||
```
|
||||
:::
|
||||
|
||||
### 连接池监控
|
||||
|
||||
```python
|
||||
# 获取连接池状态
|
||||
from src.core.database import engine
|
||||
pool = engine.pool
|
||||
print(f"池大小: {pool.size()}")
|
||||
print(f"已签出: {pool.checkedout()}")
|
||||
print(f"空闲数: {pool.checkedin()}")
|
||||
print(f"溢出数: {pool.overflow()}")
|
||||
```
|
||||
|
||||
## Redis 缓存优化
|
||||
|
||||
### 缓存策略
|
||||
|
||||
| 策略 | 适用场景 | 实现方式 |
|
||||
|------|---------|---------|
|
||||
| 读缓存 | 字典、配置等读多写少 | 查询时先读 Redis,未命中再查 DB |
|
||||
| 写失效 | 数据更新时 | 更新 DB 后删除 Redis 缓存 |
|
||||
| 预热 | 服务启动时 | 启动后主动加载热点数据到 Redis |
|
||||
|
||||
### TTL 设置建议
|
||||
|
||||
```python
|
||||
# 字典数据:较长 TTL(变更频率低)
|
||||
DICT_CACHE_TTL = 3600 # 1 小时
|
||||
|
||||
# 用户权限:中等 TTL(登录时刷新)
|
||||
PERM_CACHE_TTL = 1800 # 30 分钟
|
||||
|
||||
# 验证码:短 TTL(一次性使用)
|
||||
CAPTCHA_CACHE_TTL = 300 # 5 分钟
|
||||
```
|
||||
|
||||
### 缓存预热
|
||||
|
||||
```python
|
||||
# 服务启动时预热缓存
|
||||
def warmup_cache():
|
||||
"""预热字典、配置等热点数据"""
|
||||
from src.common.utils.dict_util import refresh_dict_cache
|
||||
refresh_dict_cache()
|
||||
```
|
||||
|
||||
## 慢查询优化
|
||||
|
||||
### 开启慢查询日志
|
||||
|
||||
MySQL:
|
||||
```sql
|
||||
SET GLOBAL slow_query_log = ON;
|
||||
SET GLOBAL long_query_time = 1;
|
||||
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
|
||||
```
|
||||
|
||||
### 常见优化手段
|
||||
|
||||
| 问题 | 优化方案 |
|
||||
|------|---------|
|
||||
| 缺少索引 | 为 WHERE/ORDER BY 字段添加索引 |
|
||||
| 全表扫描 | 使用 EXPLAIN 分析查询计划 |
|
||||
| N+1 查询 | 使用 joinedload/subqueryload 预加载关联数据 |
|
||||
| 大分页 | 使用游标分页或延迟关联 |
|
||||
| SELECT * | 只查询需要的字段 |
|
||||
|
||||
### 索引优化示例
|
||||
|
||||
```python
|
||||
# 模型定义中添加索引
|
||||
class Article(BaseModel, BaseDB):
|
||||
__tablename__ = DB_PREFIX + "article"
|
||||
|
||||
title = Column(String(255), index=True) # 单列索引
|
||||
status = Column(Integer, index=True)
|
||||
category_id = Column(Integer, index=True)
|
||||
|
||||
# 复合索引(在 __table_args__ 中定义)
|
||||
__table_args__ = (
|
||||
Index('idx_status_category', 'status', 'category_id'),
|
||||
)
|
||||
```
|
||||
|
||||
### 分页查询优化
|
||||
|
||||
```python
|
||||
# 避免大分页:使用游标分页
|
||||
# 差:OFFSET 100000 LIMIT 10(扫描大量数据)
|
||||
# 好:WHERE id > last_id LIMIT 10(利用主键索引)
|
||||
|
||||
def paginate_after_id(self, after_id: int, limit: int = 10):
|
||||
"""游标分页:基于上次最后一条记录的 ID"""
|
||||
return cls.model.query.filter(
|
||||
cls.model.id > after_id,
|
||||
cls.model.is_delete == 0
|
||||
).order_by(cls.model.id.asc()).limit(limit).all()
|
||||
```
|
||||
|
||||
## 同步架构优化
|
||||
|
||||
### 线程池配置
|
||||
|
||||
Flask 使用 Gunicorn 的 sync worker,每个 worker 处理一个请求。可以通过多 worker 和多线程提升并发:
|
||||
|
||||
```bash
|
||||
# 多 worker + 多线程
|
||||
gunicorn app:app \
|
||||
--workers 4 \
|
||||
--threads 2 \
|
||||
--worker-class gthread \
|
||||
--bind 0.0.0.0:8021
|
||||
```
|
||||
|
||||
### 异步任务
|
||||
|
||||
对于耗时操作(如邮件发送、文件处理),使用 Celery 等异步任务队列:
|
||||
|
||||
```python
|
||||
from celery import Celery
|
||||
|
||||
celery = Celery('tasks', broker='redis://localhost:6379/0')
|
||||
|
||||
@celery.task
|
||||
def send_email(to, subject, body):
|
||||
"""异步发送邮件"""
|
||||
# 发送邮件逻辑
|
||||
pass
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
性能调优的核心路径:Gunicorn 多 Worker 利用多核 CPU、数据库连接池匹配并发需求、Redis 缓存减少 DB 压力、索引和查询优化降低慢查询。建议先用监控数据定位瓶颈,再针对性调优,每次只调整一个参数并观察效果。
|
||||
Reference in New Issue
Block a user