Mei*_*ham 36 django postgresql bulk-load
我使用bulk_create将数千或行加载到postgresql数据库中.不幸的是,有些行导致IntegrityError并停止bulk_create进程.我想知道是否有办法告诉django忽略这些行并尽可能多地保存批次?
Ces*_*ssa 36
Django 2.2 从文档中ignore_conflicts
为该bulk_create
方法添加了一个新选项:
在支持它的数据库上(除了PostgreSQL <9.5和Oracle之外的所有数据库),将ignore_conflicts参数设置为True会使数据库忽略无法插入任何未通过限制的行(例如重复的唯一值).启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持它).
例:
Entry.objects.bulk_create([
Entry(headline='This is a test'),
Entry(headline='This is only a test'),
], ignore_conflicts=True)
Run Code Online (Sandbox Code Playgroud)
(注意:我不使用Django,因此可能有更合适的框架特定答案)
Django不可能通过简单地忽略INSERT
失败来实现这一点,因为PostgreSQL在第一个错误时中止了整个事务.
Django需要以下方法之一:
INSERT
单独一个事务中的每一行并忽略错误(非常慢);SAVEPOINT
在每个插入之前创建一个(可能有缩放问题);COPY
将数据批量插入或(更好)到TEMPORARY
表中,然后将其合并到主表服务器端.类似upsert的方法(3)似乎是一个好主意,但是upsert和insert-if-not-exists是非常复杂的.
就个人而言,我会采取(4):我可能会批量插入一个新的单独的表,UNLOGGED
或者TEMPORARY
,然后我会运行一些手动SQL来:
LOCK TABLE realtable IN EXCLUSIVE MODE;
INSERT INTO realtable
SELECT * FROM temptable WHERE NOT EXISTS (
SELECT 1 FROM realtable WHERE temptable.id = realtable.id
);
Run Code Online (Sandbox Code Playgroud)
将LOCK TABLE ... IN EXCLUSIVE MODE
防止从导致与上面的说法,未能完成插入冲突创建行并发插入.它并不能防止并发SELECT
S,只是SELECT ... FOR UPDATE
,INSERT
,UPDATE
和DELETE
,所以从表中读取进行正常.
如果你不能阻止并发写入时间过长你也可以使用一个可写的CTE从复制行范围temptable
为realtable
如果它失败了,重试每个块.
一种不涉及手动 SQL 和临时表的快速解决方法是尝试批量插入数据。如果失败,则恢复为串行插入。
objs = [(Event), (Event), (Event)...]
try:
Event.objects.bulk_create(objs)
except IntegrityError:
for obj in objs:
try:
obj.save()
except IntegrityError:
continue
Run Code Online (Sandbox Code Playgroud)
如果你有很多很多错误,这可能不是那么有效(你会花更多的时间连续插入而不是批量插入),但我正在处理一个几乎没有重复的高基数数据集,所以这解决了我的大部分问题问题。