加速MySQL更新/插入语句

时间:2016-12-11 23:01:11

标签: python mysql

如何从速度的角度改进以下功能?理想情况下,我希望将executemany用作此过程的一部分。

虽然功能正常,但我确信有更有效的方法可以做到这一点;检查是否存在值并根据情况更新/插入。

我需要每天在数百万个数据中执行此操作。

float int

1 个答案:

答案 0 :(得分:4)

如果您需要这样做数百万次,那么这里存在许多性能问题。

  • 你一遍又一遍地准备相同的SQL语句,数百万次。准备一次并执行数百万次会更好。

  • 在单个查询后,您在每次函数调用时都与数据库断开连接。这意味着您需要每次重新连接,并丢弃任何缓存的信息。不要这样做,保持联系。

  • 你在每一行之后做出承诺。这会减慢速度。相反,在完成批处理后提交。

  • select + update或insert可以作为单个upsert完成。

  • 您将这么多插入临时表可能是一个性能问题。

  • 如果表中有太多索引可能会降低插入速度。有时最好删除索引,进行大批量更新,然后重新创建它们。

  • 由于您将值直接放入SQL,因此您的SQL向SQL injection attack开放。

,而不是...

  • 使用预准备语句和绑定参数
  • 保持数据库连接
  • 批量更新
  • 仅在更新运行结束时提交
  • UPDATE而不是SELECT + math + UPDATE
  • 中完成所有数学运算
  • 使用" UPSERT"而不是SELECT,而是UPDATEINSERT

首先,准备好的陈述。这些让MySQL编译语句一次然后重用它。我们的想法是你用值的占位符写一个声明。

select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
      keyword=%s and 
      landing_page=%s

然后使用值作为参数执行它,而不是作为字符串的一部分。

self.cursor.execute(
   'select id, position, impressions, clicks, ctr from temp where profile_id=%s and keyword=%s and landing_page=%s',
   (profile_id, keyword, landing_page)
)

这允许数据库缓存预准备的语句,而不必每次都重新编译它。它还避免了SQL注入攻击,聪明的攻击者可以创建一个实际上更像SQL " MORE SQL HERE "的SQL。这是一个非常非常常见的安全漏洞。

注意,您可能需要使用MySQL's own Python database library to get true prepared statements。不要太担心,使用准备好的陈述不是你最大的性能问题。

接下来,您基本上做的是添加到现有行,或者如果没有现有行,则插入新行。这可以通过UPSERT,合并INSERTUPDATE的单个语句更有效地完成。 MySQL has it as INSERT ... ON DUPLICATE KEY UPDATE

要了解这是如何完成的,我们可以将您的SELECT then UPDATE写为一个UPDATE。计算在SQL中完成。

    update temp
    set impressions = impressions + %s,
        clicks = clicks + %s,
        ctr = (ctr + %s / 2)
    where profile_id=%s and
          keyword=%s and
          landing_page=%s

你的INSERT保持不变......

    insert into temp
        (profile_id, landing_page, keyword, position, impressions, clicks, ctr)
        values (%s, %s, %s, %s, %s, %s, %s)

将它们组合成一个INSERT ON DUPLICATE KEY UPDATE。

    insert into temp
        (profile_id, landing_page, keyword, position, impressions, clicks, ctr)
        values (%s, %s, %s, %s, %s, %s, %s)
    on duplicate key update
    update temp
    set impressions = impressions + %s,
        clicks = clicks + %s,
        ctr = (ctr + %s / 2)

这取决于表的键定义为什么。如果您有unique( profile_id, landing_page, keyword ),那么它应该与您的代码相同。

即使你不能进行upsert,你也可以通过尝试SELECT来消除UPDATE,检查它是否有任何更新,以及它是否做了{ {1}}。

批量处理更新。不是调用一个更新和提交的子例程,而是传递一大堆要更新的内容并在循环中处理它们。您甚至可以利用executemany运行具有多个值的相同语句。然后提交。

您可以批量执行INSERTUPSERT可以一次占用多行。例如,这会插入三行。

INSERT

您可以对insert into whatever (foo, bar, baz) values (1, 2, 3), (4, 5, 6), (7, 8, 9) 执行相同操作,从而减少与数据库通信的开销。请参阅this post for an example(在PHP中,但您应该能够适应)。

这会牺牲返回最后一个插入行的ID,但它们是中断。

相关问题