代码之家  ›  专栏  ›  技术社区  ›  Praneeth Peiris

检查PyMySQL中的SELECT插入是否成功

  •  0
  • Praneeth Peiris  · 技术社区  · 7 年前

    我有一个 INSERT SELECT 陈述。但是自从 返回数百万条记录,这给MySQL服务器带来了太多的负载。所以,我们决定打破 选择 查询成部分并通过 LIMIT

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

    我们将继续增加起始值和结束值,直到 回报 0 排。我也在考虑让这个多线程。

    我需要执行 选择 插入 ?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Solarflare    7 年前

    首先,回答您的问题:在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 对于第一行),所有连续偏移都将移动,您可以跳过一行或获取一行两次。您可能需要锁定行,这可能会阻止并行运行查询(因为它们锁定了相同的行),如果可以拆分事务,也可能会影响决策(请参阅第二个要点)。
    • 抵消 n 零件,第一行需要处理 n个 次(最后一行通常为一次),因此总工时(供选择)将增加 (n^2-n)/2 . 因此,特别是如果选择行是最相关的部分(请参阅第1个要点),这实际上会使您的情况更糟:最后一次运行将需要找到与当前查询相同数量的行(尽管它丢弃了大多数行),甚至可能需要更多的资源,这取决于 .

    你也许可以绕过一些 -在条件中使用主键的问题,例如,有一个包含如下内容的循环:

    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_max

    它将查询数加倍,与 限制 用一个 ,你需要知道 id . 它仍然需要您的主键和 -条件要兼容(因此可能需要添加适合的索引)。如果搜索条件发现源表的很大一部分(超过15%或20%),那么使用主键可能是最好的执行计划。

    如果您想对其进行并行处理(取决于您的事务需求,如果它有潜在价值,请参见上文),您可以首先获取主键的最大值( select max(id) as max_id from ... ),并为每个线程提供可使用的范围。E、 g.用于 max_id=3000 和3个线程,从其中一个开始 (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 
    

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