【Django学习笔记】—— Django 高并发通俗讲解

Django 高并发通俗讲解

用生活中的例子,把 Django 高并发这件事说清楚。


先搞懂一个问题:什么是"高并发"?

想象一家餐厅:

  • 低并发:中午来了 3 个客人,1 个服务员完全忙得过来
  • 高并发:突然涌进来 300 个客人,还是 1 个服务员------场面失控

高并发就是:在极短时间内,大量用户同时访问你的网站,你的系统还能扛住、不崩、响应够快。

Django 就像是这家餐厅的"厨房",问题是:Django 这个厨房默认只有一个灶台,一次只能炒一道菜。 那怎么扛住 300 个客人同时点单?

答案就是:不在一个灶台上死磕,而是搞一整套"流水线"。


第 1 层:CDN + 负载均衡 ------ 门口先分流

通俗解释

你开了一家奶茶店,300 人排队。你不可能让 300 人同时挤到柜台前,而是:

  1. 门口放个菜单牌(CDN):很多客人看一眼菜单就走了,根本不需要进店
  2. 安排 3 个点单窗口(负载均衡):把 300 人分流到 3 个窗口排队,而不是挤 1 个窗口

技术对应

生活例子 技术手段 作用
门口菜单牌 CDN(内容分发网络) 图片、CSS、JS 这些"静态资源"缓存在全球各地的节点,用户直接从最近的节点拿,根本不打扰 Django
3 个点单窗口 Nginx / HAProxy(负载均衡器) 把用户请求均匀分配到多个 Django 实例上,而不是全压到一个

举个例子

复制代码
用户想看一张商品图片:
  ❌ 没有 CDN:请求 → Django → 数据库 → 返回图片(慢,Django 被拖累)
  ✅ 有 CDN:  请求 → 最近的 CDN 节点直接返回(Django 完全不知道这事)

一句话总结:大部分请求在到达 Django 之前就被 CDN 挡掉了,Django 只需要处理"真正需要动脑子"的请求。


第 2 层:应用服务器 ------ 厨房多开几个灶

通俗解释

这是最关键的一层。

Django 默认的运行方式,就像一个厨师,一次只炒一道菜,炒完才炒下一道。客人多了,后面的人就得干等。

怎么办?三个方案:

方案 A:多雇几个厨师(多进程)
复制代码
厨师1:炒宫保鸡丁
厨师2:炒鱼香肉丝
厨师3:炒麻婆豆腐

3 个厨师同时炒,效率直接翻 3 倍。

方案 B:一个厨师用多个灶台(协程/异步)
复制代码
厨师:宫保鸡丁下锅了,等收汁的时候......
      → 我先去切鱼香肉丝的葱花
      → 回头翻一下宫保鸡丁
      → 鱼香肉丝下锅

一个厨师,但利用"等待时间"同时处理多道菜。这就是 gevent 协程 的思路------I/O 等待时不闲着

方案 C:换成异步厨房(ASGI)

整个厨房重新设计,从设计之初就支持"多任务并行",不只是一道菜,还能同时处理客人聊天(WebSocket)、推送消息等。

技术对应

生活例子 技术手段 代码示例
多雇厨师 Gunicorn 多进程 gunicorn -w 4(4 个进程)
一个厨师多灶台 Gunicorn + gevent 协程 --worker-class gevent --worker-connections 1000
异步厨房 Uvicorn + ASGI uvicorn myproject.asgi:application

举个例子

bash 复制代码
# 方案 A:4 个厨师(进程),每个一次炒一道菜
gunicorn myproject.wsgi -w 4
# → 能同时处理 4 个请求

# 方案 B:4 个厨师,每个能同时照看 1000 个灶台
gunicorn myproject.wsgi -w 4 --worker-class gevent --worker-connections 1000
# → 理论上能同时处理 4000 个请求!(I/O 密集场景)

# 方案 C:异步厨房
uvicorn myproject.asgi:application --workers 4
# → 支持 async/await,还能搞 WebSocket

怎么选?

你的场景 推荐方案 打个比方
普通网站(查数据库为主) Gunicorn + gevent 厨师等菜熟的时候去切菜
需要聊天/推送(WebSocket) Uvicorn + ASGI 厨师一边炒菜一边跟客人聊天
大量 CPU 计算(图片处理等) 多进程 sync worker 计算密集,切菜也得专心,别分心

一句话总结:Django 自己一次只能处理一个请求,但我们可以通过"多进程"或"协程"让它同时处理成百上千个。


第 3 层:缓存 ------ 做好的菜别重炒

通俗解释

客人点了 100 杯珍珠奶茶,你总不能每杯都从头开始煮茶、加珍珠吧?

聪明的做法:煮一大桶茶放那儿,谁来点直接倒一杯。

