Django在遍历查询集时,何时读取数据库?

Posted

技术标签:

【中文标题】Django在遍历查询集时,何时读取数据库?【英文标题】:Django when looping over a queryset, when does the db read happen? 【发布时间】:2018-09-25 08:53:35 【问题描述】:

我正在循环访问我的数据库并更新我的所有 Company 对象。

for company in Company.objects.filter(updated=False):
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

我的问题是它花费的时间太长,所以我想运行相同代码的另一个实例,但我很好奇实际的数据库读取何时发生。如果在此循环运行时company.visited get 更改为True,是否仍会被此循环访问?如果我为visited 添加第二次检查会怎样?如果第一个实例无法识别第二个实例的工作,我不想启动第二个循环:

for company in Company.objects.filter(updated=False):
    if company.visited:
        continue
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

【问题讨论】:

这段代码是在视图内、芹菜任务内还是其他地方执行? 这段代码是从manage.py shell开始的 【参考方案1】:

Company.objects.filter(updated=False) 转换为普通的 SQL 查询:

SELECT * FROM appName_company WHERE updated is false

当您开始遍历 Company 对象时,将执行此 SQL 查询。它只执行一次。第二台服务器将无法识别第一台服务器的工作,因为它们都将通过相同的Company 对象。

使用原子事务和select_for_update()锁定行以避免竞争条件:

from django.db import transaction

for company in Company.objects.filter(updated=False):
    with transaction.atomic():
        Company.objects.select_for_update().get(id=company.id)
        if company.visited:
            continue
        driver.get(company.company_url)
        company.adress = driver.find_element_by_id("address").text
        company.visited = True    
        company.save()

您可以在多台服务器上运行此代码。每个Company 只会被处理一次。

如果您需要定期执行此代码,我强烈建议您使用 Celery。为每个公司分配一个任务,让多个工人并行工作:

from celery import shared_task

@shared_task
def dispatch_tasks():
    for company in Company.objects.filter(updated=False):
        process_company.delay(company.id)

@shared_task
@transaction.atomic
def process_company(company_id):
    company = Company.objects.select_for_update().get(id=company_id)
    if company.visited:
        continue
    driver.get(company.company_url)
    company.adress = driver.find_element_by_id("address").text
    company.visited = True    
    company.save()

编辑:哦,我看到你已经用 sqlite 标签标记了这个问题。我建议切换到 PostgreSQL,因为 SQLite 在并发方面真的很糟糕。我的答案应该适用于 SQlite,但锁可能会减慢数据库速度。

【讨论】:

select_for_update()在 SQLite 上被忽略:docs.djangoproject.com/en/2.1/ref/databases/… 另见:***.com/a/46949541/1097104

以上是关于Django在遍历查询集时,何时读取数据库?的主要内容,如果未能解决你的问题,请参考以下文章

读取大数据集时在 R 3.4 中找不到函数“fread”

Apache Cassandra 阅读解释

在 Django 框架中读取过滤器查询集

如何在返回的 AJAX 调用上使用 django 模板标签?

C#中 sqlDataRead 的三种方式遍历读取各个字段数值

传递读取的 FD parcelable:何时关闭?