检查带有select的insert是否在pymysql中成功

amrnrhlw  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(275)

我有一个 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 ?

30byixjq

30byixjq1#

首先,回答您的问题:在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 次(最后一行通常为一次),因此总工时(用于选择)将增加 (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_maxnull ,否则插入:

insert ... select ... 
where id > last_id and id <= new_max and <your condition>

然后设置 last_id = new_max 继续循环。
它将查询的数量增加了一倍,这与 limit 带着一个 offset ,你需要知道 id . 它仍然需要您的主键和 where -条件是兼容的(因此您可能需要添加一个适合的索引)。如果您的搜索条件在源表中发现了很大一部分(超过15%或20%),那么使用主键可能是最好的执行计划。
如果您想将其并行化(取决于您的事务需求,如果它可能有价值,请参见上文),您可以首先获得主键的最大值( select max(id) as max_id from ... ),并为每个线程提供一个可处理的范围。e、 g.用于 max_id=3000 三个线程,从其中一个开始 (0..1000), (1001, 2000), (2001..3000) 并将其包含在第一个查询中:

select max(id) as new_max from 
where id > last_id 
  and id >= $threadmin_id and id <= $threadmax_id
  and <your condition>  
order by id limit 1000

如果这些范围大小相等,则可能取决于您的数据分布(您可能会在您的情况下找到更好的范围;但是,计算准确的范围需要执行查询,因此您可能无法做到准确)。

相关问题