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

Django Ninja下用户API请求存储、限流与历史数据统计实现方案咨询

问题1:请求总次数统计方案

存储选型不用纠结,根据你的实际需求选就行:

  • 要是只需要统计总请求数,用户规模在10万以内,直接在APIKey模型里加个request_total = models.PositiveBigIntegerField(default=0)字段就够了;要是后续还要做按日/周/月的维度统计,优先新增独立的日统计模型,参考定义如下:
from django.db import models
from datetime import date

class APIKeyDailyStats(models.Model):
    apikey = models.ForeignKey(APIKey, on_delete=models.CASCADE, related_name="daily_stats")
    stat_date = models.DateField(default=date.today)
    request_count = models.PositiveIntegerField(default=0)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["apikey", "stat_date"], name="unique_apikey_per_day")
        ]

实现方案优先选Redis+定时任务的组合,完全不会拖累你现有接口的低延迟特性:

  1. 每次请求鉴权通过后,调用Redis的INCR命令,分别对apikey:<apikey_id>:total(总请求数)、apikey:<apikey_id>:day:<当日日期>(当日请求数)两个key做自增,Redis单命令是原子操作,不会出现并发计数错误的问题
  2. 配置定时任务(可用Django Q、Celery Beat甚至系统crontab实现),每小时同步一次Redis的总请求数到APIKey模型的request_total字段,每日0点10分同步前一天的日请求数到APIKeyDailyStats表,同步完成后删除对应过期的Redis key即可
    如果你的用户量非常小(比如小于1万),也可以用自定义中间件实现,但是要注意用select_for_update加锁避免并发计数错误,性能会比Redis方案差一些。

问题2:每日请求上限实现方案

最优方案直接用Redis的过期特性实现,完全不需要自己写重置逻辑:

  1. 给当日请求数的Redis key设置TTL,TTL值计算为「次日0点01分的时间戳 - 当前时间戳」,到期后Redis会自动删除该key
  2. 每次请求执行INCR后,判断返回的计数值是否超过你设置的单日上限(比如100),超过就直接返回429状态码提示请求超限即可
  3. 第二天第一次请求时,该key已经被删除,INCR会自动从1开始计数,刚好满足你00:01自动重置的需求
    该方案性能损耗极低,完全适配你现有接口无数据库操作的特性,并发场景下也不会有问题。

问题3:最近7天请求量统计方案

直接基于你存储在数据库的APIKeyDailyStats表查询即可,Django ORM就能快速实现:

from datetime import datetime, timedelta

def get_7days_stats(apikey):
    end_date = datetime.now().date()
    start_date = end_date - timedelta(days=6) # 包含当天共7天
    # 查询该apikey最近7天的统计数据
    stats = APIKeyDailyStats.objects.filter(
        apikey=apikey,
        stat_date__gte=start_date,
        stat_date__lte=end_date
    ).values_list("stat_date", "request_count")
    # 转成字典方便查找
    stats_dict = {item[0]: item[1] for item in stats}
    # 补全没有请求记录的日期,计数为0
    result = []
    for i in range(7):
        current_date = start_date + timedelta(days=i)
        result.append({
            "date": current_date.strftime("%Y-%m-%d"),
            "count": stats_dict.get(current_date, 0)
        })
    return result

如果没有提前存日统计数据,也可以临时从Redis中拉取最近7天的key对应的值,但是长期来看数据库存储的历史统计数据更稳定,查询和后续扩展都更方便。


内容的提问来源于stack exchange,提问作者fhmisml

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 23:06:06