使用spring多線程往mysql數據庫插入100萬條數據效率對比,結果如下:
a) 20個線程*100000條/線程 = 200萬條數據, 用時7分43秒(同樣情況跑了2次,第一次是7分42秒,第二次是7分44秒)
b)1個線程*2000000條/線程 = 200萬條數據,用時11分27秒。兩者差別不大,重新調試參數,應該會有更快的結果
直接上代碼:
配置文件:threadPoolConfig.xml (放在resource/META-INF目錄下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd ">
<!-- spring thread pool executor -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 線程池維護線程的最少數量 -->
<property name="corePoolSize" value="20" />
<!-- 允許的空閑時間 -->
<property name="keepAliveSeconds" value="200" />
<!-- 線程池維護線程的最大數量 -->
<property name="maxPoolSize" value="50" />
<!-- 緩存隊列 -->
<property name="queueCapacity" value="100" />
<!-- 對拒絕task的處理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 數據庫連接驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 數據庫連接url -->
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/sampledb?useSSL=false&characterEncoding=utf8"/>
<!-- 數據庫連接用戶名 -->
<property name="user" value="root"/>
<!-- 數據庫連接密碼 -->
<property name="password" value="root"/>
<property name="automaticTestTable" value="test_timeout"/>
<!-- 隔多少秒檢查所有連接池中的空閑時間 -->
<property name="idleConnectionTestPeriod" value="60"/>
<!-- 最大空閑時間,超過空閑時間的連接將被丟棄 -->
<property name="maxIdleTime" value="900"/>
<!-- 初始化連接池數量 -->
<property name="initialPoolSize" value="10"/>
<!-- 最小連接池數量 -->
<property name="minPoolSize" value="10"/>
<!-- 最大連接池數量 -->
<property name="maxPoolSize" value="50"/>
<!-- 當連接池連接用完時,C3PO一次性創建新連接的數據 -->
<property name="acquireIncrement" value="10"/>
<!-- 數據源內加載的PreparedStatement數量 -->
<property name="maxStatements" value="200" />
<!--因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的
時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連接測試的性能。Default: false -->
<property name="testConnectionOnCheckout" value="false"/>
<!--如果設為true那么在取得連接的同時將校驗連接的有效性。Default: false -->
<property name="testConnectionOnCheckin" value="true"/>
</bean>
</beans>
1 OperationService.java
@Service
@ContextConfiguration(classes = Config.class)
public class OperationService {
@Autowired
private JdbcTemplate jdbcTemplate;
private Logger logger = LoggerFactory.getLogger(OperationService.class);
/**
* 每個線程根據自己的線程號插入相應的一段數據。例如:線程5插入id為501~600之間的數據
*/
public void dataInsert(int from, int to){
String insertSql = "insert into t_user_test values (?, 'john', '123', ?)";
LocalDateTime start = LocalDateTime.now();
for (int i = from; i <= to; i++) {
jdbcTemplate.update(insertSql, new Object[]{i, LocalDateTime.now()});
}
LocalDateTime end = LocalDateTime.now();
logger.info("開始時間:" + start + ", 結束時間:" + end);
}
}
2 MyThread.java
public class MyThread implements Runnable {
private OperationService operationService;
private int from;
private int to;
public MyThread(){
}
public MyThread(OperationService operationService, int from, int to){
this.operationService = operationService;
this.from = from;
this.to = to;
}
@Override
public void run() {
operationService.dataInsert(from, to);
}
}
3 Config.java
@Configuration
@ComponentScan(basePackages = { "common.use.multiThread" })
@ImportResource(value = {"classpath:META-INF/threadPoolConfig.xml" })
@EnableScheduling
public class Config {
}
4 測試類 MyTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class MyTest {
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private OperationService operationService;
@Test
public void test(){
for (int i = 1; i <= 20; i++) {
int unit = 100000;
int from = (i-1)*unit;
int to = i*unit - 1;
taskExecutor.execute(new MyThread(operationService, from, to));
System.out.println("int i is " + i + ", now threadpool active threads totalnum is " + taskExecutor.getActiveCount());
}
try {
System.in.read();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//注意:System.in.read()要保留,如果不保留,測試類主線程執行完,直接關閉jvm,不等待子線程執行完,這是坑。放在main方法里則可以省略。
}