2
votes

Traitement d'une grande quantité de données depuis PostgreSQL

Je cherche un moyen de traiter une grande quantité de données chargées à partir de la base de données dans un délai raisonnable.

Le problème auquel je suis confronté est que je dois lire toutes les données de la base de données (actuellement environ 30M de lignes) puis les traiter en Java. Le traitement lui-même n'est pas le problème mais la récupération des données de la base de données l'est. La récupération prend généralement de 1 à 2 minutes. Cependant, j'ai besoin que ce soit beaucoup plus rapide que cela. Je charge les données de db directement dans DTO à l'aide de la requête suivante:

"Seq Scan on post_comment (cost=0.00..397818.16 rows=21809216 width=28) (actual time=0.044..6287.066 rows=21812469 loops=1), Planning Time: 0.124 ms, Execution Time: 8237.090 ms"

id est la clé primaire, id_post et id_comment sont des clés étrangères des tables respectives et col_a et col_b sont des colonnes de petits types de données int. Les colonnes avec des clés étrangères ont des index. Les outils que j'utilise actuellement pour ce travail sont Java, Spring Boot, Hibernate et PostgreSQL.

Jusqu'à présent, les seules options qui me sont venues à l'esprit étaient

  1. Abandonnez la mise en veille prolongée pour cette requête et essayez d'utiliser une connexion jdbc simple en espérant qu'elle sera plus rapide.
  2. Réécrire complètement l'algorithme de traitement de la procédure Java vers SQL.

Ai-je manqué quelque chose ou ce sont mes seules options? Je suis ouvert à toutes les idées. Notez que je n'ai besoin que de lire les données, de ne les modifier d'aucune façon.

EDIT: L'analyse explicative de la requête utilisée

select id, id_post, id_comment, col_a, col_b from post_comment


4 commentaires

Voulez-vous dire 30 Mo ou 30 millions de lignes? S'il s'agit de lignes, quelle est la quantité de données dans chaque ligne? Si vous avez juste besoin de faire un calcul sur les données et de présenter un résumé, il sera BEAUCOUP plus rapide d'écrire une procédure stockée pour faire le travail et ne transmettre que le résumé sur le réseau.


Sans profilage et analyse des requêtes, toute réponse que nous donnerions serait une supposition, surtout avec si peu de choses à faire. En règle générale, cependant, il est généralement avantageux de laisser la base de données manipuler vos données lorsque vous pouvez raisonnablement le faire. J'ose dire que pour avancer, ce serait une assez bonne supposition.


30 millions de lignes, 5 colonnes de (3 bigints et 2 smallints). Je sais que la procédure stockée est de loin le meilleur choix, mais qui prend beaucoup de temps (il ne sera pas facile de réécrire le calcul en SQL). J'essayais de trouver des alternatives auxquelles je n'avais pas pensé. À partir du profilage - le goulot d'étranglement du calcul est la requête de base de données énoncée dans le message d'origine (cela prend environ 92% du temps de calcul). J'ajouterai le résultat de l'analyse explicative de la requête au message d'origine.


La question clé, sans réponse à laquelle tout est supposé, est ce que vous voulez faire avec les données une fois que vous les avez récupérées dans la base de données.


4 Réponses :


-1
votes

Pourquoi gardez-vous 30M en mémoire ?? il est préférable de le réécrire en pure sql et d'utiliser la pagination basée sur l'id

vous recevrez 5 comme l'id du dernier commentaire et vous publierez

select id, id_post, id_comment, col_a, col_b from post_comment where id > 5 limit 20

si vous besoin de mettre à jour toute la table, puis vous devez mettre la tâche dans le cron mais aussi là pour la traiter en parties la mémoire de la route et le téléchargement de 30M coûte très cher - vous devez traiter des pièces 0-20 20-n n + 20


4 commentaires

Cela générerait des millions de requêtes ralentissant encore plus l'application. J'aurais dû souligner que je lis les données par lots avec une taille de lot de 0,5M d'enregistrements, sinon je me retrouve juste avec OutOfMemoryException . Notez que je n'ai pas à montrer les données à l'utilisateur ni à les modifier. Je n'ai besoin que de les lire, de faire un calcul et de montrer à l'utilisateur le résultat de ce calcul.


