我想将 originDB (sqlite3)中的一个表(1000万条记录)复制到另一个名为 targetDB 的数据库中。
我的方法的过程是:
从原始表中读取数据并生成 ResultSet ,然后生成关于每条记录的相应插入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有一些相关的问题,如Out of memory when inserting records in SQLite, FireDac, Delphi
,但我没有解决我的问题,因为我们使用不同的实现方法。
我的假设是当记录的数量达不到10000时,这些相应的insert sql将被缓存在内存中,默认情况下执行提交时它们是否已被删除?每个建议都将得到欣赏。
答案 0 :(得分:1)
您正尝试通过执行以下操作一次性获取10M记录。这肯定会像你一样吃掉你的记忆
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
}
答案 1 :(得分:1)
通过在 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
我没有发现任何内存问题或大型事务的问题