Django 在查询集上循环时,数据库读取何时发生?
Django when looping over a queryset, when does the db read happen?
我正在遍历我的数据库并更新我所有的 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
,而这个循环是运行ning,这个循环还会访问吗?如果我为 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()
Company.objects.filter(updated=False)
转换为普通的 SQL 查询:
SELECT * FROM appName_company WHERE updated is false
此 SQL 查询在您开始遍历 Company
个对象时执行。它只执行一次。第二个服务器将无法识别第一个服务器的工作,因为它们都将通过相同的 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,但锁可能会降低数据库速度。
我正在遍历我的数据库并更新我所有的 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
,而这个循环是运行ning,这个循环还会访问吗?如果我为 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()
Company.objects.filter(updated=False)
转换为普通的 SQL 查询:
SELECT * FROM appName_company WHERE updated is false
此 SQL 查询在您开始遍历 Company
个对象时执行。它只执行一次。第二个服务器将无法识别第一个服务器的工作,因为它们都将通过相同的 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,但锁可能会降低数据库速度。