这就是缓存------把重复计算的结果存起来,下次直接用,不用再算一遍。

技术对应

生活例子 技术手段 效果
提前煮好的大桶茶 Redis 缓存 查询结果存好,下次直接取
菜单上写好的价格 全页缓存 整个页面存好,连渲染都省了
冰箱里的预制菜 查询缓存 复杂查询的结果缓存起来

举个例子

python 复制代码
# 没有缓存:每次都有人问"今天有什么菜",厨师每次都要重新列一遍
def menu(request):
    dishes = Dish.objects.filter(available=True)  # 每次都查数据库
    return render(request, 'menu.html', {'dishes': dishes})

# 有缓存:第一次列好菜单贴墙上,后面的人自己看
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # 缓存 15 分钟
def menu(request):
    dishes = Dish.objects.filter(available=True)  # 15 分钟内只查一次
    return render(request, 'menu.html', {'dishes': dishes})

效果对比:

复制代码
没有缓存:
  第 1 个客人 → 查数据库(50ms)→ 渲染页面(30ms)→ 返回(80ms)
  第 2 个客人 → 查数据库(50ms)→ 渲染页面(30ms)→ 返回(80ms)
  第 3 个客人 → 查数据库(50ms)→ 渲染页面(30ms)→ 返回(80ms)
  ...100 个客人 = 100 次数据库查询

有缓存:
  第 1 个客人 → 查数据库(50ms)→ 渲染(30ms)→ 缓存结果 → 返回(80ms)
  第 2 个客人 → 直接从 Redis 取(1ms)→ 返回(1ms)  ⚡
  第 3 个客人 → 直接从 Redis 取(1ms)→ 返回(1ms)  ⚡
  ...100 个客人 = 只有 1 次数据库查询!

一句话总结:缓存是性价比最高的优化手段,让 99% 的请求根本不用碰数据库。


第 4 层:Celery 异步任务 ------ 费时间的活儿后台干

通俗解释

你正在前台点单,突然有个客人说"我要一份佛跳墙"------这道菜要炖 3 个小时。你总不能让后面 299 个客人等 3 个小时吧?

聪明做法:跟客人说"好的,做好了通知您",然后让后台厨师慢慢炖,你继续接待下一个客人。

这就是 Celery------把耗时的任务丢到后台去跑,不阻塞当前请求。

举几个真实的例子

场景 同步(阻塞) 异步(Celery)
用户注册后发邮件 用户等 5 秒邮件发完才能看到"注册成功" 立即返回"注册成功",邮件后台发
生成报表 用户盯着转圈圈 30 秒 立即返回"报表生成中,完成后通知您"
调用第三方 API 第三方慢,用户一直等 丢给 Celery,用户先去干别的
批量发推送 1000 条推送,用户等几分钟 后台慢慢发,用户无感知

代码示例

python 复制代码
# tasks.py --- 定义后台任务
from celery import shared_task

@shared_task
def send_welcome_email(user_id):
    """这个函数会在后台 worker 里执行,不影响 Web 请求"""
    user = User.objects.get(pk=user_id)
    send_mail(
        '欢迎注册',
        f'你好 {user.username},欢迎加入!',
        'noreply@example.com',
        [user.email],
    )

# views.py --- 用户注册
def register(request):
    user = User.objects.create(username='张三', email='zhangsan@example.com')
    
    # ❌ 同步:用户要等邮件发完才能看到响应
    # send_welcome_email(user.id)
    
    # ✅ 异步:立即返回,邮件后台慢慢发
    send_welcome_email.delay(user.id)   # .delay() = 丢给 Celery 队列
    
    return JsonResponse({'status': '注册成功,邮件稍后送达'})

流程对比:

复制代码
同步方式:
  用户点"注册" → 创建用户 → 发邮件(5秒) → 返回"成功"
  用户感受:等了 5 秒 😫

异步方式:
  用户点"注册" → 创建用户 → 丢任务到队列 → 立即返回"成功"
                              ↓
                    Celery worker 后台慢慢发邮件
  用户感受:瞬间完成 😊

一句话总结:凡是超过 200ms 的操作,都丢给 Celery 后台跑,用户不用等。


第 5 层:数据库优化 ------ 别让仓库成瓶颈

通俗解释

数据库就像餐厅的仓库。厨师每炒一道菜都要去仓库取食材,如果仓库管理混乱,厨师就全在排队等取货。

两个关键优化:

优化 1:读写分离 ------ 拿货和存货分开

复制代码
没有读写分离:
  所有厨师都去同一个仓库 → 挤爆了

有读写分离:
  写操作(存食材)→ 主仓库(Primary)
  读操作(取食材)→ 副仓库(Replica,有 3 个)
  → 读压力分散到 3 个仓库,主仓库只管写入
