java多线程和sqlite
我在sqlite数据库中存储了大量数据。我使用JavaJDBC驱动程序从sqlite表中批量检索数据,然后处理数据。最后,处理后的数据被重写回tabledatabase中的一个新列。由于数据的处理相当简单,我尝试使用java中的多线程来加速计算 我遵循的步骤是: 产生子线程 然后,每个子级从sqlite db读取数据并处理数据 数据处理完成后,将使用同步功能Insert and commit将其重写到数据库中。 然而,我发现在处理速度计算方面没有任何改进。事实上,随着线程数量的增加,速度也随之降低 无多线程: 1000条记录~2分钟 2线程:1000条记录~2分钟:3秒 4个线程:1000条记录~2分钟:30秒 10个线程:1000条记录~2分钟:52秒 我使用的是Mac book pro:Mountain Lion;2.4 GHz Intel core 2 Duo 4GB 1067 MHz DDR3 代码如下:java多线程和sqlite,java,multithreading,sqlite,jdbc,Java,Multithreading,Sqlite,Jdbc,我在sqlite数据库中存储了大量数据。我使用JavaJDBC驱动程序从sqlite表中批量检索数据,然后处理数据。最后,处理后的数据被重写回tabledatabase中的一个新列。由于数据的处理相当简单,我尝试使用java中的多线程来加速计算 我遵循的步骤是: 产生子线程 然后,每个子级从sqlite db读取数据并处理数据 数据处理完成后,将使用同步功能Insert and commit将其重写到数据库中。 然而,我发现在处理速度计算方面没有任何改进。事实上,随着线程数量的增加,速度也随之降
package org.openscience.jch.diversity;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openscience.cdk.DefaultChemObjectBuilder;
import org.openscience.cdk.fingerprint.MACCSFingerprinter;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.jch.utilities.IteratingMolTableReader;
/**
*
* @author chandu
*/
public class MultiThreadCalculator {
// Main Class
public static void main(String args[]) throws SQLException {
int range = 0;
int start = 0;
int stop = 0;
int a = 0;
int numberOfThreads = 4;
int count = 10000;
Connection connection = connectDb("Zinc.db");
connection.setAutoCommit(false);
range = (int) Math.ceil(count /(double)(numberOfThreads));
// generate the child threads and assigns them the range of rows to read from the db
for (int i = 1; i <= numberOfThreads; i++) {
stop = range * i;
System.out.println(start + "," + stop);
new NewThread(start, stop, i,connection);
start = stop + 1;
}
System.out.println("Main thread exiting." + a);
}
// method to connect to db
private static Connection connectDb(String path) {
Connection c = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:" + path);
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
System.out.println("Opened database successfully");
return c;
}
// Child thread
public static class NewThread implements Runnable {
Thread t;
int ii;
int tStart = 0;
int tStop = 0;
static int ince = 0;
int a = 0;
Connection connection = null;
NewThread(int start, int stop, int threadID, Connection c) {
tStart = start;
tStop = stop;
ii = threadID;
System.out.println("child thread"+ii);
t = new Thread(this, "Demo Thread");
connection = c;
t.setPriority( Thread.NORM_PRIORITY + 1 );
t.start();
}
// This is the data processing part
public void run() {
Map< Integer, byte[]> map = new HashMap< Integer, byte[]>();
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM MOLDATA WHERE ID>=" + tStart + " and ID<=" + tStop + ";")) {
//SmilesGenerator sg = new SmilesGenerator(true);
MACCSFingerprinter mp = new MACCSFingerprinter();
while (rs.next()) {
IAtomContainer molecule = null;
int id = rs.getInt("ID");
InputStream is = new ByteArrayInputStream(rs.getString("STUCTURE").getBytes());
IteratingMolTableReader reader = new IteratingMolTableReader(is, DefaultChemObjectBuilder.getInstance(), true);
while (reader.hasNext()) {
molecule = reader.next();
break;
}
byte[] bi = mp.getBitFingerprint(molecule).asBitSet().toByteArray();
//System.out.println(bi.length);
//String smiles = sg.createSMILES(molecule);
map.put(id, bi);
System.out.println(id);
}
stmt.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
try {
writer(connection, map);
} catch (SQLException ex) {
Logger.getLogger(MultiThreadCalculator.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Exiting child thread." + a);
}
// Synchronised method to insert processed data and commit changes.
public synchronized static void writer(Connection connection, Map<Integer, byte[]> mp) throws SQLException {
String sql = "UPDATE MOLDATA SET FP = ? WHERE ID = ?";
PreparedStatement psUpdateRecord = connection.prepareStatement(sql);
int[] iNoRows = null;
for (int a : mp.keySet()) {
byte[] bi = mp.get(a);
psUpdateRecord.setBytes(1, bi);
psUpdateRecord.setInt(2, a);
psUpdateRecord.addBatch();
}
iNoRows = psUpdateRecord.executeBatch();
connection.commit();
System.out.println("Commit Done");
}
}
}
请记住,sqlite是一个非常小的数据库实现,针对大小和单用户/单线程使用进行了优化。您需要详细检查分析,但我预期会出现以下行为 每个线程同时读取一个数据块和其他数据块。大多数甚至所有数据都必须从磁盘读取,因为每个线程都读取另一个数据块。在这种情况下,sqlite中的缓存没有帮助,因为所有数据都不会被读取两次。当线程在访问磁盘时被序列化时,此时线程已经有效地以串行方式运行。 每个线程都进行一些复杂的计算。不管它有多复杂,它都是在内存中完成的,而sqlite在磁盘上进行读写,这要慢1000倍。 最后的插入/更新和提交完成了序列化的其余部分:提交必须写入磁盘,并且必须等待写入完成。完成该步骤后,下一个线程可以开始插入/更新其结果。 甚至可以解释线程数越多,速度也会下降:使用的线程越多,sqlite必须处理的开销就越大,而且对于许多用户或线程来说,sqlite并没有进行优化 这就是一些专业数据库变得如此昂贵的原因。它们可以处理10000个用户,并且拥有非常聪明的算法,可以让下一个东西在95%的时间内已经在内存中读取 但是你现在能做得更好吗 最实用的方法是重写代码:先从数据库中读取所有数据,然后在线程中进行处理,最后让一个线程执行所有更新/插入,最后只提交一次 您可以更改数据库,这是一个相当昂贵的解决方案。对于这种应用程序,即使是mySql也比sqlite好得多。一些数据库Oracle,Teradata。。。可以直接在数据库中运行Java代码,这样您就不需要在处理前后传输数据,这是一个常见的性能瓶颈,例如在SAS中
你做过分析吗?什么占用了时间;数据库活动还是计算?一般来说,您应该避免在多个线程之间共享同一个连接,并且每个线程有一个连接。我尝试了每个线程一个连接,但出现了错误:[SQLITE\u BUSY]数据库文件已锁定数据库已锁定。我认为Sqlite不允许在多个线程之间有多个连接。我认为问题不在于db查询调用,而在于处理数据的多线程..计算以长时间为例:为数据库中的化学结构生成MACCS结构键。。。