MySQL批量插入的分析以及注意事项
目录
1、背景
2、两种方式对比
2.1、一次插入一条数据
2.2、一次插入多条数据
3、拓展一下
4、Other
1、背景
我们在工作中基本都会碰到批量插入数据到DB的情况,这个时候我们就需要根据不同的情况选择不同的策略。
只要了解sql,就应该知道,向table中插入数据的命令,至少有insert和replace这两种,使用哪一种命令,和自己的业务有关;
本文就针对insert进行批量插入进行阐述,然后根据自身经历分享几个注意事项。
2、两种方式的对比
即使是insert命令,他也是有多种插入数据的方式的。但这里就不深入了解底层insert是怎么做的了,那个已经超出本人的知识范畴,哈哈。
但是我们可以大致了解MySQL的执行命令时的初略步骤:
1、首先建立连接(Socke连接);
2、Client将要执行的sql命令通过TCP连接,发给Server;
Client,可以理解为我们用各种语言写的项目程序(客户端);
Server,就是数据库Server,负责执行。
3、数据库Server收到数据(sql)后,会解析sql,然后进行处理;
4、将处理结果返回客户端。
有了上面的流程,我们就开始说insert的两种插入方式区别,下面是测试使用的表:
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 '是否有效:1-有效;2-无效',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
2.1、一次插入一条数据
最初学习数据库,都知道使用insert可以实现数据插入,比如向user表中插入一条数据:
mysql> insert into user (id, name, gender, addr, status) values (1, 'aaa', 1, 'beijing', 1);
Query OK, 1 row affected (0.00 sec) mysql> select * from user;
+----+------+--------+---------+--------+
| id | name | gender | addr | status |
+----+------+--------+---------+--------+
| 1 | aaa | 1 | beijing | 1 |
+----+------+--------+---------+--------+
1 row in set (0.00 sec)
这是最简单的方式了,当然这是在命令行里面,当然命令行也是一种客户端;
如果是客户端我们代码的程序,比如Java利用jdbc来执行sql,也是传给MySQL Server上面执行的insert命令;
上面的insert命令的确是能插入数据数据的,也就是每执行一条insert命令,就需要通过网络将命令发送给MySQL Server解析运行,如果有上千万行数据需要插入,那么是不是需要进行上千万次连接传输呢?虽然现在可以使用连接池,但是传输的次数是是躲不掉的。
使用insert一次插入一条数据的这种方式,绝大多数都是使用这种方式,来进行少量的数据插入!!!
如果用这种方式进行大量数据的入库,哈哈,花的时间可以喝好多杯咖啡了。
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批量插入的分析以及注意事项的更多相关文章
- Mybatis+mysql批量插入性能分析测试
前言 今天在网上看到一篇文章(后文中的文章指的就是它) https://www.jianshu.com/p/cce617be9f9e 发现了一种有关于mybatis批量插入的新方法,而且看了文章发现我 ...
- mybatis+mysql批量插入和批量更新、存在及更新
mybatis+mysql批量插入和批量更新 一.批量插入 批量插入数据使用的sql语句是: insert into table (字段一,字段二,字段三) values(xx,xx,xx),(oo, ...
- Oracle/Mysql批量插入的sql,效率比较高
1.oracle 批量插入: insert into tableName(col1,col2,col3...) select 1,'第一行第一列值','第二列值' from dual union ...
- mysql批量插入简单测试数据
mysql批量插入简单测试数据 # 参考网址: https://www.2cto.com/database/201703/618280.html 1.mysql创建测试表 CREATE TABLE ` ...
- MySQL 批量插入值
MySQL 批量插入值 今天遇到个问题,需要从类似以下语句中的各个小括号[其实也有中括号等]抽取相关字段进行组合[相关规则,此处略去不提],并保存到数据库中,此时如果每次插入一条,会连续进行多次插入, ...
- mybatis + mysql 批量插入、删除、更新
mybatis + mysql 批量插入.删除.更新 Student 表结构 批量插入 public int insertBatchStudent(List<Student> studen ...
- mybatis oracle mysql 批量插入时的坑爹问题--需谨记
mybatis oracle mysql 批量插入一.oracle的批量插入方式insert into db(id, zgbh, shbzh) select '1', '2', '3' from du ...
- Mysql批量插入返回Id错乱(原因分析)
在项目中经常会有如下场景: 往数据库中批量插入一批数据后,需要知道哪些插入成功,哪些插入失败了. 这时候往往会有两种思路,一个是在插入之前判断相同的记录是否存在,过滤掉重复的数据:另外一种就是边插入边 ...
- MYSQL批量插入数据库实现语句性能分析
假定我们的表结构如下 代码如下 CREATE TABLE example ( example_id INT NOT NULL, name VARCHAR( 50 ) NOT NULL, value ...
随机推荐
- 软件开发工具(第9章:使用Eclipse进行C/C++开发)
一.安装MinGW MinGW是指用来生成可执行文件的编译环境,它是开发C/C++项目 的工具集.为了能够使用Eclipse CDT编译且运行C和C++程序,必须 要安装一个C/C++编译器. 下载: ...
- Mysql Hash索引和B-Tree索引区别(Comparison of B-Tree and Hash Indexes)
上篇文章中说道,Mysql中的Btree索引和Hash索引的区别,没做展开描述,今天有空,上Mysql官方文档找到了相关答案,看完之后,针对两者的区别做如下总结: 引用维基百科上的描述,来解释一下这两 ...
- Java BIO及实现
发现日常学习过的知识不久就会遗忘,在此只是整理并记录一下学习笔记,做个回忆,并方便以后查阅,若有错误,欢迎指正 网络模型:TCP/IP网络模型是从OSI七层模型中演化来的,osi模型分为物理层,数据链 ...
- Mac部署hadoop3(伪分布式)
环境信息 操作系统:macOS Mojave 10.14.6 JDK:1.8.0_211 (安装位置:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jd ...
- 你真的了解Web前端开发吗?未来前端远比你想的有前途!
近几年来,随着 HTML5.JS 的流行,前端这个职业火热了起来!不少人发出疑惑,前端以后还会更有前途吗? 我只能告诉你:前端不灭 现在都明白了用户体验至上,还要用着舒服 后端提供床,前端提供颜值高的 ...
- Potato土豆win综合提权
0x01 NBNS和WDAP NBNS: 在 Windows 系统中的另外一种名称就是 NetBIOS 名称,准确的说 NetBIOS 名称并非是一种名字系统,而是 Windows 操作系统网络的一个 ...
- Python3之多线程学习
这里做一个自己复习多线程的笔记 Python中使用线程有两种方式:函数或者用类来包装线程对象. 函数式:调用 _thread 模块中的start_new_thread()函数来产生新线程.语法如下: ...
- 全平台正向tcp端口转发工具rinetd的使用
Linux下做地址NAT有很多种方法.比如haproxy.nginx的4层代理,linux自带的iptables等都能实现.其实,Linux下有一个叫rinetd的工具,安装简单,配置也不复杂. 下载 ...
- 小白学 Python(4):变量基础操作
人生苦短,我选Python 引言 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 前面的文章中,我们介绍了 ...
- 这可能就是你苦苦寻找免费、高颜值、功能强大的 Markdown 编辑器(共5款)
本文作者 | HelloGitHub-小猪蹄 Markdown 是一个轻量级的标记语言,语法简单.容易上手,它深受程序员.博客主等人群的钟爱.随着越来越多的博客系统支持 Markdown,它也开始越来 ...