for 循环中执行 sql 语句,为什么效率低下?

2020-10-28 16:05:49 +08:00
 zw1one

代码错误示例( java ):

for(PO po : poList){
  dao.insert(po);
}

已经用批量提交 /拼接 insert 语句的方式去除了 for 循环,效率也正常了。但是想问下,大家都知道这种操作它效率低,可是它具体低在哪呢?

我大概猜的几个点:

各位大佬,哪里有参考资料之类的可以看看吗 : (

6055 次点击
所在节点    Java
26 条回复
biepin
2020-10-28 16:11:13 +08:00
每次都进行 insert 操作,每次 insert 操作都是需要花时间的
misaka19000
2020-10-28 16:13:27 +08:00
网络开销也要考虑
mazhan465
2020-10-28 16:14:10 +08:00
每条 insert,DB 都要重新走一遍整个解析,数据检查,写内存,刷盘,一条 insert 插多条数据可以一次将数据写到内存,然后等待刷盘
a719031256
2020-10-28 16:20:07 +08:00
主要应该是网络开销把,数据库跟代码在同一服务器上效率跟批量插入不相上下
chendy
2020-10-28 16:25:36 +08:00
效率低下是相比于批量操作效率低下,循环要访问 n 次数据库,批量就 1 次
zw1one
2020-10-28 16:28:10 +08:00
@a719031256 网络开销指的是:传输这些插入的数据吗?还是与数据库建立链接也有网络开销?因为要插入的数据总量是不变的,两种做法的网络开销是不是差不多呢?
JaguarJack
2020-10-28 16:38:17 +08:00
应该是 insert 这个工作重复做吧
a719031256
2020-10-28 16:38:40 +08:00
@zw1one 传输数据的网络开销
HanMeiM
2020-10-28 17:48:16 +08:00
还有表里有索引的话,每次 insert 是会重建索引树的,这个还是挺麻烦的。
用 foreach 最好开个事务,一次性提上去
AngryPanda
2020-10-28 17:52:45 +08:00
TCP 连接就一次,并没有多次。
QBugHunter
2020-10-28 17:54:23 +08:00
数据库本质上来说是一种读写本地文件,然后,现在有 10 个字符需要写入本地文件
方案 A:打开文件,写入一个字符,关闭文件。然后把该过程重复 10 此
方案 B:打开文件,写入 10 个字符,关闭文件

基本上就是这个样子
chenluo0429
2020-10-28 18:42:10 +08:00
之前短暂维护过一座屎山,平板离线录入数据,然后将数据打包成文件上传到服务器,服务器解析本机的文件,写入本机数据库,然后请求返回成功。因为在 for 循环里面每次只写入一条数据,单次 insert 大概在 10-20ms 。很久没上传的平板数据量巨大,大概需要上传三天,所以总是超时无法上传成功。
l00t
2020-10-28 19:01:59 +08:00
这得看你程序本身和数据库交互的部分是怎么写的。for 不是关键。
yeqizhang
2020-10-28 19:24:23 +08:00
只能说网络开销,你不可能 for 里面每次 insert 前 connect 然后 close 吧
Bromine0x23
2020-10-28 20:46:44 +08:00
条数多的话就主要是等待响应的传播时延了
非批量模式下,要等待前一条执行的结果响应传输回来才能执行下一条,那就多了 (N-1) 个传播时延
Cuo
2020-10-29 00:42:51 +08:00
N+1 问题?
chanyan
2020-10-29 08:44:45 +08:00
实测在 pg 里面,普通业务 10 万数据拼接与同一个事务中进行 10w insert 效率没什么差别。用同一个事务即可,拼接代码的可读性不好
857681664
2020-10-29 09:11:19 +08:00
如果一堆单条 insert 在一个事务里,跟批量 insert 差距忽略不计,如果单条 insert 是每次单开一个事务,那差距大概是几十倍,前几天刚好遇到过这个问题,用 spring 的 scriptUtil.execScript,给的 sql 是大量单条 insert,1000 条插入耗时 2s 多,换成批量插入 100ms 。
xdsty
2020-10-29 09:32:15 +08:00
极客时间 mysql 实战挺不错
xdsty
2020-10-29 09:34:47 +08:00
每次 insert 都要刷 redo log 和 binlog,这都是需要写入硬盘的,所以较慢。
合并为批量 sql,只需要刷一次 redo log 和 binlog,会快一些

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/719480

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX