mysql批量更新方式的比较

文章通过测试比较了在MySQL中进行批量更新时,使用CASEWHEN、REPLACEINTO、INSERT...ONDUPLICATEKEYUPDATE等方法的效率。结果显示,REPLACEINTO和INSERT...ONDUPLICATEKEYUPDATE在多数场景下表现更优,但可能对主键或主从表造成影响,需谨慎使用。
摘要由CSDN通过智能技术生成

前言

在使用Mysql进行批量更新的时候,总会想哪一种方式效率更高,于是干脆进行了测试。

先说结论:

常见的四种方式有

  1. 直接一条记录一个update
  2. 使用case when
  3. 使用replace into
  4. 使用insert update duplicate on

1w条记录分10次插入时,每种方式平均用时如下(编号对应上面的记录,单位是毫秒):

1:21392

2:318

3:221

4:241

每次插入1w条,重复插入10次,每种方式评价用时如下(方式1直接淘汰不测了):

2:7678

3:919

4:804

所以,replace into 和update duplicate胜出

详细数据

1k条记录重复10次

case when

第0次,花费:1357 第1次,花费:191 第2次,花费:175 第3次,花费:230 第4次,花费:566 第5次,花费:142 第6次,花费:117 第7次,花费:122 第8次,花费:145 第9次,花费:135 平均花费:318

分析认为有可能是插入的是重复的id,有缓存加速的原因,后面几次很快,需要再测1w条记录分10次的情况分析,后者更实际

multiple

即单纯的一次多条update,需要在jdbc的url上加allowMultiQueries=true参数

速度巨慢,不参与1w条的测试

第0次,花费:23846 第1次,花费:23442 第2次,花费:22097 第3次,花费:19793 第4次,花费:19351 第5次,花费:20448 第6次,花费:22631 第7次,花费:19940 第8次,花费:19598 第9次,花费:22783 平均花费:21392

replace into

第0次,花费:1131 第1次,花费:96 第2次,花费:81 第3次,花费:77 第4次,花费:73 第5次,花费:308 第6次,花费:208 第7次,花费:86 第8次,花费:80 第9次,花费:78 平均花费:221

效率相当可观但是颇为危险,诸如有多个主键或主从表的情况容易造成错误

insert duplicate

Mybatis 通过DUPLICATE实现在INSERT中批量高效更新数据_BasicLab基础架构实验室的博客-CSDN博客_mybatis duplicate

上面这篇博客相当不错

第0次,花费:1278 第1次,花费:174 第2次,花费:339 第3次,花费:98 第4次,花费:100 第5次,花费:83 第6次,花费:86 第7次,花费:80 第8次,花费:86 第9次,花费:87 平均花费:241

与replace相差无几

replace与duplicate都非常容易改变主键,而且是不可逆的改变,使用当慎重

1w条数据分10次

case when

第0次,花费:1330 第1次,花费:189 第2次,花费:199 第3次,花费:184 第4次,花费:236 第5次,花费:258 第6次,花费:169 第7次,花费:159 第8次,花费:209 第9次,花费:198 平均花费:313

变化不大,大概是有自适应索引等原因,速度依然不错

replace into

第0次,花费:1133 第1次,花费:108 第2次,花费:103 第3次,花费:96 第4次,花费:104 第5次,花费:70 第6次,花费:63 第7次,花费:94 第8次,花费:100 第9次,花费:217 平均花费:208

变化不大

insert duplicate

第0次,花费:1141 第1次,花费:137 第2次,花费:88 第3次,花费:77 第4次,花费:87 第5次,花费:96 第6次,花费:86 第7次,花费:114 第8次,花费:117 第9次,花费:181 平均花费:212

1w条数据重复10次

case when

第0次,花费:10300 第1次,花费:8184 第2次,花费:7537 第3次,花费:7256 第4次,花费:7336 第5次,花费:7260 第6次,花费:7145 第7次,花费:7249 第8次,花费:7276 第9次,花费:7242 平均花费:7678

replace into

第0次,花费:2140 第1次,花费:2007 第2次,花费:735 第3次,花费:560 第4次,花费:616 第5次,花费:594 第6次,花费:605 第7次,花费:722 第8次,花费:633 第9次,花费:587 平均花费:919

insert duplicate

