2017-09-16 2 views
0

Ich stoße auf ein Leistungsproblem mit mehreren Threads, die auf dasselbe Federdaten-Repository-Objekt zugreifen. Viele der Threads werden blockiert, während auf eine Sperre für das Repository-Objekt gewartet wird. Die meisten Threads führen dieselbe Abfrage für das Repository-Objekt durch. Wenn dieser Threadblock ausgeführt wird, ist die CPU auf allen Kernen ausgelastet. Gelegentlich wird es einbrechen, was ich glaube von den blockierten Threads, die auf eine Sperre für das Repository-Objekt warten. Ich habe durch Profiling überprüft, dass mehrere Threads darauf warten, dieselbe Methode im Repository-Objekt aufzurufen. Ich habe einen Leistungsschub gesehen, als ich den Ansatz geändert habe, die Methode zu verwenden, die eine Liste zurückgibt. Aber die Verriegelung ist immer noch ein Flaschenhals.Spring Data Repository Multi-Threading-Leistung

UPDATE: Nach mehr Forschung bin ich zu der Schlussfolgerung gekommen, dass das Repository-Objekt ein Singleton ist. Dieses Objekt wird gesperrt, wenn jeder Thread darauf zugreift. Wie prototype ich das Repository-Objekt? (Ich würde ein Read-Only-Repository für diesen Anwendungsfall erstellen.) Müsste die Konfiguration geändert werden? Machen Spring-Daten das schon?

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); 

Ich werde sagen, ich bin ein gutes Stück von Leistung bekommen von dem, was ich im Moment haben. Ich bin auch nicht sicher, ob ich meine Konfiguration korrekt eingerichtet habe, um die Datenbank mit mehreren Threads/Verbindungen zu treffen. Ich denke nicht, dass das das Problem ist, aber ich habe die vollständige Konfiguration zur Verfügung gestellt. Irgendwelche Tipps für die Leistung sind willkommen.

Hier ist eine Stack-Ablaufverfolgung eines Threads, wenn ich den Prozess/Anwendung anhalten, während es blockiert ist. Ich habe auch eine Beispielausgabe vom Pro-Filer. Wenn es länger läuft, summiert sich die blockierte Zeit. Es wird eindeutig von einem anderen Thread im @Autowired-Repository-Objekt blockiert. Ich dachte, ich hätte das vermieden, indem ich den Prototypumfang benutzt habe.

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) 
+0

Wie sei es die 'EntityJpaRepository' in Haupt bekommen (mit' ctx. getBean') und erstellen Sie Ihren Thread mit 'new AThread (theEntityJpaRepository)'. Kannst du nicht auch eine Methode verwenden, um eine 'Liste ' stattdessen zu holen? –

+0

Ich habe eine List-Methode verwendet, um einige Leistungsverbesserungen zu erhalten. Aber ich sehe immer noch das gleiche Verhalten. Mehrere Threads warten darauf, in die Repository-Methode zu gelangen. Ich würde gerne denken, dass die @ Scope ("Prototyp") Annotation mir eine Kopie des Repository-Objekts geben würde. Ich beginne zu denken, dass die @ Autowired Anmerkung ein geteiltes (Singleton) Objekt ist. –

+1

Ihr Code sieht vollkommen in Ordnung. Es ermöglicht bis zu 100 gleichzeitige Anfragen an die Datenbank. Allerdings verstehe ich nicht, warum Ihre Threads blockiert werden. Die angegebene Implementierung der run-Methode führt nur eine readonly-Anforderung an die Datenbank aus und kann nicht durch andere Threads auf Isolationsstufe niedriger als serializable blockiert werden. Kannst du bitte ausführlicher erklären, wie "Threads blockiert werden, während auf eine Sperre für das Repository-Objekt gewartet wird" in einer realen Implementierung der Run-Methode? –

Antwort

0

Das sieht sehr ähnlich wie ein Problem mit dem Laden der Klasse Ihrer Abfrage wiederholt. Wiederholte Lasten derselben Klasse können zu schweren Konflikten auf dem Klassenladerschloss führen. (Beispiel hier: https://plumbr.eu/blog/locked-threads/classloading-and-locking)

Die sharedEntityManager laden die Klasse in Zeile 302 - und dies läuft in den synchronisierten Block auf der Linie 404 in dem Classloader

+0

Das Problem stellte sich als mein Fehler heraus. Ich hatte die Priorität des Programms geändert und nutze immer noch die Maschine, auf der es lief, für andere Aufgaben. Als ich die Priorität der Aufgabe wieder auf normal änderte, ging das Problem weg. Es war auch nicht vorhanden, wenn sowohl der Datenbankprozess als auch das betreffende Programm dieselbe Priorität hatten. –

Verwandte Themen