python 复制代码
# settings.py
DATABASES = {
    'default': {   # 主库:写入
        'HOST': 'primary-db',
    },
    'replica': {   # 从库:读取
        'HOST': 'replica-db',
    },
}

# 自动路由:读走从库,写走主库
class ReplicaRouter:
    def db_for_read(self, model, **hints):
        return 'replica'
    def db_for_write(self, model, **hints):
        return 'default'

优化 2:连接池 ------ 别每次都重新开门

复制代码
没有连接池:
  厨师取货 → 开仓门 → 取货 → 关仓门 → 开仓门 → 取货 → 关仓门 ...
  (光开关门就浪费大量时间)

有连接池:
  仓门一直开着(连接复用),厨师直接进去取货
  → 省掉了反复建立连接的开销
python 复制代码
# settings.py
DATABASES = {
    'default': {
        'CONN_MAX_AGE': 60,  # 连接保持 60 秒,复用
    }
}

优化 3:ORM 查询优化 ------ 别一个一个拿

python 复制代码
# ❌ N+1 问题:查 100 本书和作者,要查 101 次数据库
books = Book.objects.all()           # 第 1 次查询
for book in books:
    print(book.author.name)          # 每本书都查一次作者 → 100 次查询!

# ✅ select_related:JOIN 查询,1 次搞定
books = Book.objects.select_related('author')  # 1 次查询,JOIN 出作者信息
for book in books:
    print(book.author.name)          # 不再查数据库

打个比方:N+1 问题就像你炒 100 道菜,每道菜都跑一趟仓库取一种调料。select_related 就是一次性把 100 道菜的调料全拿齐。
一句话总结:数据库是整个系统最容易成为瓶颈的地方,读写分离 + 连接池 + 查询优化三管齐下。


第 6 层:水平扩展 ------ 开分店

通俗解释

一家店生意太好,厨师加到极限了还是忙不过来,怎么办?

开分店。

复制代码
                ┌─ Django 店 1(4 个厨师)
Nginx 门卫 ────┤─ Django 店 2(4 个厨师)
                ├─ Django 店 3(4 个厨师)
                └─ Django 店 N(4 个厨师)
                          │
                共享一个大仓库(数据库 + Redis)

关键前提:Django 是"无状态"的------它不记住某个特定用户的状态(Session 存在 Redis 里),所以任何一个 Django 实例都能处理任何一个用户的请求。

这就像连锁餐厅:不管你去哪家分店,刷会员卡都能查到你的信息(因为会员信息存在总部服务器,而不是各家分店)。

一句话总结:Django 无状态 = 可以无限加机器 = 想要多少并发就加多少台。


终极对比:一台 runserver vs 生产级架构

维度 python manage.py runserver 生产级架构
服务器 Django 自带的开发服务器 Gunicorn(gevent) / Uvicorn
进程数 1 个 多进程 × 多协程
缓存 Redis 全页缓存 + 查询缓存
异步任务 无,全同步 Celery + 消息队列
数据库 单库,每次新建连接 读写分离 + 连接池
静态资源 Django 自己处理 CDN 全球分发
负载均衡 Nginx 多实例分流
大致并发 ~10 个请求 10,000+ 请求

一句话总结全文

Django 本身就像一个能力很强但一次只能做一件事的厨师。它的高并发能力不靠自己,而是靠一套"组合拳":

门口分流(CDN)→ 多灶台(Gunicorn/gevent)→ 做好的菜不重炒(缓存)→ 费时间的活后台干(Celery)→ 仓库管理优化(读写分离)→ 开分店(水平扩展)

每一层都在帮 Django 分担压力,最终扛住高并发。

相关推荐
绝世番茄1 小时前
鸿蒙HarmonyOS ArkTS原生学习:缩放手势实现 PinchToZoom 深度解析
学习·华为·harmonyos·鸿蒙
a1117761 小时前
SLAM 学习笔记(四)后端(KF BA)
笔记·学习
六点_dn2 小时前
Linux学习笔记-chmod命令
linux·笔记·学习
那个松鼠很眼熟w2 小时前
4. GB2312字符集,以及字符编码
笔记
MartinYeung52 小时前
[论文学习]揭示大语言模型智能体记忆模块中的隐私风险
人工智能·学习·语言模型
浮江雾2 小时前
Flutter第四节------核心概念学习笔记:从基础语法到异步编程
linux·服务器·开发语言·笔记·flutter·入门·基础
ysa0510303 小时前
【板子】欧拉序
c++·笔记·算法·深度优先·图论
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端:Flutter 鸿蒙应用的错误处理与优雅降级策略
学习·flutter·华为·harmonyos
2301_800256113 小时前
人工智能入门学习——ANN、CNN基本概念和考试核心代码
人工智能·学习·cnn