Question Pourquoi la requête.list () d'Hibernate est-elle lente?


J'utilise Hibernate 4.1.6 et j'ai des problèmes avec la vitesse à laquelle une liste est construite. J'exécute la requête suivante.

public void doQuery(final Baz baz){
  final Query query = getSessionFactory().getCurrentSession().createQuery(
          "select c.id, foo.someValue from Foo as foo "+
          "join foo.a as a"+
          "join foo.b as b "+
          "join b.c as c "+
          "where baz=:baz"
          );
  query.setParameter("baz", baz);
  Long start=System.currentTimeMillis();
  final List<Object[]> list = query.list();
  Long end=System.currentTimeMillis();
  System.out.println((end-start));
}

Je mets le débogage en veille prolongée pour obtenir la requête réelle envoyée à la base de données. J'ai exécuté cette requête directement dans la base de données et il a renvoyé 23 000 lignes en 0,015 ms. Donc, je suppose que la requête n'est pas le problème. L'exemple ci-dessus montre qu'il faut environ 32 secondes pour créer cette liste. Y a-t-il quelque chose qui peut être fait pour accélérer cela?

Mise à jour: J'ai essayé d'utiliser la méthode createSQLQuery () à l'aide de la requête de débogage hibernate et elle a été aussi lente que la méthode createQuery ().

Mise à jour: j'ai essayé d'utiliser une session sans état, mais elle était tout aussi lente.

Mise à jour: J'ai généré des statistiques (définissant le drapeau hibernate.generate_statistics sur true) mais rien ne me semble alarmant:

Hibernate SessionFactory Statistics [
    Number of connection requests[4]
    Number of flushes done on the session (either by client code or by hibernate[3]
    The number of completed transactions (failed and successful).[3]
    The number of transactions completed without failure[3]
    The number of sessions your code has opened.[4]
    The number of sessions your code has closed.[3]
    Total number of queries executed.[4]
    Time of the slowest query executed.[28258]
    the number of collections fetched from the DB.[6]
    The number of collections loaded from the DB.[6]
    The number of collections that were rebuilt[0]
    The number of collections that were 'deleted' batch.[0]
    The number of collections that were updated batch.[0]
    The number of your objects deleted.[0]
    The number of your objects fetched.[1]
    The number of your objects actually loaded (fully populated).[204]
    The number of your objects inserted.[1]
    The number of your object updated.[0]
]

Hibernate SessionFactory Query Statistics [
    total hits on cache by this query[0]
    total misses on cache by this query[0]
    total number of objects put into cache by this query execution[0]
    Number of times this query has been invoked[1]
    average time for invoking this query.[28258]
    maximum time incurred by query execution[28258]
    minimum time incurred by query execution[28258]
    Number of rows returned over all invocations of this query[23303]
]

Mise à jour: Je vois la même lenteur lorsque je fais un next () à partir d’un ScrollableResults à partir d’une requête native. Notez que je ne fais rien dans la boucle.

    ScrollableResults results = query.scroll();
    Long start=System.currentTimeMillis();
    while (results.next()) {
       //do nothing
    }
    Long end=System.currentTimeMillis();
    System.out.println((end-start));

15
2017-08-28 16:06


origine


Réponses:


Je ne suis pas certain à 100% de cette réponse, car les problèmes de réglage / d’optimisation sont toujours difficiles à cerner.

Cependant, basé sur le fait que vous avez allumé show_sql, extrait la requête, et l'exécute directement sur la base de données et voit des résultats inférieurs à la seconde par rapport à la durée d'exécution via Hibernate Query, je me concentre sur la manière dont Hibernate construit et hydrate les objets query.list() appel.

Voici un autre utilisateur qui a mentionné des problèmes de performances Query similaires dans Hibernate, et a vu des performances spectaculaires augmenter en ajoutant des constructeurs complets (constructeurs acceptant une valeur pour chaque champ) dans le POJO: Requête d'hibernation simple retournant très lentement

On dirait qu'ils sont tombés sur ce correctif, et il n'y avait pas une compréhension claire de la raison pour laquelle cela fonctionnait. Il y avait des spéculations concernant Hibernate devant utiliser la réflexion pour détecter les propriétés. Je suis curieux moi-même et je prévois de creuser le code source d'Hibernate pour mieux comprendre cela lorsque j'ai une chance. En attendant, vous pouvez envisager d'ajouter ces constructeurs complets avec des paramètres pour tous vos attributs de classe POJO et voir si cela fait une différence.

S'il vous plaît faites le moi savoir ce que vous trouvez, car je suis très intéressé par l'optimisation des performances d'Hibernate. Merci!


7
2017-08-28 17:05



Si les requêtes (avec show_sql) ne semble pas avoir de problème, alors peut-être que c'est dans le code. Démarrez VisualVM (fourni avec le JDK comme jvisualvm) et utiliser son profileur de processeur pour déterminer les méthodes les plus longues.


4
2017-08-28 19:11



J'ai exécuté cette requête directement dans la base de données et il a renvoyé 23 000 lignes en 0,015 ms. Donc, je suppose que la requête n'est pas le problème.

Cela peut être prématuré, car les temps d'exécution des requêtes dépendent beaucoup plus que le texte de la requête. Même si ces requêtes sont exécutées sur les mêmes données, comment savez-vous que la base de données utilise le même plan d'exécution? Comment savez-vous qu'il obtient la même quantité de hits du cache dans son cache disque? Par exemple, hibernate utilise des instructions préparées pour communiquer avec la base de données, mais vous ne l'avez probablement pas fait. Dans Oracle, les plans d'exécution sont mis en cache par le texte de la requête. Ainsi, un texte de requête différent signifie un plan d'exécution fraîchement calculé. Comme le plan d'exécution mis en cache peut avoir été formé en fonction de différents paramètres de requête, il peut très bien être différent - et cela peut changer les temps d'exécution par ordre de grandeur. Notez que je ne dis pas que cela est la base de données, mais je n’écarterais pas cette possibilité.

Par conséquent, la première chose à faire est de mesurer si la base de données ou quelque chose fonctionnant dans votre JVM gaspille tout ce temps. Un moyen simple de le faire consiste à regarder la consommation de la machine virtuelle Java pendant l'exécution de la requête. Si elle est nettement inférieure à un thread, la JVM attend quelque chose - probablement la base de données.

S'il s'agit de la base de données, utilisez les outils d'optimisation de votre base de données pour capturer le plan d'exécution et d'autres indicateurs de performance pertinents.

S'il se trouve dans la machine virtuelle Java, utilisez un profileur pour identifier le goulot d'étranglement des performances.


1
2017-08-29 18:38



Nous avons rencontré un problème similaire, dunno si c'est lié. Fondamentalement, comme nous étions en train de mettre à jour de nouvelles sessions SessionFactory une fois par requête, il faisait des requêtes comme:

 select streamref0_.UUID as UUID145_, streamref0_.Tape_TapeId as Tape2_145_ from StreamRefToTape streamref0_ where streamref0_.UUID=?

Vous remarquerez les grands nombres là-bas. Il s'avère qu'il s'incrémente une fois par nouvelle usine de session. Quoi qu’il en soit, c’était à cause d’Oracle qu’il passait tout son temps à faire un nouveau plan pour chaque requête (il était rapporté que cpu était presque entièrement en train de générer de nouveaux plans). avant?). Le correctif dans ce cas particulier était de simplement utiliser la même usine au lieu d'une nouvelle à chaque fois. Voir également Hibernate produit un SQL différent pour chaque requête

http://asktom.oracle.com/pls/asktom/f?p=100:11:0:::P11_QUESTION_ID:2588723819082 explique les analyses difficiles, qui sont apparemment mauvaises.

Une autre solution possible est d'utiliser "raw sql" (jdbc) ou éventuellement des requêtes SQL brutes dans hibernate, bien que cela ne semble pas avoir résolu le problème dans ce cas particulier ...


0
2018-03-06 20:28