30 Mo ne sont pas une quantité excessive à conserver en mémoire à la fois. Dans tous les cas, sur quelle base prétendez-vous que l'accès paginé sur plusieurs requêtes améliorerait les performances observées du PO? Ma première hypothèse est que ce serait pire. Beaucoup , en fait, car nous parlons maintenant de millions de requêtes distinctes.


ici on dit environ 30m de lignes dans le tableau, n'est-ce pas ?? 1) si vous voulez faire un calcul qui est meilleur sur sql que pour charger des portions et accumuler - sql spead parfois plus 2) le fractionnement de page est une pratique courante lors du chargement et de l'affichage de données - le contexte n'a pas été spécifié ici l'accès personnel est un solution pour les systèmes surchargés car elle vous permet de faire des réponses en lecture et de lire des fragments sur différents serveurs backend, ce qui entraîne un traitement parallèle et des informations qui dépassent les coûts de lecture et de mémoire cohérents


la lecture de parties vous permet d'appliquer un index plutôt que de lire l'ensemble du tableau de manière cohérente - ce sera rapide.et le traitement de la partie sélectionnée des données peut être mis dans un flux parallèle - ce qui augmentera la productivité plusieurs fois et là sera une lecture rapide sur l'index et le traitement des données dans l'intervalle



1
votes

Devez-vous traiter toutes les lignes en même temps ou pouvez-vous les traiter une par une?

Si vous pouvez les traiter une par une, vous devriez essayer d'utiliser un jeu de résultats déroulant.

XXX

Ceci se souviendra encore de chaque objet dans le gestionnaire d'entités, et deviendra ainsi de plus en plus lent. Pour éviter ce problème, vous pouvez détacher l'objet du gestionnaire d'entités une fois que vous avez terminé. Cela ne peut être fait que si les objets ne sont pas modifiés. S'ils sont modifiés, les changements ne seront PAS persistants.

org.hibernate.Query query = ...;
query.setReadOnly(true);
ScrollableResults sr = query.scroll(ScrollMode.FORWARD_ONLY);

while(sr.next())
{
    MyClass myObject = (MyClass)sr.get()[0];
    ... process row for myObject ... 
    entityManager.detach(myObject);
}


1 commentaires

merci, je vais essayer et voir comment se passe la performance



1
votes

Si j'étais à votre place, je contournerais définitivement la mise en veille prolongée et passerais directement à JDBC pour cette requête. Hibernate n'est pas fait pour traiter de grands ensembles de résultats, et il représente une surcharge supplémentaire pour les avantages qui ne sont pas applicables à des cas comme celui-ci.

Lorsque vous utilisez JDBC, n'oubliez pas de définir autocommit sur false et de définir une taille de récupération importante (de l'ordre de milliers), sinon postgres récupérera d'abord les 21 millions de lignes en mémoire avant de commencer à vous les céder. (Voir https://stackoverflow.com/a/10959288/773113 )


0 commentaires

1
votes

Depuis que vous avez demandé des idées, j'ai vu ce problème être résolu dans les options ci-dessous en fonction de la façon dont il s'intègre dans votre environnement: 1) Essayez d'abord avec JDBC et Java, un code simple et vous pouvez faire un test sur votre base de données et vos données pour voir si cette amélioration est suffisante. Vous devrez ici faire des compromis sur les autres avantages d'Hibernate. 2) Au point 1, utilisez le multi-threading avec plusieurs connexions tirant les données vers une file d'attente, puis vous pouvez utiliser cette file d'attente pour traiter davantage ou imprimer selon vos besoins. vous pouvez également considérer Kafka. 3) Si les données vont continuer à augmenter, vous pouvez considérer Spark comme la dernière technologie qui peut tout faire en mémoire et sera beaucoup plus rapide.

Voici quelques-unes des options, s'il vous plaît comme si ces idées vous aident n'importe où.


0 commentaires