【问题标题】:MySQL: Is it faster to use inserts and updates instead of insert on duplicate key update?MySQL:使用插入和更新而不是重复键更新时插入是否更快?
【发布时间】:2010-03-22 21:37:42
【问题描述】:

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

我在整个数据的重复键更新上使用插入并在一次调用中完成。

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

将插入和更新分开在性能方面有优势吗?这背后的机制是什么?

谢谢!

【问题讨论】:

  • 我会删除已知行,然后我会再次插入它们。这仅在您没有影响其他表的触发器或外键时才有效。
  • @Kevin Crowell 我不明白你的意思。我提到这只有在他没有的情况下才有效。
  • 也考虑使用“替换” - dev.mysql.com/doc/refman/5.1/en/replace.html

标签: mysql performance


【解决方案1】:

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

插入/更新(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"; 
    ?>

重复密钥更新(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】:

    你说

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

    如果您无需访问数据库就知道哪些是 INSERT 哪些是 UPDATE,那么运行正确的语句必须比执行 INSERT ... ON DUPLICATE KEY ... 更快...

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

    【讨论】:

      【解决方案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
      

      【讨论】:

      • 我也有这个。我想做一个大规模的更新电话。更新花费的时间太长,并且会锁定大部分数据库。 Insert ... on duplicate key update 要快得多,并且不会锁定大部分数据库。没有对此进行任何衡量,但这很有趣。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          您是否对每条记录使用单独的语句?您可能希望查看加载数据 infile 以进行批量更新。上次(一年)当我尝试这样做时,我们获得了一些性能。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-08-02
            • 1970-01-01
            • 2014-02-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-15
            • 1970-01-01
            相关资源
            最近更新 更多