当前位置:Gxlcms > 数据库问题 > MySQL批量插入的分析以及注意事项

MySQL批量插入的分析以及注意事项

时间:2021-07-01 10:21:17 帮助过:11人阅读

!!!

  如果用这种方式进行大量数据的入库,哈哈,花的时间可以喝好多杯咖啡了。

2.2、一次插入多条数据

  上面已经说到了,一次插入一条数据的主要缺陷是:需要建立N次连接,然后传输N连接,因为连接池的存在,可以忽略连接耗时,但是传输N次的耗时,不可小觑,所以我们可以从这方面进行考虑优化。

  比如,一个工人负责将100块砖从A点搬到B点,每次搬1块砖,花费1个单位时间,那么搬完100块砖,需要100单位时间(不考虑来回);

  如果一次搬5块砖,那么只需要20单位时间,是不是快了很多呢?

  同理,我们使用insert也可以进行批量插入数据:

insert into user 
	(id, name, gender, addr, status) 
values 
	(2, ‘bbb‘, 0, ‘shanghai‘, 1),
	(3, ‘ccc‘, 1, ‘hangzhou‘, 0),
	(4, ‘ddd‘, 0, ‘chongqing‘, 0);

  这样就可以一次性插入3条数据了。

  对于客户端来说,只需要进行拼接sql语句即可,然后将拼接后的sql一次性发给MySQL Server就可以了。

  注意,SQL要使用拼接,而不是说预处理!!!

  预处理的作用是避免频繁编译sql、sql注入;使用预处理来进行批量插入时,使用循环每次设置占位符值,这个和一次插入一条命令是等价的,如下面的示例,其实执行了3次1条记录插入:

<?php
    $pdo = new PDO("mysql:host=localhost;dbname=test","root","root");
    $sql = "insert into user (id, name, gender, addr, status) values (?,?,?,?,?)";
    $stmt = $pdo->prepare($sql);

    $stmt->execute(array("5", "eee", "1", "PEK", 1));
    $stmt->execute(array("6", "fff", "0", "SHA", 0));
    $stmt->execute(array("7", "ggg", "1", "LNL", 1));
 ?>

  正确的方式:

<?php
    $pdo = new PDO("mysql:host=localhost;dbname=test","root","root");
    $sql = ‘insert into user (id, name, gender, addr, status) values ‘;

    // 可以使用循环进行sql拼接
    $sql .= ‘("5", "eee", "1", "PEK", 1),‘;
    $sql .= ‘("6", "fff", "0", "SHA", 0),‘;
    $sql .= ‘("7", "ggg", "1", "LNL", 1)‘; 

    $pdo->exec($sql);
 ?>

  如果是Java可以使用原生JDBC,进行上面一样拼接,就不写代码了;

  如果Java使用Mybatis的话,可以使用<foreach>标签,

<insert id="batchInsert" parameterType="list">
    insert ignore into user (id, name, gender, addr, status) values
    <foreach collection="list" item="item" separator=",">
        (
	        #{item.id,jdbcType=INT}, 
	        #{item.name,jdbcType=VARCHAR}, 
	        #{item.gender,jdbcType=BIT},
	        #{item.addr,jdbcType=VARCHAR}, 
	        #{item.status,jdbcType=BIT}
        )
    </foreach>
</insert>

