数据库批量插入操作指南 (数据库批量插入怎么做)

在日常开发工作中,我们常常需要向数据库中插入大量数据。对于一次只能插入一条数据的操作,手动添加显然是个非常繁琐的过程。此时,批量插入就成为了更好的选择。

本文将介绍数据库批量插入操作的基础理论、使用方法以及需要注意的事项,希望能够为开发者们提供一些实用的经验。

一、什么是批量插入

批量插入,顾名思义就是一次性向数据库中插入多条数据。通常情况下,批量插入的操作速度与单次插入相比要快得多。原因在于,多条数据的集中操作可以将多个查询请求一次性发送给数据库,减少了连接和释放时间。此外,批量插入还能够减少数据库日志记录,避免了多余的系统开销。

二、批量插入的技术实现

1. 数据库支持批量插入

首先要注意的是,不是所有数据库都支持批量插入。在使用之前,需要先确认数据库是否支持批量插入操作。大多数主流的数据库如MySQL、Oracle、SQLServer等都支持此操作。

2. 使用预编译语句

批量插入需要使用到SQL语句,这些语句需要先解析成数据库可以执行的二进制码。预编译语句可以将SQL语句预先编译成二进制码,以减少每个插入操作的解析和编译时间。

3. 使用事务处理

批量插入操作存在着一定的风险:如果某一条插入操作失败,整个插入操作将被中断,同时之前已经成功插入的数据也会被回滚,导致数据的不连续性。使用事务处理可以有效避免这种风险。通过事务处理,我们可以将多个插入操作统一到一个事务中,在所有操作执行完毕后再一起提交到数据库中。如果某个操作失败,那么整个事务都将被回滚,保证了数据的一致性。

三、批量插入的实现方法

1. JDBC批量插入

JDBC是Java数据库连接的标准,通过JDBC可以实现Java程序与各种关系型数据库的连接和操作。在JDBC中,批量插入需要使用到PreparedStatement类。该类可以通过addBatch()方法插入多个参数,最后一次性将参数塞入到PreparedStatement中。

示例代码:

// 插入数据

String sql = “INSERT INTO user (name, age, sex) VALUES (?, ?, ?)”;

PreparedStatement pstmt = conn.prepareStatement(sql); // 预编译SQL

for (User u : userList) { // 模拟用户数据

pstmt.setString(1, u.getName());

pstmt.setInt(2, u.getAge());

pstmt.setString(3, u.getSex());

pstmt.addBatch(); // 将操作添加到预编译语句中

}

pstmt.executeBatch(); // 一次执行多条SQL语句

conn.commit(); // 提交事务

2. MyBatis批量插入

MyBatis是一款优秀的Java持久化框架,可以将Java对象映射到关系型数据库中。在MyBatis中,批量插入需要使用到BatchExecutor类。

示例代码:

INSERT INTO user (name, age, sex) VALUES