第0次,花费:1845 第1次,花费:808 第2次,花费:1652 第3次,花费:521 第4次,花费:604 第5次,花费:482 第6次,花费:534 第7次,花费:603 第8次,花费:481 第9次,花费:514 平均花费:804

测试实例

下面列出本人测试时的代码与环境

mysql 8.0+

表结构

DROP TABLE IF EXISTS `updatetest`;
CREATE TABLE `updatetest`  (
  `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
  `col1` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `col2` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `col3` int(0) UNSIGNED NOT NULL,
  `col4` int(0) UNSIGNED NOT NULL,
  `col5` char(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = REDUNDANT STORAGE MEMORY;

SET FOREIGN_KEY_CHECKS = 1;

mapper类

@Mapper
public interface UpdateMapper {

     void updateByMultiple(@Param("list") List<UpdatetestDO> list);

     void updateByCase(@Param("list") List<UpdatetestDO> list);

     void updateByReplace(@Param("list") List<UpdatetestDO> list);

     void updateByDuplicate(@Param("list") List<UpdatetestDO> list);

     void initInsert(@Param("list") List<UpdatetestDO> list);
}

 xml文件

<insert id="initInsert">
        insert into
        updatetest(col1, col2, col3, col4, col5)
        values
        <foreach collection="list" item="it" separator=",">
            (#{it.col1},#{it.col2},#{it.col3},#{it.col4},#{it.col5})
        </foreach>
    </insert>

    <update id="updateByMultiple">
        <foreach collection="list" item="it">
            update
            updatetest
            set
            col1=#{it.col1},
            col2=#{it.col2},
            col3=#{it.col3},
            col4=#{it.col4},
            col5=#{it.col5}
            where id=#{it.id};
        </foreach>
    </update>

    <update id="updateByCase">
        update
        updatetest
        <trim prefix="set" suffixOverrides=",">
            <foreach collection="list" item="it" open="col1 = case id" close="end,">
                when #{it.id} then #{it.col1}
            </foreach>
            <foreach collection="list" item="it" open="col2 = case id" close="end,">
                when #{it.id} then #{it.col2}
            </foreach>
            <foreach collection="list" item="it" open="col3 = case id" close="end,">
                when #{it.id} then #{it.col3}
            </foreach>
            <foreach collection="list" item="it" open="col4 = case id" close="end,">
                when #{it.id} then #{it.col4}
            </foreach>
            <foreach collection="list" item="it" open="col5 = case id" close="end,">
                when #{it.id} then #{it.col5}
            </foreach>
        </trim>
        WHERE id IN
        (<foreach collection="list" item="it" separator=",">
        #{it.id}
    </foreach>)
    </update>
    <insert id="updateByReplace">
        replace into
        updatetest(id,col1, col2, col3, col4, col5)
        values
        <foreach collection="list" item="it" separator=",">
            (#{it.id},#{it.col1},#{it.col2},#{it.col3},#{it.col4},#{it.col5})
        </foreach>

    </insert>
    <insert id="updateByDuplicate">
        insert into
        updatetest(id,col1, col2, col3, col4, col5)
        values
        <foreach collection="list" item="it" separator=",">
            (#{it.id},#{it.col1},#{it.col2},#{it.col3},#{it.col4},#{it.col5})
        </foreach>
        on duplicate key update
        col1=values(col1),
        col2=values(col2),
        col3=values(col3),
        col4=values(col4),
        col5=values(col5)
    </insert>

测试类

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class FrameApplicationTests {
    @Autowired
    UpdateMapper mapper;

    @Test
    public void initInsert() {
        ArrayList<UpdatetestDO> dos = new ArrayList<>();
        for (long i = 1001L; i <= 10000L; i++) {
            UpdatetestDO it = new UpdatetestDO("col1" + i, "col2" + i, i, i, "col5" + i);
            dos.add(it);
        }
        mapper.initInsert(dos);
    }

    @Test
    public void caseTest(){
        long all=0;
        for (int cnt = 0; cnt < 10; cnt++) {

            ArrayList<UpdatetestDO> dos = new ArrayList<>();
            for (long i = 1L; i <= 10000L; i++) {
                UpdatetestDO it = new UpdatetestDO("col1:"+cnt+":" + i, "col2:case:" + i, i, i, "col5:cnt:" + i);
                it.setId(i);
                dos.add(it);
            }
            long start = System.currentTimeMillis();
            mapper.updateByCase(dos);
            long end = System.currentTimeMillis();
            long spend=end-start;
            System.out.println("第"+cnt+"次,花费:"+spend);
            all+=spend;

        }
        System.out.println("平均花费:"+all/10);
    }

    @Test
    public void multipleTest(){
        long all=0;
        for (int cnt = 0; cnt < 10; cnt++) {
            int base=cnt*1000;
            ArrayList<UpdatetestDO> dos = new ArrayList<>();
            for (long i = 1L; i <= 1000L; i++) {
                UpdatetestDO it = new UpdatetestDO("col1:"+base+":" + i, "col2:mul" + i, i, i, "col5:cnt:" + i);
                it.setId(base+i);
                dos.add(it);
            }
            long start = System.currentTimeMillis();
            mapper.updateByMultiple(dos);
            long end = System.currentTimeMillis();
            long spend=end-start;
            System.out.println("第"+cnt+"次,花费:"+spend);
            all+=spend;
        }
        System.out.println("平均花费:"+all/10);
    }

    @Test
    public void replaceTest(){
        long all=0;
        for (int cnt = 0; cnt < 10; cnt++) {
            ArrayList<UpdatetestDO> dos = new ArrayList<>();
            for (long i = 1L; i <= 10000L; i++) {
                UpdatetestDO it = new UpdatetestDO("col1:"+cnt+":" + i, "col2:rep:" + i, i, i, "col5:cnt:" + i);
                it.setId(i);
                dos.add(it);
            }
            long start = System.currentTimeMillis();
            mapper.updateByReplace(dos);
            long end = System.currentTimeMillis();
            long spend=end-start;
            System.out.println("第"+cnt+"次,花费:"+spend);
            all+=spend;
        }
        System.out.println("平均花费:"+all/10);
    }

    @Test
    public void duplicateTest(){
        long all=0;
        for (int cnt = 0; cnt < 10; cnt++) {
            ArrayList<UpdatetestDO> dos = new ArrayList<>();
            for (long i = 1L; i <= 10000L; i++) {
                UpdatetestDO it = new UpdatetestDO("col1:"+cnt+":" + i, "col2:dup:" + i, i, i, "col5:cnt:" + i);
                it.setId(i);
                dos.add(it);
            }
            long start = System.currentTimeMillis();
            mapper.updateByDuplicate(dos);
            long end = System.currentTimeMillis();
            long spend=end-start;
            System.out.println("第"+cnt+"次,花费:"+spend);
            all+=spend;
        }
        System.out.println("平均花费:"+all/10);
    }
}

MySQL批量更新是通过一次性更新多条记录,以提高更新效率和性能的一种方法。常见的MySQL批量更新方法有以下几种: 1. 使用REPLACE INTO批量更新:通过将需要更新的记录一次性插入到一个临时表中,然后使用REPLACE INTO语句将临时表中的数据更新到目标表中。这种方法需要用户具有temporary表的create权限。 2. 使用INSERT INTO ... ON DUPLICATE KEY UPDATE批量更新:通过使用INSERT INTO ... ON DUPLICATE KEY UPDATE语句,可以将需要更新的记录一次性插入到目标表中,如果有重复的记录,则进行更新操作。这种方法适用于目标表有唯一索引或主键的情况。 3. 使用多值语法进行批量更新:通过使用多个值的语法,可以一次性更新多条记录。例如,使用UPDATE语句的多值语法:UPDATE table SET column1 = value1, column2 = value2 WHERE condition,其中value1和value2表示需要更新的多个值。 4. 使用LOAD DATA INFILE进行批量更新:通过将需要更新的数据保存在一个文本文件中,然后使用LOAD DATA INFILE语句将文本文件中的数据批量导入到目标表中。这种方法对于大规模的批量更新非常高效。 需要根据具体的需求和场景选择合适的MySQL批量更新方法。使用批量更新可以显著提高更新效率和性能,避免了逐条更新的低效率和可能导致阻塞的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [大批量更新数据mysql批量更新的四种方法](https://blog.csdn.net/Carey_Lu/article/details/118793662)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [MySQL批量更新的四种方法](https://blog.csdn.net/weixin_45707610/article/details/130900245)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值