Java 通过jdbc插入记录批时内存不足
我想将originDB(sqlite3)中的一个表(1000万条记录)复制到另一个名为targetDB的数据库中Java 通过jdbc插入记录批时内存不足,java,jdbc,sqlite,Java,Jdbc,Sqlite,我想将originDB(sqlite3)中的一个表(1000万条记录)复制到另一个名为targetDB的数据库中 我的方法的过程是: 从源表读取数据,生成一个结果集,然后对每条记录生成相应的insert sql,当记录数达到10000条时,执行提交批量插入。代码如下: public void transfer() throws IOException, SQLException { targetDBOperate.setCommit(false);//batch insert i
我的方法的过程是: 从源表读取数据,生成一个结果集,然后对每条记录生成相应的insert sql,当记录数达到10000条时,执行提交批量插入。
代码如下:
public void transfer() throws IOException, SQLException {
targetDBOperate.setCommit(false);//batch insert
int count = 0;
String[] cols = parser(propertyPath);//get fields of data table
String query = "select * from " + originTable;
ResultSet rs = originDBOperate.executeQuery(query);//get origin table
String base = "insert into " + targetTable;
while(rs.next()) {
count++;
String insertSql = buildInsertSql(base,rs,cols);//corresponding insert sql
targetDBOperate.executeSql(insertSql);
if(count%10000==0) {
targetDBOperate.commit();// batch insert
}
}
targetDBOperate.closeConnection();
}
下图是使用内存的趋势,纵轴表示内存使用情况
我们可以说,它会越来越大,直到内存耗尽。stackoverflow有一些相关的问题,例如,但是我没有解决我的问题,因为我们使用了不同的实现方法。
我的假设是,当记录计数未达到10000时,这些对应的insert sql将被缓存在内存中,并且在默认情况下执行commit时它们没有被删除?每一条建议都将不胜感激 您正在尝试通过执行以下操作一次性获取10万条记录。这肯定会像吃任何东西一样吞噬你的记忆
String query = "select * from " + originTable;
ResultSet rs = originDBOperate.executeQuery(query);//get origin table
使用分页查询读取批次,并根据需要执行批次更新
您甚至没有执行批处理更新,您只是通过执行以下代码一个接一个地触发10K查询
String insertSql = buildInsertSql(base,rs,cols);//corresponding insert sql
targetDBOperate.executeSql(insertSql);
if(count%10000==0) {
targetDBOperate.commit();// This simply means that you are commiting after 10K records
}
通过在SQLite或任何其他关系数据库中移动更多行,您应该遵循一些基本原则: 1) 将
autoCommit
设置为false,即不提交每次插入
2) 使用批更新,即每行不往返
3) 使用准备好的语句,即不要分析每个插入
把这些放在一起可以得到以下代码:
cn
是源连接,cn2
是目标连接
对于每个插入的行,您调用addBatch
,但每个batchSize
只调用一次executeBatch
,这将启动一次往返
不要忘记循环结束时的最后一个executeBatch
,以及最后一个commit
cn2.setAutoCommit(false)
String SEL_STMT = "select id, col1,col2 from tab1"
String INS_STMT = "insert into tab2(id, col1,col2) values(?,?,?)"
def batchSize = 10000
def stmt = cn.prepareStatement(SEL_STMT)
def stmtIns = cn2.prepareStatement(INS_STMT)
rs = stmt.executeQuery()
while(rs.next())
{
stmtIns.setLong(1,rs.getLong(1))
stmtIns.setString(2,rs.getString(2))
stmtIns.setTimestamp(3,rs.getTimestamp(3))
stmtIns.addBatch();
i += 1
if (i == batchSize) {
def insRec = stmtIns.executeBatch();
i = 0
}
}
rs.close()
stmt.close()
def insRec = stmtIns.executeBatch();
stmtIns.close()
cn2.commit()
使用sqlite-jdbc-3.23.1对您的尺寸进行样本测试:
inserted rows: 10000000
total time taken to insert the batch = 46848 ms
我没有观察到任何内存问题或大型事务问题一些建议:1)确保将自动提交设置为
false
。2) 使用真正的批处理更新,首先确保在insert
语句绑定变量中使用(这可能是内存问题的原因)。结果集不一定一次具体化所有行。sqlite不支持fetch size设置吗?在执行commit方法后会释放10k insert sql吗?