【问题标题】:Optimization of bulk update/insert批量更新/插入优化
【发布时间】:2014-01-16 06:49:10
【问题描述】:

我正在使用 Django 1.6 和 PostgreSQL 9.1 编写一个 Web 应用程序,它将显示在线游戏的玩家统计信息。我使用 django-extensions “runscript” 创建了一个脚本,该脚本获取所有在线玩家并将其插入/更新到我的表中。该脚本使用 cron 每小时执行 4 次。我需要插入或更新,因为播放器可能已经在表中(因此应该更新)或不在表中。

对于我的问题:高峰时段大约有 25,000 名玩家在线,我不确定应该如何优化这一点(最小化 hdd i/o)。到目前为止,我是这样做的:

@transaction.commit_manually
def run():
    for fetched_player in FetchPlayers():
        defaults = {
            'level': fetched_player['level'],
            'server': fetched_player['server'],
            'last_seen': date.today(),
        }
        player, created = Player.objects.get_or_create(name=fetched_player['name'], defaults)
        if not created:
            player.level = fetched_player['level']
            if player.server != fetched_player['server']:
                # I save this info to another table
            player.server = fetched_player['server']
            player.last_seen = date.today()
            player.save()
    transaction.commit()

绕过 Django 并使用 psycopg2 或类似工具访问数据库会更快吗?当“其他人”修改数据库时,Django 会感到困惑吗?注意 Django 只读取数据库,所有的写入都由这个脚本完成。

如何(使用 Django 或 psycopg2)从数据库中批量获取播放器,更新找到的播放器,然后插入未找到的播放器?如果这甚至可能?查询会变得很大:'SELECT * FROM player WHERE name = name[0] OR name = name[1] OR ... OR name[25000]'。 :)

【问题讨论】:

  • 也许你可以在第一次插入播放器时做一个注释,所以fetched_player 将包含一个信息,如果播放器是新的并且应该插入,或者它不是并且应该更新 - 这个会让事情变得容易得多。您根本不必从数据库中获取数据。
  • @LudwikTrammer 我不确定我理解你的意思。 FetchPlayers() 只是检索和解析 HTML 文档。这些“笔记”应该存储在哪里?
  • 如果没有任何对象字段被修改,Django'smart'是否足够不执行插入查询?如果我将一个字段设置为与它已有的值相同的值,然后调用 .save() 会怎样?我尝试对所有属性进行检查(就像 player.server != fetched_player['server']),然后仅在任何属性确实发生更改时才调用 .save() 。这似乎已经最小化了一些 I/O,请参阅:pastebin.com/RfWbUGYJ 和 imgur.com/cKlsLJ4 我还能做些什么改进?
  • 哦,好的。 FetchPlayers() 使用了你无法控制的数据源 - 我一开始没有得到这个。
  • 关于改进的问题。这一切都取决于 - 如果插入相对较少(这听起来很合理),最好: 1. 检查对象是否存在 .exists() 2. 如果它确实使用 .update() 更新 3. 如果它不插入 @ 987654328@ 这样您就不必从数据库中获取整个对象。这可能会更快,但确保它的唯一方法是尝试并查看。

标签: django postgresql psycopg2 django-postgresql


【解决方案1】:

如果您想减少查询次数,我的建议如下: 对每个玩家直接调用 update(),返回更新的行数,如果计数为 0(表示玩家是新玩家),则将玩家数据放入临时列表中。完成所有获取的播放器后,使用 bulk_create() 通过一条 SQL 语句插入所有新播放器。

假设你有 M+N 玩家(M 新,N 更新),查询次数:

之前:(M+N) 选择 + M 插入 + N 更新

之后:(M+N) 次更新 + 1 次批量插入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-10
    • 2019-06-24
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 2013-11-19
    • 2018-09-04
    • 1970-01-01
    相关资源
    最近更新 更多