You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django如何在首个请求到达前提前缓存分页count(*)查询

方案说明

之前预热失效的核心原因

你在AppConfig.ready()中构造请求预热不生效,本质是两个问题:

  • 生产环境大多用gunicorn/uWSGI多worker模式部署,AppConfig.ready()仅在主进程执行,主进程写入的缓存上下文不会被fork出的worker进程完整继承,worker启动后读不到预热的缓存结果。
  • ready()执行阶段Django的请求中间件、数据库路由层还未完成全量初始化,伪造的请求走不到完整的分页查询逻辑,根本不会触发django-cachalot的缓存写入。

可直接落地的简洁方案

方案1:自定义预热命令+启动前执行(最通用)

不用构造伪造请求,直接写一个Django自定义命令,执行和分页逻辑完全一致的count(*)查询:

  1. 新建管理命令文件:
# 对应app下新建 management/commands/preheat_count_cache.py
from django.core.management.base import BaseCommand
# 替换成你首页接口实际用到的查询集
from yourapp.models import YourHomeDataModel

class Command(BaseCommand):
    help = "预热首页分页count查询缓存"
    def handle(self, *args, **kwargs):
        # 直接执行和PageNumberPagination完全一致的count逻辑,cachalot会自动缓存结果
        _ = YourHomeDataModel.objects.filter(your_filter_params).count()
        self.stdout.write(self.style.SUCCESS("首页count缓存预热完成"))
  1. 在部署启动脚本中,启动Web服务前先执行该命令:
python manage.py preheat_count_cache
# 之后再启动gunicorn/uWSGI服务

这种方式执行时Django所有组件、缓存客户端、数据库连接都已经初始化完成,写入的缓存所有worker进程都能直接读取,首个请求不会再触发慢count查询。不管是Docker、systemd还是物理机部署都能适配。

方案2:用Web服务的worker初始化钩子(无需改部署流程)

如果你用gunicorn部署,可以直接在gunicorn配置文件中加worker启动后钩子,每个worker进程启动时自动完成预热:

# gunicorn.conf.py
def post_worker_init(worker):
    # 导入要预热的查询集,执行count触发缓存
    from yourapp.models import YourHomeDataModel
    _ = YourHomeDataModel.objects.filter(your_filter_params).count()

如果用uWSGI,也可以用对应的post-fork钩子实现同样逻辑,每个worker独立预热,完全避开主进程预热不生效的问题。

补充优化

如果你的大表写入频率不高,可以给分页count单独设置更长的缓存过期时间,配合定时任务每天/每小时刷新一次缓存,进一步降低数据库压力,不用每次服务重启都执行全表count。


内容的提问来源于stack exchange,提问作者Abdul Rahman.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 21:21:36