IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JDBC Java Discussion :

[HSQLDB / Pool JDBC] Nombre de connexions max


Sujet :

JDBC Java

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Points : 14
    Points
    14
    Par défaut [HSQLDB / Pool JDBC] Nombre de connexions max
    Salut à tous,

    Je travaille sur un projet de serveur multijoueurs Minecraft. Le serveur utilise une API qui s'appelle Bukkit et me donne tout un tas de fonctions pour vérifier ce que font les joueurs et me permet de mettre en place des EventsListeners.

    Le client (minecraft) envoie une nouvelle position au serveur (bukkit) qui la valide/infirme et lui renvoie la réponse.

    Je dois calculer à chaque mouvement de joueur s'ils ont le droit de faire ce déplacement. On parle d'une cinquantaine de joueurs simultanés qui seront dans l'espace de construction. Hors de cet espace, les permissions de déplacement sont calculées différemment.

    Tout de même, un joueur qui court peut traverser facilement 6 blocs en 1 seconde => 50*6 = 300 calculs par seconde.

    Pour définir le booléen qui dit si oui ou non tu as le droit d'être ici, je souhaite utiliser une base de données stockée sur la RAM qui s'appelle HSQLDB. J'ai fais un petit test de rapidité et pour faire cette opération :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    String req = "SELECT p.origin, p.dimension FROM plot_list p INNER JOIN mc_entity en ON p.entitykId = en.entitykId WHERE en.mId = '" + id + "'";
    ResultSet result = Statement.executeQuery(req);
    Ca prend 0.5 ms !

    J'ai fait quelques estimations sur le temps de réponse et je pensais être plus proche de la milliseconde que de sa moitié, belle surprise !

    Un peu de calcul totalement imprécis:
    300 req * 0.5 ms = 150ms/s de temps effectif de travail de la bdd. Je ne compte pas le temps de calcul qui est un peu supérieur au temps d'accès des données.

    J'ai plusieurs questions (dont certaines n'en sont pas vraiment) :

    1. Créer mes procédures d'accès en procédures stockées fera-t-il baisser le temps ?
    2. Utiliser le principe du pool de connexion permettrait-il d'encore gagner du temps ?
    3. Si je décide d'appliquer ce calcul à l'ensemble du monde réuni on pourra atteindre dans le meilleur des cas 400 voire 500 joueurs simultanés. Est-il possible dans ce cas de répliquer la base de données pour augmenter le nombre de thread/connexions possibles ? Je précise que c'est stocké en RAM et donc détruit dès que le serveur est redémarré donc de toute façon pas long à refaire (au plus, une vingtaine de tables avec 5,6 colonnes et quelques centaines/milliers de lignes).
    4. Comme ce ne sont que des tables temporaires, je peux essayer d'augmenter le nombre de colonnes en réduisant le nombre de tables, voire créer une table par requête pour éviter au maximum les jointures qui doivent grappiller du temps.

    Pour info: les vraies tables avec les vraies données sont stockées en MySql:

    Comme vous pouvez le voir je vis dans la spéculation en ce moment, un peu de concret me ferait le plus grand bien

    Merci d'avoir lu, si vous pensez à d'autres choses pour améliorer le temps de réponse, n'hésitez surtout pas.

  2. #2
    Rédacteur/Modérateur
    Avatar de Laurent.B
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2004
    Messages
    3 468
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2004
    Messages : 3 468
    Points : 17 036
    Points
    17 036
    Par défaut
    Bonjour,

    1 - Si la BDD se trouve sur la même machine que l'application, je dirais qu'a priori ça n'a pas d'intérêt.

    2 - Clairement, le pool est fait pour accélérer les accès BDD, en maintenant une ou plusieurs connexions ouvertes, lesquelles sont donc réutilisées à chaque requête. Le coût d'ouverture d'une connexion BDD n'étant pas négligeable, utiliser un pool devrait théoriquement avoir un impact conséquent. Cela-dit, dans le cas de HSQLDB, comme tout est déjà en mémoire, j'aurais tendance à penser que le gain de temps n'est sans doute pas aussi évident (à valider). Par contre, le pool a néanmoins l'avantage de permettre de contrôler le nombre de connexions simultanées et donc par extension, maîtriser les ressources utilisées pour les accès base.

    3 - Oui, ça de toute façon, si tu veux augmenter la capacité de traitement, il faut bien répartir la charge sur différentes machines mais ensuite, la problématique sera de gérer la synchronisation des données, c'est-à-dire de répliquer les ajouts / modifications / suppressions de données sur chaque instance (si nécessaire)... Il y plusieurs possibilités mais tout ça dépend de ce que contient la base, des dépendances qu'il y a entre les données...

    4 - Ce dont tu parles ressemble à des vues, donc créer autant de tables ou vues contenant l'ensemble des données nécessaires selon le type de requête, évitant ainsi les jointures, me semblerait approprié, effectivement.

    Un autre axe d'amélioration des performances est la création et l'utilisation d'index...

    Voilà ce que je peux en dire.

    Bon courage.
    Responsable FAQ Eclipse | Maintiens et développe un des logiciels destinés aux rédacteurs sur developpez.com
    Gardons toujours à l'esprit que le forum constitue une base documentaire, dont l'utilité et la qualité dépendent du soin apporté à nos questions et nos réponses. Soyons polis, précis (dans le titre et dans le corps des questions), concis, constructifs et faisons de notre mieux pour respecter la langue française et sa grammaire. Merci pour nous (les modérateurs) mais aussi et surtout, merci pour vous.
    Problème solutionné => je vais au bas de la page et je clique sur le bouton (qui suite à mise à jour du forum, a légèrement changé d'aspect).

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 394
    Points : 639
    Points
    639
    Par défaut
    Bonjour,

    Il faut aussi que tu utilises des PreparedStatement car avec ta fonction static tu récrés un Statement à chaque fois à mon avis...

    L'idée est que tu crées un statement une fois, et que tu ré-exécutes ce même statement à chaque fois, ça sera nettement plus rapide. Regarde ici : http://docs.oracle.com/javase/tutori.../prepared.html

    Tu dois avoir un Statement par thread car deux thread ne peuvent pas exécuter un même objet PreparedStatement.

    Je n'ai pas une grosse expérience avec HSQLDB. Pense aussi à créer des index éventuellement ? Mais comme c'est une base mémoire je ne sais pas si ça a une incidence ?

    Bon courage.

    Romain.

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    La base de données va faire énormément de calcul inutile pour ton besoin, surtout avec des inner join comme tu le fait. C'est bien pour sortir et trier des milliers de données, pas pour l'utiliser comme une bête tableau dont on accède à une ligne à la fois. Il faut bien être conscient que pour un SGDB, une requete qui te sort 1 row et une requête similaire qui te sort 10.000 row, ça s'execute dans le même temps. La même quantité de données est à brasser, l'analyse de la requête SQL reste la même.

    Bien qu'il peux rester intéressant d'y stocker les données, il ne faut pas reposer sur la db pour valider toutes les actions de tes joueurs.

    Il est de loin préférable de garder en mémoire une structure, d'1 ou 2 M par exemple, reprenant l'environnement immédiat de tes joueurs. Je suis sur qu'un joueur n'est jamais entouré, dans son environnement immédiat, par plus de 2000 objets (le moteur graphique ne tiendrait pas le coup) -> à 1k l'objet t'as une structure de 2M/joueur à maintenir, c'est raisonnable. Tu n'a plus qu'à maintenir cett structure en mémoire et la flusher / lire depuis la DB au fur et à mesure que l'utilisateur bouge. Ca pourrait même se faire sur un thread séparé de celui qui gère les actions du joueurs.

    Parce que, franchement, 0.5ms pour valider une action d'un joueur... C'est beaucoup. Tu peux en parcourir des tableau et en faire des tests pendant ce temps là.

    Aussi, vu que tu travaille sur des données très larges (les monde à la minecraft sont pas petits), des structure de données en arbres optimisées pour ce genre de chose seraient plus appropriées. Il ne faut pas oublier de calculer le temps de ton algorithme de recherche en fonction du nombre d'objets totaux dans ton monde.

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Points : 14
    Points
    14
    Par défaut
    Salut à tous et merci pour vos super réponses !

    @Lanrent B

    3-4 :
    Il n'y aurait aucune dépendance entre les bases, ça serait simplement utilisé pour multiplier le nombre de connexions possibles.
    Donc du coup je crois comprendre que c'est une bonne idée. Je retiens de ta réponse, le pool, base répliquée, vues, index. Bref plein de jolies choses à mettre en place

    @rg77140

    Je connais le principe des requetes préparée et j'utilise beaucoup en PHP. Je vais utiliser ce principe aussi.



    @tchize_

    Je remarque que je n'ai pas du tout été assez clair car je ne vois ou tu veux en venir.

    Je comprends bien que la BDD va lire toute la table pour ensuite les trier et les renvoyer, donc plus la table sera lourde, plus la requete mettra longtemps. Effectivement j'aimerais utiliser un "bete tableau" comme tu dis mais qu'il soit
    utilisable à travers les threads et que je puisse le consulter en simultané dans différents thread. C'est parce que je ne sais pas faire ça que j'ai choisi d'utiliser une base de données en RAM.

    Je ne comprends vraiment pas ton principe de structure. Il faut comprendre que ce n'est pas mon code qui gère le monde, c'est le code bukkit déjà écrit pas d'autre gens.
    La seule chose que je puisse faire est d'utiliser les évènement bukkit, faire un calcul puis les annuler ou ne rien faire, ce qui correspond à une validation de la part de mon évènement, transmis à bukkit qui lui même transmet au joueur.

    Je ne comptais pas enregistrer le monde entier de minecraft dans la db, ça serait trop lourd. Par contre je stocke les parcelles sous cette forme:

    id | origin varchar(13) | dimension varchar (6) | owner varchar (40)

    A chaque fois qu'un joueur bouge, une évènement est déclenché, je récupère sa parcelle où il à le droit de bouger, je calcule si la nouvelle position est dans cette parcelle, si non, j'annule le mouvement.
    C'est très simple à faire et ça prends environ 500 nanoseconde tout comme la requete. En tout j'arrive à 1ms requete + calcul.

    Je ne comprends pas pourquoi je stockerais moi même tous les éléments à proximité du joueur alors que bukkit le fais, de toute façon je ne comprends pas pourquoi j'en aurais besoin.
    Et non je ne travaillerais pas sur des données très larges, comme j'ai dis plus haut, pas plus de 1000 enregistrements par table et pas plus de 10 tables.

    Il faut bien comprendre que je ne gère pas l'environnement du jeu avec ces tables, juste des données que je défini pour ajouter des fonctionnalitées, en l'occurence la notion de propriété du terrain qui est totalement inexistante dans minecraft.

    ------------

    Si il y a une solution plus rapide pour lire des données que utiliser une BDD en RAM je suis plus que preneur, cela m'éviterait pas mal de questions et me ferait surement gagner du temps.

    Notez que ces données ne seront jamais modifiées par les joueurs mais seulement par le serveur qui update les parcelles des joueurs de temps en temps, peut être 1 fois toutes les 10 secondes, et encore.

    Je lance le serveur
    Mon plugin s'enclenche et se connecte à MySql
    Un joueur se connecte, on récupère sa percelle en MySql et on l'INSERT en HSQL
    Un joueur place/détruit un block ou bouge SELECT dans HSQL
    calcul de permission => true/false

    C'est ce qui doit se passer mais je peux changer de système de stockage temporaire. En revanche, le reste reste à sa place car il y a des dépendances. Par exemple le site web dépend de MySql.


    Merci encore !

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Disons qu'utiliser une base de donnée "juste" pour ne pas avoir à implémenter du locking au niveau de tes données, c'est un peu tuer une mouche au bazooka

    Ce que tu met en DB, tu peux le garder en mémoire. La lecture de données ne nécessite pas d'exclusion mutuelle. Seule l'écriture nécessite de bloquer quelque nano secondes les accès en lecture. En plus, si je suis bien ton dernier messag, tu va surtout écrire dans cette base de donnée (les parcelles entourante) plus que tu ne va lire (tu ne va lire que là où va le joueur), les sgbd traditionnel sont optimiser pour beaucoup de lectures, peu d'écritures. Si tu continue dans cette voie, teste bien les performance sous de fortes écritures, et pas juste les performances en lecture.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Points : 14
    Points
    14
    Par défaut
    En plus, si je suis bien ton dernier messag, tu va surtout écrire dans cette base de donnée (les parcelles entourante) plus que tu ne va lire (tu ne va lire que là où va le joueur)
    Non c'est l'inverse, beaucoup plus de lectures que d'écritures. Les parcelles entourantes ? Je ne vois pas ce que tu ne comprends pas.

    A chaque mouvement je récupère la parcelle du joueur soit son point d'origine (xyz) ainsi que sa taille pour pouvoir calculer les limites.

    Je calcule si le point est dans la parcelle par une formule simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (nx >= this.px) || (this.px >= nx + diml) || (nz >= this.pz) || (this.pz >= nz + dimw) || (ny >= this.py) || (this.py >= ny + dimh)
    Avec nx/ny/nz le point d'origine et px/py/pz la position du joueur et dim la dimension.

    Je ne lis pas de position, je lis un volume et je calcule si le joueur est dans le volume. La position m'est donnée grace à un event qui s'appelle PlayerMoveEvent et qui fait partie de l'API Bukkit.

    Le rapport écriture/lecture c'est peut etre 1/50 000, voire moins.


    J'ai aussi clairement dis que je ne connaissais pas d'autres solutions... J'ai jamais dis que je voulais éviter un locking des données, surtout si ça peut me faire gagner du temps.

    Je me cite :
    Si il y a une solution plus rapide pour lire des données que utiliser une BDD en RAM je suis plus que preneur, cela m'éviterait pas mal de questions et me ferait surement gagner du temps.
    Bref, je veux bien reconnaitre que je ne suis pas clair mais là tu me fais presque dire l'inverse de ce que j'ai dis

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Si tu veux implémenter des verrous facilement, regarde du coté de commons-locking et du ReadWriteLock
    Tout ce que tu as à faire c'est créer un ReadWriteLock qui protège ta structure. A chaque fois qu'on veux modifier -> Ca bloque les futures lectures et quand tous les lecteurs actuels ont fini: on peux écrire tranquille. A chaque fois qu'on veux lire, ça s'assure que personne ne peux écrire pendant ce temps là.


    Une implémentation ici avec un exemple d'utilisation
    http://docs.oracle.com/javase/6/docs...WriteLock.html

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Points : 14
    Points
    14
    Par défaut
    Ca à l'air bien mais je comprends pas où sont stockées les données. Dans le cache ?
    Un autre aspect qui m'est flou c'est comment faire passer l'accès au ReadWriteLock() entre différents plugin du serveur ?

    C'est pour ça que je me tourne vers une solution BDD, c'est la seule solution que je comprenne plutôt bien.


    Je vais vous exposer l'architecture que j'ai en tête le plus complètement possible, ça permettra de mettre le doigt sur mes lacunes en Java et éventuellement de m'orienter vers les bonnes pages de la doc

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    SERVER START
     
     
    -plugin 1:
    Création de la BDD temporaire à partir de MySql
     
    onEnable(){
       creation de la BDD temporaire avec JDBC:HSQLDB
       connection à MySql avec JDBC
       Récupération de la composition des tables dans MySql
       Création des tables dans Hsql
    }
     
    -plugin 2:
    Création du Pool de connections
     
    onEnable(){
    Connection à HSQL
    Création du pool
    // Ont accède à une référence du pool grâce à un contexte c'est ça ? -> flou
    }
     
     
    -plugin n:
    Récupération des données
     
    onEnable(){Connexion au pool
    Emprunter une pool connexion
    SELECT sur les données temporaires
    Rendre la pool connexion
    }
     
     
     
    ON SERVER SHUTDOWN
    Event(){
    POOL -> attente des retours de connexions
    HSQLDB -> fermeture de la BDD et donc destruction de cette dernière
    }

    Dand tout ce code, HSQLDB peut etre remplacer par ReadWriteLock() c'est ça ?
    Du coup on économise le pool de connection et on accède plus vite aux données ?

    J'ai lu le code de l'implémentation que tu m'a donné. Dans chaque plugin je dois mettre un ReadWriteLock() différent ?


    Merci à toi

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 394
    Points : 639
    Points
    639
    Par défaut
    Tu peux stocker les données sont stockées dans une Map par exemple (a toi de choisir la bonne implémentation de Map en fonction de ta problématique qui est beaucoup de lecture et peu d'écriture).

    Pour faire la comparaison avec une base de donnée et le lien que t'a donné tchize__ :
    - La table SQL correspond à RWDictionary
    - Une ligne de ta table correspond à CachedData

    Au lieu de faire des insert dans ta base tu fais des RWDictionary.put(String,CachedData), et au lieu de faire des select tu fais des RWDictionary.get(String).

    La key utilisée dans le RWDictionary.put et dans le RWDictionary.get serait l'id que tu passais en paramètre de ta requête SQL.

    Pour ma part je ne sais pas si ça sera plus performant qu'HSQLDB mais tu peux tester ça rapidement à mon avis. Essai aussi avec de la volumetrie, car un test avec 1000 lignes dans ta Map ou dans ta table ne sera pas représentatif.

    Romain.

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 45
    Points : 14
    Points
    14
    Par défaut
    Merci pour tes conseils très clairs. Je vais me pencher là dessus.

    Et tu parierais sur lequel comme gagnant ?

Discussions similaires

  1. [Débutant] Comment indiquer le nombre de connexions max a un service web
    Par PascalCmoa dans le forum Services Web
    Réponses: 2
    Dernier message: 22/02/2013, 11h59
  2. Invalidation des connexions du pool JDBC
    Par FBA_17 dans le forum Glassfish et Payara
    Réponses: 1
    Dernier message: 30/12/2009, 17h38
  3. Réponses: 0
    Dernier message: 17/12/2008, 15h30
  4. [9.2] Augmenter le nombre de connexions d'une pool EJB
    Par dingoth dans le forum Weblogic
    Réponses: 1
    Dernier message: 03/07/2008, 15h55
  5. pools jdbc - failover de connexion
    Par morgan59 dans le forum Weblogic
    Réponses: 2
    Dernier message: 25/01/2007, 09h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo