首先,回答你的问题:在 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-th 行可能与以前不同(因为顺序不固定)。这可能会也可能不会增加负载(和资源需求),具体取决于您的索引和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_max 是null,则退出循环,否则执行插入:
insert ... select ...
where id > last_id and id <= new_max and <your condition>
然后设置last_id = new_max 并继续循环。
它使查询数量翻倍,与limit 和offset 相比,您需要知道实际的id。它仍然需要您的主键和您的 where-condition 兼容(因此您可能需要添加适合的索引)。如果您的搜索条件在您的源表中找到了相当大的百分比(超过大约 15% 或 20%),那么无论如何使用主键可能是最好的执行计划。
如果你想并行化这个(取决于你的事务要求,如果它可能有价值,见上文),你可以首先获得主键的最大值 (select max(id) as max_id from ...) ,并给每个线程一个工作范围和。例如。对于 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
如果这些范围大小相同,则可能取决于您的数据分布(并且您可能会在您的情况下找到更好的范围;但计算确切的范围需要执行查询,因此您可能无法准确)。