Skip to content

性能调优

概述

Django 应用的性能调优涉及 Gunicorn Worker 配置、数据库连接池、Redis 缓存策略、慢查询优化等多个层面。合理的调优能显著提升并发处理能力和响应速度。

性能调优原则

  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

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

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分钟)

连接池大小

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]

查询优化

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 压力、索引和查询优化降低慢查询。建议先用监控数据定位瓶颈,再针对性调优,每次只调整一个参数并观察效果。

小蚂蚁云团队 · 提供技术支持