【问题标题】:Database query optimization数据库查询优化
【发布时间】:2011-02-27 10:45:08
【问题描述】:

好吧,我的巨人朋友们,我再次在你的肩膀上寻找一点空间:P

这就是问题所在,我有一个 python 脚本正在修复一些数据库问题,但它花费的时间太长,主要的更新语句是这样的:

cursor.execute("UPDATE jiveuser SET username = '%s' WHERE userid = %d" % (newName,userId))

这被调用了大约 9500 次,使用不同的 newName 和 userid 对......

关于如何加快流程的任何建议?也许以某种方式我可以通过一个查询来完成所有更新?

任何帮助将不胜感激!

PS:Postgres 是正在使用的数据库。

【问题讨论】:

  • 你能发布“EXPLAIN ANALYZE ”的输出吗?
  • 你能告诉我多长是太长吗?十秒,五个小时?
  • @Anonymous 我需要它在 2.5 小时内运行,目前需要 5 个
  • 我不明白为什么这不能在几秒钟内运行?

标签: python database postgresql query-optimization


【解决方案1】:

将所有数据插入另一个空表(例如称为 userchanges),然后批量更新:

UPDATE jiveuser
SET username = userchanges.username
FROM userchanges
WHERE userchanges.userid = jiveuser.userid
    AND userchanges.username <> jiveuser.username

请参阅the COPY command 上的此文档以批量加载您的数据。

还有tips for improving performance when populating a database

【讨论】:

  • 把它做成一个临时表,完成后你就不必费心了。
  • @Cade Roux:我更新了你的语句,因为 PostgreSQL 的 UPDATE...FROM 结构与你写的有点不同(但足够接近,我只在第二遍时才注意到它......)
  • 另外,请考虑使用 COPY 将数据推送到服务器,尽管它需要一些特定于驱动程序的支持(通常称为 putcopydata 或类似名称)
  • 因此,为了创建该临时表,我需要运行与之前运行更新语句相同数量的插入语句...插入是否比基于索引的更新快得多领域?
  • @Jay,就像我说的,插入一次 9500 行,一次更新 9500 行。在 RDBMS 上,较少的大集合操作通常比许多小集合操作更快。
【解决方案2】:

首先,不要使用 % 运算符来构造您的 SQL。相反,将您的参数元组作为第二个参数传递给cursor.execute,这也无需引用您的参数并允许您对所有内容使用 %s:

cursor.execute("UPDATE jiveuser SET username = %s WHERE userid = %s", (newName, userId))

这对于防止SQL Injection attacks 很重要。

要回答您的问题,您可以通过在userid 列上创建索引来加速这些更新,这将允许数据库在O(1) 恒定时间内更新,而不必扫描整个数据库表,即O(n)。由于您使用的是 PostgreSQL,因此创建索引的语法如下:

CREATE INDEX username_lookup ON jiveuser (userid);

编辑:由于您的评论显示您已经在 userid 列上建立了索引,因此您无法加快查询速度。因此,您的主要选择要么忍受缓慢,因为这听起来像是一次性解决问题的事情,要么遵循VeeArr 的建议并测试cursor.executemany 是否会给您带来足够的提升。

【讨论】:

  • 好吧,我有一个用户 ID 的索引,这是 where 子句中的内容......用户名上的索引真的有帮助吗?
  • @hdx:不,那是我的错误,我不小心输入了username 而不是userid,而且我没有意识到您已经在该列上有了索引。我刚刚纠正了我的错字。
  • %s Postgres DBAPI 的占位符吗?我不知道,我在问,因为占位符通常是 ?
  • @TZ: %s 是 DB-API 2.0 标准占位符;实现可以使用诸如?%s 之类的占位符,但我的理解是%s 需要工作才能使实现被视为符合DB-API 2.0。
【解决方案3】:

花费这么长时间的原因可能是您启用了自动提交,并且每次更新都在自己的事务中完成。

这很慢,因为即使您有一个电池支持的 RAID 控制器(当然,所有数据库服务器上都应该有),它仍然需要为每个事务提交写入该设备以确保持久性。

解决方案是每个事务执行不止一行。但是不要让交易太大,否则你也会遇到问题。尝试每 10,000 行更改提交一次作为粗略猜测。

【讨论】:

    【解决方案4】:

    您可能想查看executemany():信息here

    【讨论】:

      【解决方案5】:

      也许你可以在用户 ID 上创建一个索引来加快速度。

      【讨论】:

        【解决方案6】:

        我会对此进行解释。如果它正在执行索引查找以查找记录——如果你在用户 ID 上有一个索引应该这样做——那么我看不出你可以做些什么来提高性能。如果它没有使用索引,那么诀窍就是找出原因并修复它。

        哦,您可以尝试使用准备好的语句。有 9500 次插入,这应该会有所帮助。

        【讨论】:

          【解决方案7】:

          将此移动到存储过程并从数据库自身执行它。

          【讨论】:

          • 好吧,当您将数据放到临时表或类似的数据库中时,您就不需要服务器功能了。
          【解决方案8】:

          首先确保您在“userid”上有一个索引,这将确保 dbms 不必每次都进行表扫描

          CREATE INDEX jiveuser_userid ON jiveuser (userid);
          

          接下来尝试准备语句,然后对其调用执行。这将阻止优化器每次都检查查询

          PREPARE update_username(string,integer) AS UPDATE jiveuser SET username = $1 WHERE userid = $2;
          EXECUTE update_username("New Name", 123);
          

          最后,通过关闭自动提交可以挤出更多性能

          \set autocommit off
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-03-30
            • 2017-01-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多