2017-08-29 66 views
0

我的问题解释如下。SQL查询执行时间急剧增加

这是我的服务器上运行我的PHP代码现在:

$limit = 10000; 
$annee = '2017'; 

//Counting the lines I need to delete 
$sql = " SELECT COUNT(*) FROM historisation.cdr_".$annee." a 
     INNER JOIN transatel.cdr_transatel_v2 b ON a.id_cdr = b.id_cdr "; 
$t = $db_transatel->selectAll($sql); 

//The number of lines I have to delete 
$i = $t[0][0]; 

do { 

    if ($i < $limit) { 
     $limit = $i; 
    } 
    //The problem is comming from that delete 
    $selectFromHistoryAndDelete = " DELETE FROM transatel.cdr_transatel_v2 
            WHERE id_cdr IN (
             SELECT a.id_cdr FROM historisation.cdr_".$annee." a 
             INNER JOIN (SELECT id_cdr FROM historisation.cdr_transatel_v2) b ON a.id_cdr = b.id_cdr 
            ) 
            LIMIT " . $limit; 
    $delete = $db_transatel->exec($selectFromHistoryAndDelete, $params); 

    $i = $i - $limit; 

} while ($i > 0); 

The execution of the query.

正如你可以在图片中看到,在第195循环的执行时间为13和17秒之间。 它在第195次循环中增加到73秒,在第196次循环中增加到1305秒。

现在查询运行2000秒。

查询正在删除没有人正在使用的测试表中的行。

我正在删除10000行10000以使查询快速且不超载服务器。

我想知道为什么执行时间会像这样增加,尽管最终会更快,因为我通过内部连接会更快,因为它们在表中的行数较少。

有没有人有想法?

编辑:表引擎是MyISAM。

+0

回滚段。我认为DB保持在某个地方删除行来处理可能的回滚。尝试在每次删除后提交。 – StanislavL

+0

不幸的是,我已经尽力了,但是我的表在MyIASM中,并且它不支持回滚和提交。 – McKenneii

+1

桌子很大时,IN太慢。使用JOIN查找要删除的行要快得多。 – vladatr

回答

0

根据您的最新评论,内部联接是多余的,因为您要从包含要加入的值的表中删除。实质上,您必须处理b.id_cdr = a.id_cdr两次,因为cdr_2017上比较的值数量不会被内部联接更改,只是查询要删除的值的数量。

至于增量缓慢的原因,这是因为您手动执行与SELECT cdr_id FROM cdr_2017 LIMIT 10000 OFFSET x相同的功能。

也就是说,您的查询必须对cdr_2017执行全表扫描以确定要删除的id值。在您删除这些值时,SQL优化器必须通过cdr_2017表进一步移动以检索值。

DELETE FROM IN(1,2,3,...10000) 
DELETE FROM IN(1,2,3,...20000) 
... 
DELETE FROM IN(1,2,3,...1000000) 

假设cdr_id得到的是增量的主键,来解决你可以使用从cdr_2017检索到的最后一个索引来筛选选定的值的问题。 这会更快,因为您现在正在利用查询两侧的索引值,因此不再需要全表扫描来验证连接的记录。

$sql = " SELECT COUNT(a.cdr_id) FROM historisation.cdr_".$annee." a 
     INNER JOIN transatel.cdr_transatel_v2 b ON a.id_cdr = b.id_cdr "; 
$t = $db_transatel->selectAll($sql); 

//The number of lines I have to delete 
$i = $t[0][0]; 

//set starting index 
$previous = 0; 

do { 

    if ($i < $limit) { 
     $limit = $i; 
    } 

    $selectFromHistoryAndDelete = 'DELETE d 
FROM transatel.cdr_transatel_v2 AS d 
JOIN (
    SELECT @previous := cdr_id AS cdr_id 
    FROM historisation.cdr_2017 
    WHERE cdr_id > ' . $previous . ' 
    ORDER BY cdr_id 
    LIMIT 10000 
) AS a 
ON a.cdr_id = d.cdr_id'; 

    $db_transatel->exec($selectFromHistoryAndDelete, $params); 

    //retrieve last id selected in cdr_2017 to use in next iteration 
    $v = $db_transatel->selectAll('SELECT @previous'); //prefer fetchColumn 
    $previous = $v[0][0]; 

    $i = $i - $limit; 

} while ($i > 0); 

//optionally reclaim table-space 
$db_transatel->exec('OPTIMIZE TABLE transatel.cdr_transatel_v2', $params); 

你也可以重构使用cdr_id > $previous AND cdr_id < $last的限制条款,这也应该改进性能,删除订单。

虽然我想说明的是,在MyISAM数据库引擎的操作过程中会执行cdr_transatel_v2上的表锁。由于MySQL处理并发会话和查询的方式,以这种方式进行批量删除没有太多收益,并且仅适用于InnoDB和事务。尤其是当使用PHP和FastCGI时,与Apache mod_php相反。由于不在cdr_transatel_v2上的其他查询仍将被执行,并且cdr_transatel_v2上的写入操作仍将排队。如果使用mod_php,我会将限制减少到1,000记录以减少队列时间。 欲了解更多信息,请参阅https://dev.mysql.com/doc/refman/5.7/en/internal-locking.html#internal-table-level-locking


替代做法。

考虑到需要删除的大量记录,当删除的记录超过保留的记录时,通过使用INSERT而不是DELETE来反转操作会更有益。

#ensure the storage table doesn't exist already 
DROP TABLE IF EXISTS cdr_transatel_temp; 

#duplicate the structure of the original table 
CREATE TABLE transatel.cdr_transatel_temp 
LIKE transatel.cdr_transatel_v2; 

#copy the records that are not to be deleted from the original table 
INSERT transatel.cdr_transatel_temp 
SELECT * 
FROM transatel.cdr_transatel_v2 AS d 
LEFT JOIN historisation.cdr_2017 AS b 
ON b.cdr_id = d.cdr_id 
WHERE b.cdr_id IS NULL; 

#replace the original table with the storage table 
RENAME TABLE transatel.cdr_transatel_v2 to transatel.backup, 
    transatel.cdr_transatel_temp to cdr_transatel_v2; 

#remove the original table 
DROP TABLE transatel.backup; 
+0

感谢您花时间思考并回答。如果我能找到第一个解决方案,我会明确地尝试第一个解决方案。 @Vladatr谈到了IN表演,所以我没有它就做了我的查询。现在删除运行在〜0.07秒。我会尝试查看您的查询是否更快。再次感谢您的帮助 ! – McKenneii