(#{item.name}, #{item.age}, #{item.sex})

// 将数据封装到List中

List userList = new ArrayList();

for (int i = 0; i

userList.add(new User(“test” + i, 18, “F”));

}

// 执行批量插入

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

for (int i = 0; i

List subList = userList.subList(i * 100, (i + 1) * 100);

userMapper.batchInsert(subList);

}

sqlSession.commit(); // 提交事务

四、批量插入的注意事项

1. 数据库限制

不同的数据库对于批量插入操作的限制不同。使用之前,需要确保数据库对于一次插入操作的数据量不会超出限制。

2. 防止数据重复插入

在批量插入操作中,可能会产生重复数据的情况。为了避免出现这种情况,需要在插入之前进行数据去重操作。

3. 注意执行过程中的异常处理

批量插入是一次性操作大量数据的过程,如果在此过程中出现异常,必须进行及时处理以避免数据丢失或不完整的情况。

相关问题拓展阅读:

mysql 怎么用insert批量插入数据

这就是自己实现一个id自增的东西。

比如的你有个公用册袭类专门用来生成后面的数字州灶兄,所有需要用到的方法都调用这个类的一辩铅个方法我们就叫它nextid吧,只要这个方法是线程安全的就可以了。

idutils.nextid()

int

nextid()

{

lock(this){

return

this.id++;

}

}

应用启动的时候从数据库查询一下id的更大值并设置给工具类的id,让它接着增长就行了。

具体实现看你用什么语言。

请仔细阅读别人回答的是什么意思。想想oracle的sequence的实现,是不是类似?

一次插入多行数据

insert

into

表名

values(值列表1),(值列表2),…(值列表n);

例如:

insert

into

students(sid,sname,dob)

values

(‘001′,’张三核银’,”),

(‘002′,’李四’,”),

(‘宴配003′,’王五’,”);

将一张表或查询中的改祥宴数据插入到另一张表里

insert

into

表名(字段列表)

select

(字段列表)

from

源表

where

筛选表达式;

例如将表2中的记录全部插入到表1,假设它们的结构一样

insert

into

表1(*)

select

*

from

表2;

加锁情况与死锁原因分析

为方便大家复现,完整表结构和数据如下:

CREATE TABLE `t3` (

`c1` int(11) NOT NULL AUTO_INCREMENT,

`c2` int(11) DEFAULT NULL,

PRIMARY KEY (`c1`),

UNIQUE KEY `c2` (`c2`)

) ENGINE=InnoDB

insert into t3 values(1,1),(15,15),(20,20);

在 session1 执行 commit 的瞬间,我们会看到 session2、session3 的其中一个报死锁。这个死锁是这样产生的:

1. session1 执行 delete  会在唯一索引 c2 的 c2 = 15 这一记录上加 X lock(也就是在MySQL 内部观测到的:X Lock but not gap);

2. session2 和 session3 在执行 insert 的时候,由于唯一约束检测发生唯一冲突,会加 S Next-Key Lock,即对 (1,15> 这个区间加锁包括间隙,并且被 seesion1 的 X Lock 阻塞,进入等待;

3. session1 在执行模世 commit 后,会释放 X Lock,session2 和 session3 都获得 S Next-Key Lock;

4. session2 和 session3 继续执行插入操作,这个时候 INSERT INTENTION LOCK(插入意向锁)出现了,并且由于插入意向锁会被 gap 锁阻塞,所以 session2 和 session3 互相等待,造成死锁。

死锁日志如下: 

INSERT INTENTION LOCK

在之前的死锁分析第四点,如果不分析插入意向锁,也是会造成死锁的,因为插入最终还是要对记录加 X Lock 的,session2 和 session3 还是会互相阻塞互相等待。

但是插入意向锁是客观存在的,我们可以在官方手册中查到,不可忽略:

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.

插入意向锁其实是一种特殊的 gap lock,但是它不会阻塞其他锁。假设存在值为 4 和 7 的索引记录,尝试插入值 5 和 6 的两个事务在获取插入行上的排它锁之前使用插入意向锁锁定间隙,即在(4,7)上加 gap lock,但是这两个事务不会互相冲突等待。

当插入一条记录时,会去检查当前插入位置的下一条记录上是否存在锁对象,如果下一条记录上存在锁对象,就需要判断该锁对象是否锁住了 gap。如果 gap 被锁住了,则插入意向锁与之冲突,进入等待状态(插入意向锁之间并不互斥)。总结一下这把锁的属性:

1. 它不会阻塞其他任何锁;

2. 它本身仅会被 gap lock 阻塞。

在学习 MySQL 过程中,一般只有在它被阻塞的时候才能观察到,所以这也是它常常被忽略的原因吧…

GAP LOCK

在此例中,另外一个重要的点就是 gap lock,通常情况下我们说到 gap lock 都只会联想到 REPEATABLE-READ 隔离级别利用其解决幻读。但实际上在 READ-COMMITTED 隔离级别,也会存在 gap lock ,只发生在:唯一约束检查到有唯一冲突的时候,会加 S Next-key Lock,即对记录以及与和上一条记录之间的间隙加共享锁。

通过下面这个例子就能验证:

这余码桥里 session1 插入数据遇到唯一冲突,虽然报错,但是对 (15,20> 加的 S Next-Key Lock 并不会马上释放,所以竖猛 session2 被阻塞。另外一种情况就是本文开始的例子,当 session2 插入遇到唯一冲突但是因为被 X Lock 阻塞,并不会立刻报错 “Duplicate key”,但是依然要等待获取 S Next-Key Lock 。

有个困惑很久的疑问:出现唯一冲突需要加 S Next-Key Lock 是事实,但是加锁的意义是什么?还是说是通过 S Next-Key Lock 来实现的唯一约束检查,但是这样意味着在插入没有遇到唯一冲突的时候,这个锁会立刻释放,这不符合二阶段锁原则。这点希望能与大家一起讨论得到好的解释。

如果是在 REPEATABLE-READ,除以上所说的唯一约束冲突外,gap lock 的存在是这样的:

普通索引(非唯一索引)的S/X Lock,都带 gap 属性,会锁住记录以及前1条记录到后1条记录的左闭右开区间,比如有记录,delete 6,则会锁住

对于 gap lock,相信 DBA 们的心情是一样一样的,所以我的建议是:

1. 在绝大部分的业务场景下,都可以把 MySQL 的隔离界别设置为 READ-COMMITTED;

2. 在业务方便控制字段值唯一的情况下,尽量减少表中唯一索引的数量。

锁冲突矩阵

关于数据库批量插入怎么做的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 数据库批量插入操作指南 (数据库批量插入怎么做)