如何缓存BULK_CREATE的单个Django rest API帖子?

问题描述

我有一个Django rest API端点。它接收JSON有效负载,例如

{ "data" : [0,1,2,3] }

在views.py函数中对其进行解码,并生成一个新的数据库对象,如so(伪代码):

newobj = MyObj.(col0 = 0, col1= 1, col2 = 2, col3 = 3)
newobj.save()

在测试中,创建x1000新对象的列表,然后执行批量创建的速度要快20倍:

Myobj.objects.bulk_create(newobjs, 1000)

所以,问题是,当我们有1000个帖子时,如何将单个帖子保存在Django中的某个位置,以便进行批量写入?


解决方案

感谢上面的回复,答案包括建议的部分内容,但这是一个超集,因此是摘要。

这实际上是关于创建FIFO。memcached被证明是不合适的(在尝试之后),因为只有redis具有启用此功能的列表功能,here进行了很好的解释。

还请注意,内置缓存Django不支持redis列表API调用。

因此我们需要一个新的docker-compose.yml条目来添加redis:

  redis:
    image: redis
    ports:
      - 6379:6379/tcp
    networks:
      - app-network  

然后在views.py中添加:(请注意redisrpush的用法)

import redis
...
redis_host=os.environ['REDIS_HOST']
redis_port = 6379
redis_password = ""
r = redis.StrictRedis(host=redis_host, port=redis_port, password=redis_password, decode_responses=True)
...
def write_post_to_redis(request):
payload = json.loads(request.body)
r.rpush("df",json.dumps(payload))
因此,这会将接收到的有效负载推送到redis内存缓存中。我们现在需要读取(或弹出)它并将其写入postgres数据库。因此,我们需要一个每隔n秒唤醒并进行检查的进程。为此,我们需要Djangobackground_task。首先,使用以下命令安装:

pipenv install django-background-tasks

并添加到settings.py的已安装应用程序

INSTALLED_APPS = [
...
    'background_task',

然后运行迁移以添加后台任务表:

python manage.py migrate

现在在views.py中,添加:

from background_task import background
from background_task.models import CompletedTask

并添加将缓存数据写入postgres数据库的函数,请注意装饰符,它声明应该每隔5秒在后台运行一次。另请注意redislpop的用法。

@background(schedule=5)
def write_cached_samples():
...
payload = json.loads(r.lpop('df'))
# now do your write of payload to postgres
... and delete the completed tasks or we'll have a big db leak
CompletedTask.objects.all().delete()

为了启动该进程,请在urls.py的基础上添加以下内容:

write_cached_samples(repeat=10, repeat_until=None)

最后,因为后台任务需要单独的进程,所以我们在docker-compose.yml中复制了Django docker容器,但将asgi服务器运行命令替换为后台进程运行命令。

django_bg:
      image: my_django
      command: >
        sh -c "python manage.py process_tasks"
      ...
总而言之,我们添加了两个新的停靠容器,一个用于redis内存缓存,另一个用于运行Django后台任务。我们使用redis列表rPush和lop函数创建一个FIFO,并使用接收推送接口和后台任务弹出。

有一个小问题,其中nginx连接到了错误的Django容器,并通过停止并重新启动后台容器进行了更正,还有一些问题是Docker网络路由初始化错误。

下一步,我将Django HTTP API端点替换为Go One,看看我们的速度提高了多少,因为Daphne ASGI服务器达到了每秒仅100个请求的最大CPU。

相关文章