项目中数据库插入大批量数据遇到的问题

在项目中尝试插入8000万条数据时遇到磁盘空间不足和Oracle表空间问题,导致数据处理中断。解决方案包括删除无用文件以释放服务器内存,扩展表空间,以及调整SQL循环插入的批处理大小。最后通过将批处理大小从500降低到50成功解决问题。

上周五的时候在测试环境跑大批数据(大概有8000万),想着等周一来看,但周一发现数据并没有处理完,停住了,查看日志时发现问题,首先是看到“no space left on device”这个异常,哦,磁盘空间不足了,删呗…,df- I 查一下文件属性使用情况:

使用的都很少,那再用 df -h 看看实际文件大小:

(这个图是删完以后的,当时忘截图了……)

标红的地方当时使用情况已经达到100G,达到了100%;所以显而易见,服务器内存不足了;然后就可以找一些没用的日志文件删一删就可以了;

当我继续去跑数据后,过了会儿我发现数据又不动了,于是去查看日志,又发现另一个新的异常:” ORA-01653: unable to extend table 表名by 1024 in tablespace

### Java 数据库批量插入性能优化及栈溢出解决方案 在 Java 中,当需要将数据插入数据库时,可能会遇到性能瓶颈和内存溢出问题。以下是一些关键的优化策略和解决方案。 #### 1. 使用 JDBC 批处理 JDBC 提供了批处理功能,可以显著减少与数据库的交互次数,从而提高性能。通过 `PreparedStatement` 的 `addBatch()` 和 `executeBatch()` 方法,可以将多条 SQL 语句组合成一个批次进行执行。需要注意的是,MySQL JDBC 驱动默认情况下可能忽略 `executeBatch()` 的效果[^2],因此需要显式启用批处理模式。可以通过设置连接属性 `rewriteBatchedStatements=true` 来启用批处理优化。 ```java String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"; Connection conn = DriverManager.getConnection(url, "user", "password"); PreparedStatement stmt = conn.prepareStatement("INSERT INTO table_name (column1, column2) VALUES (?, ?)"); for (int i = 0; i < batchSize; i++) { stmt.setString(1, "value1"); stmt.setString(2, "value2"); stmt.addBatch(); } stmt.executeBatch(); stmt.close(); conn.close(); ``` #### 2. Hibernate 批量插入优化 Hibernate 是一个强的 ORM 框架,但在处理大批量数据插入时可能存在性能问题。为了优化 Hibernate 的批量插入操作,可以采取以下措施: - **配置 JDBC 批处理**:在 Hibernate 配置文件中启用批处理功能。 ```properties hibernate.jdbc.batch_size=50 hibernate.order_inserts=true hibernate.order_updates=true ``` - **禁用一级缓存清理**:频繁的插入操作会导致 Session 的一级缓存膨胀,最终引发内存溢出。可以在每次插入一定数量的数据后调用 `session.clear()` 清理缓存[^4]。 ```java Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for (int i = 0; i < largeDataSize; i++) { Entity entity = new Entity(); // 设置实体属性 session.save(entity); if (i % batchSize == 0) { session.flush(); session.clear(); } } tx.commit(); session.close(); ``` #### 3. 分块读取文件并使用多线程 对于从文件中读取数据插入数据库的场景,可以采用分块读取和多线程处理的方式来避免 JVM 内存溢出。具体步骤包括: - **分块读取文件**:将文件按固定小(如 1000 条记录)分块读取。 - **多线程处理**:使用线程池分配任务,每个线程负责处理一个数据块。 - **批量插入**:每个线程通过 JDBC 批处理将数据插入数据库。 代码示例如下[^3]: ```java ExecutorService executor = Executors.newFixedThreadPool(numThreads); List<List<String>> dataBlocks = readFileInChunks(filePath, chunkSize); for (List<String> block : dataBlocks) { executor.submit(() -> { try (Connection conn = DriverManager.getConnection(url, user, password)) { PreparedStatement stmt = conn.prepareStatement(insertSql); for (String record : block) { stmt.setString(1, record); stmt.addBatch(); } stmt.executeBatch(); } catch (SQLException e) { e.printStackTrace(); } }); } executor.shutdown(); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); ``` #### 4. 数据库端优化 除了应用程序层面的优化外,还可以对数据库进行调整以提升批量插入性能: - **禁用索引和约束**:在插入大批量数据前临时禁用索引和外键约束,完成后重新启用。 - **调整事务隔离级别**:使用较低的隔离级别(如 READ_UNCOMMITTED)可以减少锁竞争。 - **增加数据库缓冲区小**:根据数据库类型调整相关参数以优化写入性能。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值