Java 通过jdbc插入记录批时内存不足

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

我想将originDB(sqlite3)中的一个表(1000万条记录)复制到另一个名为targetDB的数据库中
我的方法的过程是: 从源表读取数据,生成一个结果集,然后对每条记录生成相应的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吗?