当前位置: 首页 > 面试题库 >

Django:如何在事务中包装批量更新/插入操作?

糜运良
2023-03-14
问题内容

这是我的用例:

  • 我有多个并行运行的芹菜任务
  • 每个任务可以批量 创建更新 许多对象。为此,我正在使用django-bulk

所以基本上我使用的是一个非常方便的功能insert_or_update_many:

  1. 它首先执行选择
  2. 如果找到对象,它将对其进行更新
  3. 否则会创建它们

但这引入了并发问题。例如:如果在第1步中不存在对象,则将其添加到要插入的对象列表中。但是在此期间,另一个Celery任务已经创建了该对象,当它尝试执行批量插入时(步骤3),我会收到重复Entry的错误。

我想我需要将3个步骤包装在一个“阻止”块中。我已经阅读了有关事务的内容,并且尝试将步骤1,2,3包装在一个with transaction.commit_on_success:块中

with transaction.commit_on_success():
    cursor.execute(sql, parameters)
    existing = set(cursor.fetchall())
    if not skip_update:
        # Find the objects that need to be updated
        update_objects = [o for (o, k) in object_keys if k in existing]
        _update_many(model, update_objects, keys=keys, using=using)
    # Find the objects that need to be inserted.
    insert_objects = [o for (o, k) in object_keys if k not in existing]
    # Filter out any duplicates in the insertion
    filtered_objects = _filter_objects(con, insert_objects, key_fields)
    _insert_many(model, filtered_objects, using=using)

但这对我不起作用。我不确定我是否对交易有充分的了解。我基本上需要一个块,可以在其中放置几个​​操作,以确保没有其他进程或线程正在访问(写入)我的数据库资源。


问题答案:

我基本上需要一个块,可以在其中放置几个​​操作,以确保没有其他进程或线程正在访问(写入)我的数据库资源。

Django交易通常不会为您保证。如果您来自计算机科学的其他领域,那么您自然会以这种方式将事务视为阻塞,但是在数据库世界中,锁的级别不同,隔离级别也不同,并且每个数据库的锁也不同。因此,为了确保您的事务能够做到这一点,您将必须了解事务,锁及其性能特征,以及数据库提供的用于控制它们的机制。

但是,让一堆进程都试图锁定表以执行竞争性插入听起来不是一个好主意。如果很少发生冲突,则可以进行某种形式的乐观锁定,如果失败则重试该事务。或者,您可以将所有这些celery任务定向到一个进程中(如果无论如何都要获取表锁,则并行执行此操作没有性能优势)。

我的建议是从忘记批量操作开始,而仅使用Django的一次执行一次update_or_create。只要您的数据库具有防止重复条目的约束(听起来确实如此),它就不会出现上述竞争条件。如果性能确实不可接受,那么可以考虑使用更复杂的选项。

采用开放式并发方法意味着,“带走”不是像平常那样进行冲突,而是要获取表锁,而是应该照常进行,然后在发现有问题的情况下重试该操作。在您的情况下,它可能看起来像:

while True:
    try:
        with transaction.atomic():
            # do your bulk insert / update operation
    except IntegrityError:
        pass
    else:
        break

因此,如果遇到竞争状况,结果IntegrityError将导致该transaction.atomic()块回滚所做的任何更改,并且while循环将强制重试该事务(假定大容量操作现在将看到新存在的行,并且将其标记为要更新而不是插入)。

如果冲突很少发生,则这种方法非常有效,如果冲突频繁发生,则效果很差。



 类似资料:
  • 问题内容: 我不确定是否在批量索引编制中正确使用了该操作。 我的要求是: 网址是: 我想我错过了文档中的某些内容,但仍然找不到如何进行此操作的方法。 我想要 在索引中创建以上文档,或者如果存在则对其进行更新。 问题答案: 如果您通过批量API将索引中的记录添加为 那么如果该ID已经存在于索引中,您将获得一个异常。如果要添加或 替换 文档(取决于文档是否存在),则应按以下方式进行请求 如果已经存在具

  • 我正在写一个数据挖掘程序,可以批量插入用户数据。 当前SQL只是一个普通的批量插入: 如果发生冲突,如何进行更新?我试过: 但它抛出

  • 我试图将数据插入GraphDB,因此SPARQL更新查询由总大小约为1M的语句组成,并带有一些DELETE和WHERE语句。我使用GraphDB REST API执行此操作失败: 1) 成功启动了发送更新请求(python代码段)的事务处理POST/repositories/{repositoryID}/transactions 2) 获取错误 此Sparql语句成功地执行在工作台SPARQL控制

  • 问题内容: 我想用Django更新表格-原始SQL中的内容如下: 我的第一个结果是这样的-但这很讨厌,不是吗? 有没有更优雅的方式? 问题答案: UPD Django 2.2版本现在具有bulk_update。 请参阅以下django文档部分 一次更新多个对象 简而言之,你应该可以使用: 你还可以使用F对象来执行诸如增加行数之类的操作: 请参阅文档:https : //docs.djangopro

  • 问题内容: 有没有办法像在MySQL服务器上那样批量执行查询? 将无法使用,因为如果该字段已经存在,它将直接忽略该字段并且不插入任何内容。 将无法工作,因为如果该字段已经存在,它将首先对其进行处理,然后再次进行处理,而不是对其进行更新。 可以使用,但不能批量使用。 因此,我想知道是否可以批量发出这样的命令(一次不止一次发送)。 问题答案: 您 可以 使用INSERT … ON DUPLICATE

  • 我第一次尝试执行批量插入/更新操作。我正在使用Mybatis注释(映射器)执行与数据库相关的操作。 我有一个@Param,它是列表 映射器将具有: ****我不确定到底是做什么的。如果有人能对它有所了解,我会非常感激。**** 我在关注@Repository- 例如Controller类有: 此外,有多少批量插入的记录以上的方法将能够处理?(记录可以范围从5000到50000) 附言:-这也是我第