3、拓展一下

  批量insert,每次insert的量是多少合适呢?

  以上面工人搬砖的例子,一次搬5块砖,需要20单位时间,那岂不是1次搬100块砖,只需要1单位时间了?是这个逻辑,但是这样是不行的,需要看实际情况!!!

  这个实际情况是什么呢?不好说,比如一个比较强壮的工人,一次100块砖,不是难事;如果工人没那么强转,一次100块砖,可能直接把工人给干倒了,1块砖也搬不了,这时可不止100单位时间。

  另外,放砖的B点,是不是能一次接收100块砖,这也是一个问题。

  上面的例子,类比到insert批量插入,就需要注意:

  1、要根据情况设置一次批量插入的数据量,数据量大,在网络中传输的事件也越久,出现问题的可能也越大;

  2、除了网络,还要看机器配置,MySQL Server配置差了,sql写得再好,效率也不会太高;

  3、另外批量这个词,是指一次插入多条数据,我们除了要注意数据的条数,还要注意一条数据的大小,举个例子:比如一条记录的数据量有1M,10条记录的数据量就10M,这时一次插100条,100M数据,嘿嘿,你试试看!!所以,一次插入多少数据,一定要经过多次测试后再决定,别人1次插100条最优,你可能1次插10条才最优,没有绝对的最优值(批量插入未必总是比单条插入效率高)。

  4、数据库有个参数,max_allowed_packet,也就是每一个包(sql)命令大小,默认是1M,那么sql的长度大于1M就会报错。你可能会说,咱们把这个参数设成10M,100M不就行了???对呀,没毛病,但你是DBA吗?你有权限吗?即使调大这一个参数,你要知道影响的可不止你这一张表,而是整个DB Server,那影响的可是很多库,很多表。

  5、批量插入并不是越快越好,我们可能希望越快越好,这很正常,节省时间嘛。但是我们一定要知道,数据库分读写,有集群,这就意味着,需要同步!!!如果有分库分表分区的情况,如果短时间内插入的数据量太大,数据库同步可能就会比较迷了,读写数据不一致的情况在所难免了,可能会因为一张表的批量插入,影响整个DB服务组的同步,同时还要考虑并发问题,哈哈哈。 

4、Other

  可以注意一下,我在上面写的insert语句中,基本每一条命令都写了插入的字段,如下:

insert into user (id, name, gender, addr, status) values (1, ‘aaa‘, 1, ‘beijing‘, 1);

  其实我知道表的各个字段的排列顺序,完全可以省略字段名,如下:

insert into user values (1, ‘aaa‘, 1, ‘beijing‘, 1);

  这两种方式的效率,这里就不谈了,不过第一种方式,在某些场景有优势,举个例子:比如user表中增加create_time、update_time:

CREATE TABLE `user` (
  `id` 		int(11) 	NOT NULL AUTO_INCREMENT COMMENT ‘编号‘,
  `name` 	varchar(40) NOT NULL COMMENT ‘姓名‘,
  `gender` 	tinyint(1) 	DEFAULT ‘0‘ COMMENT ‘性别:1-男;2-女‘,
  `addr` 	varchar(40) NOT NULL COMMENT ‘住址‘,
  `status` 	tinyint(1) 	DEFAULT ‘1‘ COMMENT ‘是否有效‘,
  `create_time` timestamp  DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp  DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

  如果没有强制要求create_time和update_time必须从客户端接收,那么完全可以用默认值,insert的时候不用下面的语句:

--- 强制create_time和update_time使用client传递值
insert into user 
	(id, name, gender, addr, status, create_time, update_time) 
values 
	(2, ‘bbb‘, 0, ‘shanghai‘, 1, ‘2019-11-09 18:00:00‘, ‘2019-11-09 18:00:00‘),
	(3, ‘ccc‘, 1, ‘hangzhou‘, 0, ‘2019-11-09 18:00:00‘, ‘2019-11-09 18:00:00‘),
	(4, ‘ddd‘, 0, ‘chongqing‘, 0, ‘2019-11-09 18:00:00‘, ‘2019-11-09 18:00:00‘);

--- create_time和update_time不需要强制使用client传递值,可以使用默认值
insert into user 
	(id, name, gender, addr, status) 
values 
	(2, ‘bbb‘, 0, ‘shanghai‘, 1),
	(3, ‘ccc‘, 1, ‘hangzhou‘, 0),
	(4, ‘ddd‘, 0, ‘chongqing‘, 0);

  类似的,对于有些字段有默认值,并且批量插入的时候,都使用默认值时,可以省略该字段,因为拼接sql的时候可以少拼接一点,网络传输的数据就少一点,能提升一点是一点吧,这个还得看实际情况。

MySQL批量插入的分析以及注意事项

标签:replace   targe   test   逻辑   tiny   create   参数   type   别人   

人气教程排行