MySQL:在重复密钥更新时使用插入和更新而不是插入是否更快?

时间:2010-03-22 21:37:42

标签: mysql performance

我有一个cron作业,用于更新数据库中的大量行。有些行是新的,因此插入,有些是现有行的更新,因此更新。

我在整个数据的重复密钥更新中使用insert,并在一次调用中完成。

但是 - 我实际上知道哪些行是新的,哪些是更新的,所以我也可以单独进行插入和更新。

分离插入和更新在性能方面有优势吗?这有什么机制?

谢谢!

6 个答案:

答案 0 :(得分:6)

你说

  

我实际上知道哪些行是新的,哪些是更新的,所以我也可以单独进行插入和更新。

如果你知道没有命中INSERT的数据库和UPDATEs,那么运行正确的语句必须比执行INSERT更快... ON DUPLICATE KEY ...

INSERT将不会更快; UPDATE会更快,因为您不必先尝试INSERT。

答案 1 :(得分:5)

在我的测试中使用ON DUPLICATE KEY UPDATE平均比使用Insert / Update慢1.3倍。 这是我的考验:

INSERT / UPDATE(54.07秒)

    <?php 
       $mtime = microtime(); 
       $mtime = explode(" ",$mtime); 
       $mtime = $mtime[1] + $mtime[0]; 
       $starttime = $mtime; 
    ?> 
    <?php
    set_time_limit(0);
    $con = mysql_connect('localhost', 'root', '');
    mysql_select_db('test');

    for ($i = 1; $i <= 1000; $i = $i + 2)
    {
        mysql_query("
                    INSERT INTO users
                    VALUES(NULL, 'username{$i}', 'email.{$i}', 'password{$i}')
                    ");
    }

    for ($i = 1; $i <= 1000; $i++)
    {
        if ($i % 2 == 0)
        {
            mysql_query("
                    INSERT INTO users
                    VALUES(NULL, 'username{$i}', 'email.{$i}', 'password{$i}')
                    ");
        }
        else
        {
             mysql_query("
                        UPDATE users
                        SET (username = 'username{$i}', email = 'email{$i}', password = 'password{$i}')
                        ");
        }
    }
    ?>
    <?php 
       $mtime = microtime(); 
       $mtime = explode(" ",$mtime); 
       $mtime = $mtime[1] + $mtime[0]; 
       $endtime = $mtime; 
       $totaltime = ($endtime - $starttime); 
       echo "This page was created in ".$totaltime." seconds"; 
    ?>

ON DUPLICATE KEY UPDATE(70.4秒)

<?php 
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   $starttime = $mtime; 
?> 
<?php
set_time_limit(0);
$con = mysql_connect('localhost', 'root', '');
mysql_select_db('test');

for ($i = 1; $i <= 1000; $i = $i + 2)
{
    mysql_query("
                INSERT INTO users
                VALUES(NULL, 'username{$i}', 'email.{$i}', 'password{$i}')
                ");
}

for ($i = 1; $i <= 1000; $i++)
{
    mysql_query("
                INSERT INTO users
                VALUES({$i}, 'username{$i}', 'email.{$i}', 'password{$i}')
                ON DUPLICATE KEY UPDATE
                username = 'username{$i}', email = 'email{$i}', password = 'password{$i}'
                ");    
}
?>
<?php 
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   $endtime = $mtime; 
   $totaltime = ($endtime - $starttime); 
   echo "This page was created in ".$totaltime." seconds"; 
?>

答案 2 :(得分:3)

我得到了另一个完全不同的结果。 INSERT ON DUPLICATE比UPATE快!

MySQL版

innodb_version 5.6.13

protocol_version 10

版本5.6.13-enterprise-commercial-advanced

version_compile_machine x86_64

version_compile_os osx10.7

结果

SELECT udf_CreateCounterID(0,CURRENT_DATE);
SELECT @update, @updateend, @updatediff, @insertupdate, @insertupdate_end, @insertupdatediff, @keyval, @countlmt;

@ update = 2013-09-12 17:32:27

@ updateend = 2013-09-12 17:33:01

@ updatediff = 34

@ insertupdate = 2013-09-12 17:32:00

@ insertdate_end = 2013-09-12 17:32:27

@ insertupdatediff = 27

@ keyval = 13

@ countlmt = 1000000

CREATE TABLE `sys_CounterID` (`exch_year` int(11) NOT NULL,
                              `nextID` int(11) NOT NULL,
                               PRIMARY KEY (`exch_year`)
                             ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

测试功能

 CREATE DEFINER=`root`@`localhost` FUNCTION `udf_CreateCounterID`(exchID SMALLINT, listyear DATE) RETURNS int(10) unsigned
 BEGIN
DECLARE keyvalue INT UNSIGNED DEFAULT 0;

SET @countlmt = 1000000;
SET keyvalue = ((exchID % 512) << 9 ) + EXTRACT(YEAR FROM listyear) % 100;

SET @keyval = keyvalue;
SET @retVal =  0;

SET @count = @countlmt;
SET @insertupdate = SYSDATE();

WHILE @count > 0 DO

    INSERT INTO `sys_CounterID`(`exch_year`,nextID)
    VALUE( keyvalue, 1)
    ON DUPLICATE KEY UPDATE 
        nextID = (@retVal := nextID + 1);

    SET @count = @count - 1;

END WHILE;

SET @insertupdate_end = SYSDATE();
SET @insertupdatediff = TIMESTAMPDIFF(SECOND, @insertupdate,@insertupdate_end);


SET @count = @countlmt;
SET @update = SYSDATE();

WHILE @count > 0 DO

    UPDATE sys_CounterID 
    SET nextID = (@retVal := nextID + 1)
    WHERE exch_year = keyvalue;
    SET @count = @count - 1;
END WHILE;

SET @updateend = SYSDATE();
SET @updatediff = TIMESTAMPDIFF(SECOND, @update,@updateend);
RETURN @retVal;
END

答案 3 :(得分:0)

这取决于你使用哪个存储引擎,MyISAM非常擅长选择和插入,因为它可以同时执行它们,但它在写入时会锁定整个表,因此不太适合更新。您如何尝试对其进行基准测试,并找出哪种方法需要更长时间?

答案 4 :(得分:0)

从性能的角度来看,差异在于语句数量 - 对于内存数据集通过网络和解析查询是大部分时间,这就是为什么在单个语句中使用它有助于提高性能。既然你知道哪些需要插入和更新,我不相信你会看到任何性能差异。如果更新使用WHERE语句来索引要更新的记录的ID,则应该看不到性能差异。

答案 5 :(得分:0)

您是否为每条记录使用个别陈述?您可能希望查看批量更新的加载数据infile。我上次尝试时,我们上次获得了一些表现(一年)。