# 性能调优 ## 概述 Django 应用的性能调优涉及 Gunicorn Worker 配置、数据库连接池、Redis 缓存策略、慢查询优化等多个层面。合理的调优能显著提升并发处理能力和响应速度。 :::tip 性能调优原则 1. 先测量,后优化:用数据说话,避免盲目调参 2. 瓶颈优先:找到最大瓶颈,集中优化 3. 渐进式:每次只调整一个参数,观察效果 ::: ## Gunicorn Worker 配置 ### Worker 数量 ```bash # 单 Worker(开发环境) python manage.py runserver # 多 Worker(生产环境) gunicorn application.wsgi:application --bind 0.0.0.0:8000 --workers 4 ``` :::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 manage.py runserver` | 开发调试 | 单进程,自动重载 | | `gunicorn` | 生产部署 | 多进程,成熟的进程管理 | ### Gunicorn 配置示例 ```bash pip install gunicorn gunicorn application.wsgi:application \ --bind 0.0.0.0:8000 \ --workers 4 \ --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 同时重启。 ::: ## 数据库连接池 Django 使用 `django.db.backends` 内置连接池,可通过 `DATABASES` 配置中的 `CONN_MAX_AGE` 控制连接复用: ```python # application/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'djangoadmin_prod', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'djangoadmin', 'PASSWORD': '<强密码>', 'CONN_MAX_AGE': 600, # 连接存活时间(秒),0 表示每次请求新建连接 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, } } ``` | 参数 | 说明 | 推荐值 | |:----|:----|:----| | `CONN_MAX_AGE` | 连接存活时间(秒) | `600`(10分钟) | :::warning 连接池大小 Django 默认每个请求使用独立连接。高并发场景可考虑使用 `django-db-connection-pool` 等第三方包管理连接池。 ::: ## 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 分钟 ``` ## 慢查询优化 ### 开启慢查询日志 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 查询 | 使用 `select_related`/`prefetch_related` 预加载关联数据 | | 大分页 | 使用游标分页或延迟关联 | | SELECT * | 只查询需要的字段(`.values()` 或 `.only()`) | ### 索引优化示例 ```python # 模型定义中添加索引 class Article(BaseModel): title = models.CharField(max_length=255, db_index=True) # 单列索引 status = models.IntegerField(db_index=True) category_id = models.IntegerField(db_index=True) class Meta: db_table = get_table_name('article') indexes = [ models.Index(fields=['status', 'category_id'], name='idx_status_category'), # 复合索引 ] ``` ### 分页查询优化 ```python # 避免大分页:使用游标分页 # 差:OFFSET 100000 LIMIT 10(扫描大量数据) # 好:WHERE id > last_id LIMIT 10(利用主键索引) def paginate_after_id(queryset, last_id, limit=10): """游标分页:基于上次最后一条记录的 ID""" return queryset.filter(id__gt=last_id).order_by('id')[:limit] ``` ## 查询优化 ### select_related 和 prefetch_related ```python # N+1 问题示例(差) articles = Article.objects.filter(status=1) for article in articles: print(article.category.name) # 每次循环都查询一次数据库 # 优化:使用 select_related(外键关联) articles = Article.objects.filter(status=1).select_related('category') for article in articles: print(article.category.name) # 已预加载,不再查询 # 优化:使用 prefetch_related(多对多关联) users = User.objects.prefetch_related('roles') for user in users: print(user.roles.all()) # 已预加载 ``` ### values() 和 only() ```python # 只查询需要的字段 users = User.objects.filter(status=1).values('id', 'username', 'email') # 或使用 only() users = User.objects.filter(status=1).only('id', 'username', 'email') ``` ## 总结 性能调优的核心路径:Gunicorn 多 Worker 利用多核 CPU、数据库连接池匹配并发需求、Redis 缓存减少 DB 压力、索引和查询优化降低慢查询。建议先用监控数据定位瓶颈,再针对性调优,每次只调整一个参数并观察效果。