我的数据库中有数据需要定期更新。数据源返回在该时间点可用的所有内容,因此将包括数据库中尚未存在的新数据。
在循环源数据时,如果可能的话,我不希望进行1000次单独的写入。
有没有像update_or_create
这样的东西可以批量工作?
一个想法是将update_or_create
与手动事务结合使用,但我不确定这是否只是将单个写入排队,或者是否将所有写入组合到一个SQL插入中?
或者类似地,在循环中使用update_or_create
的函数上使用@commit_on_success()
可以工作吗?
除了翻译数据并将其保存到模型中之外,我没有对数据做任何事情。在循环期间,没有任何东西依赖于该模型。
6条答案
按热度按时间wfsdck301#
从Django 4.1开始,**
bulk_create
方法通过update_conflicts
支持 upserts,这是与update_or_create
**相同的单查询批量:字符串
c3frrgcw2#
自从Django增加了对bulk_update的支持,现在这在某种程度上是可能的,尽管每个批处理需要执行3个数据库调用(一个get,一个bulk create和一个bulk update)。在这里为通用函数创建一个良好的接口有点挑战,因为您希望函数既支持高效查询又支持更新。这里是我实现的一个方法,它是为批量update_or_create设计的,其中您有许多公共标识键(可以是空的)和一个标识键在批次中不同。
这是作为基础模型上的方法实现的,但可以独立于基础模型使用。这也假设基础模型在名为
updated_on
的模型上有一个auto_now
时间戳;如果不是这种情况,则代码中假设这一点的行已经被注解以便于修改。为了批量使用它,请在调用它之前将更新分成批量。这也是一种绕过数据的方法,这些数据可能具有次要标识符的少量值中的一个,而不必更改接口。
字符串
示例用法:
型
可能的竞赛条件
上面的代码利用事务和select-for-update来防止更新上的竞争条件。但是,如果两个线程或进程试图创建具有相同标识符的对象,则插入可能存在争用条件。
简单的缓解方法是确保common_keys和unique_key的组合是数据库强制的唯一性约束(这是该函数的预期用途)。这可以通过unique_key引用具有
unique=True
的字段来实现,或者通过unique_key与common_keys的子集结合来实现,这些子集通过UniqueConstraint一起强制作为唯一的)。使用数据库强制的唯一性保护,如果多个线程试图执行冲突创建,则除一个线程外,所有线程都将失败,并产生IntegrityError
。由于封闭事务,失败的线程将不会执行任何更改,并且可以安全地重试或忽略(失败的冲突创建可以被视为首先发生的创建,然后立即被覆盖)。如果不可能利用唯一性约束,那么您需要实现自己的并发控制或lock the entire table。
0yycz8jy3#
批量更新将是一个upsert命令,就像@imposeren说的,Postgres 9.5给了你这种能力。我认为Mysql 5.7也可以(参见http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html),这取决于您的确切需求。也就是说,使用db游标可能是最简单的。这没什么不对,它是在那里当ORM只是不够。
沿着这些路线应该行得通。这是伪代码,所以不要只是剪切-n-粘贴它,但概念是有你。
字符串
这里的假设是:
db_results
是某种结果迭代器,可以在列表或字典中使用db_results
的结果可以直接输入原始sql exec语句with
块向下推一点rlcwz9us4#
Django的django-bulk-update-or-create库可以做到这一点。
busg9geu5#
我一直在使用@Zags的答案,我认为这是最好的解决方案。但我想给他一些建议。
字符串
如果你使用auto_now=True字段,如果你使用.update()或bulk_update(),它们将不会被更新,这是因为字段“auto_now”使用.保存()触发,正如你可以在文档中阅读的那样。
如果你有auto_now字段F.e:updated_on,最好将其显式添加到unique_key_to_defaults中。
型
gt0wga4j6#
我是这样做的:
字符串
总共3个查询将总是发生,而不是n个查询(如果你是在循环中更新和创建)。
注意:我认为产品的名称字段是唯一的上面的例子。