检查在PyMySQL中使用SELECT的INSERT是否成功

时间:2018-08-23 09:55:08

标签: python mysql pymysql

我有一个INSERT查询,它从SELECT语句中获取值。但是由于SELECT返回数百万条记录,因此它在MySQL服务器上增加了太多的负载。因此,我们决定将SELECT查询分为多个部分,并通过使用LIMIT子句执行。

INSERT INTO target_table 
    SELECT * FROM source_table
    WHERE my_condition = value
    ...
    LIMIT <start>, <end>

我们将继续增加起始值和结束值,直到SELECT返回0行为止。我也正在考虑使它成为多线程。

我该如何使用PyMySQL?

我是否需要执行SELECT,获取结果然后生成INSERT

1 个答案:

答案 0 :(得分:1)

首先,回答您的问题:在PyMySQL中,您可以通过cursor.execute获得该值:

execute(query, args=None)

Execute a query

Parameters:   
    query (str) – Query to execute.
    args (tuple, list or dict) – parameters used with query. (optional)

Returns: Number of affected rows

因此您可以重复执行查询,直到得到的值小于所选范围。

无论如何,请考虑:

  • 首先应该检查是否可以优化select(假设它不像示例中那样简单),例如通过添加索引。您可能还想测试一下选择和实际插入之间的区别,以大致了解哪个部分更相关。
  • 如果插入引起了问题,则可能是由于事务的大小所致。在那种情况下,将其拆分只会在您还可以拆分事务的情况下减少问题(尽管由于您考虑并行执行查询,所以这似乎没有必要)
  • 如果一个查询产生过多的(cpu)负载,则并行运行该查询的多个实例最多只能将其分布在多个内核上,这实际上将减少其他查询的可用cpu时间。如果“负载”与I / O负载,有限资源的影响或“一般响应性”有关,则可能会出现以下情况:小查询可能会在内存中生成一个小的临时表,大查询会在磁盘上生成一个大的临时表(尽管专门针对offset,这不太可能,请参阅下文。)否则,通常需要添加一些小暂停连续运行的(足够小的)零件之间,以将相同的工作量分散在更长的时间上。
  • limit仅在拥有order by(可能是通过主键)的情况下才有意义,否则,在连续运行中,第m行可以与以前不同(因为顺序不固定)。这可能会或可能不会增加负载(和资源需求),具体取决于您的索引和您的where条件。
  • 对于源表的更新也是如此,就像您在结果集中添加或删除一行(例如,更改第一行的my_condition的值)一样,所有连续的偏移量都会移动,并且您可能会跳过一行或两次获得一行。您可能需要锁定行,这可能会阻止并行运行查询(因为它们锁定相同的行),并且还可能影响是否可以拆分事务的决定(请参见第二个要点)。
  • 使用offset要求MySQL首先查找然后跳过行。因此,如果您将查询分为n个部分,则第一行将需要处理n次(而最后一行通常需要处理一次),因此(用于选择)的总工作量将增加{ {1}}。因此,特别是如果选择行是最相关的部分(请参阅第一个要点),这实际上会使您的情况更糟:仅最后一次运行将需要查找与当前查询相同的行数(尽管它会抛出大部分它们消失),甚至可能需要更多资源,具体取决于(n^2-n)/2的效果。

通过使用条件中的主键,您也许可以解决一些order by问题,例如包含这样的循环:

offset

如果select max(id) as new_max from where id > last_id and <your condition> order by id limit 1000 -- no offset! new_max,则退出循环,否则执行插入操作:

null

然后设置insert ... select ... where id > last_id and id <= new_max and <your condition> 并继续循环。

它将查询数量加倍,与last_id = new_maxlimit相比,您需要知道实际的offset。它仍然需要您的主键和您的id-条件兼容(因此您可能需要添加适合的索引)。无论如何,如果您的搜索条件找到了源表的很大一部分(大约超过15%或20%),那么使用主键仍然是最佳的执行计划。

如果要对此进行并行化(取决于您的交易要求以及是否值得,请参见上文),您可以首先获取主键(where)的最大值,并为每个线程分配一个范围跟...共事。例如。对于select max(id) as max_id from ...和3个线程,以max_id=3000中的一个开始,并将其包含在第一个查询中:

(0..1000), (1001, 2000), (2001..3000)

如果这些范围的大小相等,则可能取决于您的数据分布(并且您可能会在情况中找到更好的范围;尽管要计算确切的范围仍需要执行查询,所以您可能并不准确)。 / p>