以下哪项会有更好的表现?
(1) **INSERT IGNORE**
cursor.execute('INSERT IGNORE INTO table VALUES (%s,%s)')
(2) **SELECT or CREATE**
cursor.execute('SELECT 1 FROM table WHERE id=%s')
if not cursor.fetchone():
cursor.execute('INSERT INTO table VALUES (%s,%s)')
我必须花费数百万的时间来完成这个模式,所以我希望找到这种模式的最佳性能。哪一个最好?为什么呢?
答案 0 :(得分:2)
由于多种原因,insert ignore
是更好的方法。
就性能而言,只编译和执行一个查询,而不是两个。这节省了将数据移入和移出数据库的开销。
在维护方面,只有一个查询更易于维护,因为逻辑只在一个地方。例如,如果您添加了where
子句,则更有可能错过在两个单独的查询中添加它。
就准确性而言,只有一个查询应该没有(或至少很少)竞争条件的机会。如果在select
和insert
之间插入了一行,那么您仍会收到错误。
但是,insert ignore
优于insert . . . on duplicate key update
。后者只能避免重复问题的错误。 insert ignore
可能会忽略您实际关心的错误。
顺便说一下,你应该检查声明中的错误。
答案 1 :(得分:1)
对于大多数性能问题,最好的方法是尝试两种方式并测量它们以查看实际上哪个更快。大多数时候,有很多影响性能的小事情在表面上并不明显。试图提前预测某些事情的表现通常需要比进行测试更长的时间,甚至可能无法准确地进行。
但重要的是要尽可能小心地模拟您的实际生产条件。正如我之前所说,小事情可以对性能产生重大影响,并且您希望通过在测试环境和生产环境之间更改其中一个来避免使测试失效。
使用SQL性能时,最相关的项之一是测试期间数据库的内容。对于许多行,在几行中表现良好的查询变得非常慢。或者,当所有数据非常相似时快速查询在非常多样化时变得非常慢。最好的方法(如果可能)是创建生产数据库的克隆,在其中运行测试。这样,您就确定不会在不准确的测试环境中欺骗自己。
一旦您的测试运行,您可能希望运行数据库的explain plan
等效项,以确切了解每种方法的实际情况。这通常允许您开始调整以消除明显的问题。有时候,这会给变化带来足够的差异,这种变化更快,甚至可以建议第三种方法来打败它们。
答案 2 :(得分:0)
对于单个或几个条目,我会毫不怀疑地使用第一种方法“INSERT IGNORE”。
我们不了解您的案例的详细信息,但是如果您有批量插入(因为您提到需要运行数百万次),那么启动插入性能的关键是使用1个insert语句对于大量条目而不是每个条目的插入语句。
这可以通过以下方式实现:
使用INSERT IGNORE。
INSIG IGNORE INTO表VALUES(id1,'val1'),(id2,'val2')....
或者,您可以做的是执行单个select语句,对于大量条目,获取现有条目,即:SELECT id FROM table WHERE id in(id1,id2,id3 ....) 然后以编程方式在您的代码中从初始列表中排除 从db检索到的那些。 然后运行INSERT语句:
INSERT INTO表VALUES(id1,'val1'),(id5,'val5')..
通常情况下,我们希望INSERT IGNORE批量插入在db引擎处理后是最佳的,但这不能保证。因此,对于您的解决方案,最好使用大量数据对这两种情况进行小型验证。
如果您不想运行小的比较测试来验证,那么您可以在测试期间使用INSERT IGNORE批量插入(在两种情况下都需要),以防您注意到缓慢,您可以尝试第二种方法。
通常情况下,第二种方法会很快,因为第一次选择是在大量的id(pk)上完成的,因此查询速度快,并且比每个条目运行一个select更好。以编程方式过滤ID也很快。