在 jsonfields 中递增的 Django 更新方法超级慢

Django update method for incrementing in jsonfields is super slow

提问人:Stephen Milic 提问时间:11/16/2023 最后编辑:Stephen Milic 更新时间:11/16/2023 访问量:46

问:

我有一个使用 json 字段的模型,虽然这在 99% 的时间里就像一个魅力,但我现在有一个有点烦人的问题,即执行时间。

我需要使用 Django 的 ORM 方法增加大量记录,我不知道如果数据库使用 json 字段以外的其他内容应该有多快,但基本上这里有一些我面临的时间示例:update

  • JSON 字段中仅 +1 的 4000 条记录更新就需要 20 秒
  • 6000 条记录更新花了将近 2 分钟

这些都是小的,我预计一些更新会有超过 100000 条记录,甚至可能达到 100 万条。

对于我正在做的事情的技术方面:

我在 jsonfield 值内使用范围来提取需要更新的记录,在获取我用来将其中一个 jsonfield 值递增 1 的查询集后,这运行得很好。filterupdate

这种行为非常具体,可能不是最优化的,但这就是我现在所拥有的,为此,我使用 进行原始 sql 查询,作为参考,这里是生成所述查询的函数:RawSQL

def jsonfield_increment(field_name: str, key: str, increment_by: int = 1, operator: str = "+") -> RawSQL:
    sql = f"""
        jsonb_set(
            {field_name},
            '{{{key}}}',
            (COALESCE({field_name}->>'{key}','12')::int {operator} {increment_by})::text::jsonb
        )
    """

    return RawSQL(sql, [])

这种方法基本上提取 inside 的值,然后递增它。keyfield

然后,我只需在之前使用 过滤的查询集上运行该方法。updatefield=jsonfield_increment(args)

所以这就是我想知道的,

  • 有没有办法优化更新记录的方式?此更新需要经常运行。

  • 我是否应该拆分查询集中每条X记录的更新并使用线程池保存每个块?

  • 我应该使用其他方法而不是存储实例,然后使用?updatebulk_update

  • 我是否应该花时间完全重新设计这些 int 的存储方式而不使用 jsonfield ?

虽然最后一个听起来是最好的选择,但我有点害怕这样做 因为我在那里存储的东西有点动态,所以它需要一个整体 带有关系的新表,它最终可能会超过 100 个 millons 记录带有 like , , (必需的 jsonfield) 和 where parent would 是当前存储 int 的对象。索引可以使它变得更快,但在该表上写入可能需要一些时间。nameintdataparent

如果有帮助,我可以尝试提供该方法生成的 SQL。update

python sql django postgresql

评论

0赞 rioV8 11/16/2023
因为 6000 条记录更新不需要 30 秒,所以你有一些 N^2 问题,很可能是 ,是某个生成器filter
0赞 Richard Huxton 11/16/2023
您是一次一行执行这些操作,还是在一个语句中执行整个批次操作?如果一次一行,就不要再这样做了 - 数据库的全部意义在于它可以非常有效地进行处理。
0赞 rioV8 11/16/2023
对 100、200、500、1000、2000、5000 条记录的查询元素执行 NOP 操作所需的时间
0赞 Stephen Milic 11/16/2023
@rioV8:看起来像这样: ,我可以做计时,但我开始重新设计整个事情,不使用 jsonfield,这似乎更有效率,将旧东西保留在一个分支中以防万一@RichardHuxton 该方法的重点是在一个语句中更新所有内容filterModel.objects.filter(**{f"field__{dyn_key_name}__range": interval})update

答:

-1赞 Roberto Rubertelli 11/16/2023 #1

我会试着告诉你我会怎么做。我从 pandas 数据帧中读取了 json。Pandas 数据帧将数据传递给 Django ORM。 要将数据从 Pandas 传递到 ORM,我有 2 种可能的方法: 最好的方法是使用 sqlackemy,它是异步的,它非常适合我需要做的工作。我需要与可用的主机建立 SSH 连接。我需要在 setting.py 中配置 sqlackemy。 第二种使用 df.iterrows() 来迭代数据帧的行,并使用 bulk_create() 将它们加载到 ORM 模型中。 我没有考虑到我在 ORM 模型中有外键。它们在迭代时会减慢很多速度。

评论

0赞 Stephen Milic 11/16/2023
DF 用于所有这些整数的初始保存,但是对于所描述的行为使用一个 DF 并没有真正意义,它所做的是在一组记录上将 jsonb 中的值增加 1,将所有这些记录加载为实例以将它们加载到 DF 中以添加 +1 并没有真正意义,特别是因为如果您实例化了记录,您可以简单地遍历它们直接编辑实例并批量保存它们,这与使用不需要实例化的作用相同update
0赞 Roberto Rubertelli 11/16/2023
@Stephen米利奇.我完全不明白你的问题。我的歉意