我遇到了多個線程訪問同一個彈簧數據存儲庫對象的性能問題。許多線程在等待資源庫對象的鎖時被阻塞。大多數線程都在存儲庫對象上執行相同的查詢。當這個線程塊運行時,CPU在所有內核上都被最大化。偶爾它會下降,我認爲這是來自等待鎖存器對象的阻塞線程。我通過分析驗證了多個線程正在等待調用存儲庫對象中的相同方法。通過改變使用返回列表的方法的方法,我確實看到了性能的提升。但鎖定仍然是一個瓶頸。Spring數據倉庫多線程性能
更新:經過更多的研究,我得出結論,存儲庫對象是一個單身人士。這個對象在每個線程訪問它時都被鎖定。我如何建立資源庫對象的原型? (我會爲這個用例創建一個只讀存儲庫。)配置是否需要更改? Spring數據已經做到了嗎?
MWE:
public interface EntityJpaRepository extends JpaRepository<Entity, Integer> {
@Query(value = "select * from SomeTable where id = (?1);", nativeQuery = true)
Entity findById(int id);
//Method that returns a list of Entities
@Query(value = "select * from SomeTable where id in (?1);", nativeQuery = true)
List<Entity> findAllWithIds(List<Integer> ids);
}
@Component
@Scope("prototype")
public class AThread implements Runnable {
@Autowired
EntityJpaRepository myRepository;
final int someId;
public AThread(int someId) {
this.someId = someId;
}
@Override
public void run() {
//may call subMethod 1
myRepository.findById(someId);
//may call subMethod 1
List<Integer> ids = someMethodWhichReturnsIDs();
myRepository.findAllWithIds(ids);
//may call subMethod 1
}
public void subMethod1(){
//sometimes loop
subMethod2();
//may call
}
public void subMethod2(){
//more stuff
List<Integer> ids = someMethodWhichReturnsIDs();
//more stuff
}
}
public static void main(String[] args){
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) ctx.getBean("taskExecutor");
List<int> someInts;//assume this is full of ints.
for(int someId: someInts){
taskExecutor.execute((Runnable)ctx.getBean("AThread", someId));
}
waitThreads(taskExecutor);
我會說我得到的性能公平位出了什麼我現在有。我也不確定我是否已正確設置配置以使用多個線程/連接訪問數據庫。我不認爲這是問題,但我提供了完整的配置。歡迎任何性能提示。
@Configuration
@EnableJpaRepositories(basePackages= {"org.repository"})
@ContextConfiguration(locations={"classpath:META-INF/spring/app-context.xml"})
@ComponentScan(basePackages = "org.somepackage")
public class JpaConfiguration {
@Value("#{mainDataSource}")
private javax.sql.DataSource dataSource;
@Bean
public Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernate.dialect", MySQLDialect.class.getName());
props.put("hibernate.format_sql", true);
return props;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
return hibernateJpaVendorAdapter;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSource);
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
return lef;
}
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(100);
pool.setMaxPoolSize(500);
pool.setWaitForTasksToCompleteOnShutdown(true);
pool.setKeepAliveSeconds(1800);
return pool;
}
}
這是一個線程的堆棧跟蹤,當我暫停進程/應用程序時,它被阻塞。我也有pro-filer的樣本輸出。當它運行更長時間時,阻塞的時間加起來。它顯然被@Autowired存儲庫對象上的另一個線程阻塞。我以爲我通過使用原型範圍避免了這一點。
taskExecutor-1 BLOCKED 4.68003 6320 34 2062 1 [email protected] taskExecutor-9
java.lang.ClassLoader.loadClass(Unknown Source)
sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
java.lang.ClassLoader.loadClass(Unknown Source)
org.springframework.util.ClassUtils.isVisible(ClassUtils.java:1209)
org.springframework.util.ClassUtils.getAllInterfacesForClassAsSet(ClassUtils.java:1136)
org.springframework.util.ClassUtils.getAllInterfacesForClassAsSet(ClassUtils.java:1143)
org.springframework.util.ClassUtils.getAllInterfacesForClass(ClassUtils.java:1099)
org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:302)
com.sun.proxy.$Proxy41.createNativeQuery(Unknown Source)
org.springframework.data.jpa.repository.query.NativeJpaQuery.createJpaQuery(NativeJpaQuery.java:65)
org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.doCreateQuery(AbstractStringBasedJpaQuery.java:72)
org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:165)
org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:197)
org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74)
org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:98)
org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:89)
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:421)
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:512)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke( CrudMethodMetadataPostProcessor.java:122)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
com.sun.proxy.$Proxy81.findAllWithIds(Unknown Source)
sun.reflect.GeneratedMethodAccessor64.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
com.sun.proxy.$Proxy90.findAllWithIds(Unknown Source)
org.somepackage.AThread.subMethod2(AThread.java:696)
org.somepackage.AThread.subMethod1(AThread.java:346)
org.somepackage.AThread.run(AThread.java:132)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
java.lang.Thread.run(Unknown Source)
如何得到'EntityJpaRepository'在main(用'CTX。 getBean')並用'new AThread(theEntityJpaRepository)'創建你的線程。也不能使用某種方法來取代'List'而不是? –
2017-09-16 04:17:30
我已經使用列表方法來獲得一些性能改進。但我仍然看到相同的行爲。多個線程正在等待進入存儲庫方法。我想認爲@Scope(「prototype」)註釋會給我一個Repository對象的副本。我開始認爲@Autowired註釋是一個共享(單例)對象。 –
你的代碼看起來非常合理。它允許對數據庫執行多達100個併發請求。不過,我不明白爲什麼你的線程被阻止。如果run方法的實現僅對數據庫執行單一隻讀請求,並且不能被隔離級別低於可序列化的其他線程阻塞。你能否詳細解釋一下在運行方法的實際實現中,「線程在等待資源庫對象鎖定時被阻塞」的問題? –