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

Requêtes MySQL Discussion :

Gérer un classement de joueurs dans un jeu


Sujet :

Requêtes MySQL

  1. #1
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut Gérer un classement de joueurs dans un jeu
    Bonjour,

    je viens à vous pour un besoin simple (au moins dans sa définition):

    - j'ai un jeu (amateur) dans lequel à chaque partie jouée le joueur gagne ou perd des points, ce qui fait évoluer son score global. A partir du score on peut établir un classement de l'ensemble des joueurs que le joueur peut parcourir à sa guise dans le jeu (le plus haut score est le premier, ...).

    - on a divers classements pour diverses périodes (annuel, mensuel, hebdomadaire, ...) ; chaque classement ne prend en compte que les parties jouées pendant la période concernée.

    - question volumétrie, le jeu rassemble quelques 150 000 joueurs inscrits et une partie est jouée en moyenne toutes les 1.5 secondes (donc les scores des joueurs et donc les classements évoluent à cette fréquence aussi).

    Voilà pour la présentation. Mon principal souci est qu'en l'état actuel des choses, faire des requêtes sur la base mange pas mal de ressources au serveur (calculer le rang d'un joueur, récupérer la liste des joueurs du rang (xxx) à (xxx + 10) pour les afficher, etc...) ; j'aimerais trouver une solution qui soit la plus économe possible.

    Concernant la structure (simplifiée) de ma base, on a:

    - une table 'profil' qui rassemble les caractéristiques du joueur (id, pseudo, avatar, ...).

    ID_PROFILE bigint(20)
    PSEUDO varchar(25)
    AVATAR smallint(6)

    PRIMARY: (ID_PROFILE)
    INDEXES: aucun (en dehors du primary)


    - plusieurs tables, une pour chaque type de période de classement: charts_yearly, charts_monthly, charts_weekly, ...

    PERIOD int(11)
    ID_PROFILE bigint(20)
    SCORE int(11)

    PRIMARY: (PERIOD, ID_PROFILE)
    INDEXES: (PERIOD, ID_PROFILE, SCORE), (SCORE)

    Le champ 'PERIOD' est un identifiant pour la période concernée. Par exemple dans charts_monthly, PERIOD vaut (année * 100 + mois), donc un enregistrement avec PERIOD = 201012 représente le classement pour le joueur PROFILE pour ses parties jouées en décembre 2010.
    On a donc 0 ou 1 enregistrement pour chaque couple (PERIOD, ID_PROFILE) ; zéro si le joueur n'a joué aucune partie pendant la période donnée.

    Le champ 'SCORE' est le cumul des points gagnés/perdus à chaque partie.

    Les requêtes qui sont faites sur la base:
    (Les valeurs entre [ ] sont les valeurs injectées dans la requête en fonction du contexte)

    A la fin d'une partie, pour créer/mettre à jour un classement (exemple ici, le classement annuel ; il y a autant de requêtes faites que de types de classement (annuel, mensuel, hebdomadaire, ...):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    INSERT INTO charts_yearly 
        (PERIOD, ID_PROFILE, SCORE) 
        VALUES ( (YEAR(NOW())), [profile_id], [scorePartie] ) 
    ON DUPLICATE KEY UPDATE 
        SCORE=SCORE + [scorePartie];
    Récupérer le rang d'un joueur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT count(*) AS RANK
        FROM charts_yearly
        WHERE
            PERIOD = [period]
        AND 
            (SCORE < [playerScore] OR 
            (SCORE = [playerScore] AND ID_PROFILE < [playerProfileId] )

    Récupérer les joueurs classés entre le rang (begin) et le rang (begin+length):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SET @RANK:=[begin]-1; 
    SELECT @RANK:=@RANK+1 AS RANK, c.*, p.* 
        FROM charts_yearly c, profiles p
        WHERE c.PERIOD = [period] 
        AND c.ID_PROFILE = p.ID_PROFILE
        ORDER BY c.SCORE DESC, c.ID_PROFILE ASC 
        LIMIT [begin], [length]
    Enfin pour récupérer le classement autour d'un joueur donné, je fais d'abord la requête n°2 pour récupérer son rang puis la requête n°3 pour récupérer les joueurs aux rangs [rang - 5, rang + 5]

    Voilà.

    Après un premier test sur mon laptop une base avec 150 000 profils et 300 000 entrées dans charts_yearly (deux entrées par profil pour 2010 et 2011), je mets près de 600ms lorsque je veux récupérer les 10 joueurs classés du 100 000 ème au 100 010 ème (cf. la dernière requête).

    Cette dernière requête semble peu affectée par le nombre d'enregistrements dans la table (j'obtiens les même temps avec charts_weekly qui a 7 millions d'enregistrements), mais beaucoup plus par les valeurs de LIMIT: il est beaucoup plus lent de récupérer les rangs [100 000, 100 010] que les rangs [100, 110].

    EDIT: avec PhpMyAdmin, un profiling de la requête donne ceci:


    Et encore, c'est ce que j'obtiens sur mon laptop (un core-i5 + SSD) ; le serveur de prod actuel étant un modeste Pentium Dual Core E5200 AVEC 2Go de RAM.

    Sachant que les joueurs consultent très régulièrement le classement mais que ce n'est pas la seule chose que le serveur ait à gérer (loin de là), vous comprenez aisément mon souci.

    Si vous aviez des conseils avisés, je serais évidemment preneur

    PS: Pour info le principal soft qui utilise le serveur MySQL est le serveur de jeu (en Java, via JDBC) ; il y a une connexion distincte pour chaque 'fonctionnalité' (gestion des utilisateurs + authentification, gestion des classements, ...) ; à terme, un site web devra pouvoir également fournir ces infos.
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  2. #2
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par nouknouk Voir le message
    - j'ai un jeu (amateur)
    ...
    - le jeu rassemble quelques 150 000 joueurs inscrits
    Tu as beaucoup de copains toi !
    À ce niveau là ce n'est plus de l'amateurisme !

    - une table 'profil' qui rassemble les caractéristiques du joueur (id, pseudo, avatar, ...).

    ID_PROFILE bigint(20)
    PSEUDO varchar(25)
    AVATAR smallint(6)

    PRIMARY: (ID_PROFILE)
    INDEXES: aucun (en dehors du primary)
    Commence par ajouter un index unique sur le pseudo. Quand un joueur se connecte, c'est bien par là que tu le cherches dans la table non ?


    - plusieurs tables, une pour chaque type de période de classement: charts_yearly, charts_monthly, charts_weekly, ...

    PERIOD int(11)
    ID_PROFILE bigint(20)
    SCORE int(11)

    PRIMARY: (PERIOD, ID_PROFILE)
    INDEXES: (PERIOD, ID_PROFILE, SCORE), (SCORE)

    Le champ 'PERIOD' est un identifiant pour la période concernée. Par exemple dans charts_monthly, PERIOD vaut (année * 100 + mois), donc un enregistrement avec PERIOD = 201012 représente le classement pour le joueur PROFILE pour ses parties jouées en décembre 2010.
    On a donc 0 ou 1 enregistrement pour chaque couple (PERIOD, ID_PROFILE) ; zéro si le joueur n'a joué aucune partie pendant la période donnée.
    Je pense que tu as moins de 150 000 périodes dans une table mais que par contre tu peux avoir potentiellement les 150 000 joueurs dans la table ? Autrement dit, dans une table de classement, les joueurs sont beaucoup plus nombreux que les périodes ?
    => Met ID_PROFILE en premier dans la clé primaire.
    => Met un index individuel sur la seconde colonne de la clé primaire, donc maintenant sur PERIOD.
    Dans un index multi-colonnes, que ce soit la clé primaire ou un index ordinaire, seule la première colonne est indexée individuellement ; les suivantes ne le sont que par rapport à la précédente. Comme je pense que tu dois chercher autant par chacune des deux colonnes, il te faut cet index supplémentaire sur la seconde colonne de la clé primaire.

    Quel est l'intérêt d'ajouter SCORE dans un index multi-colonnes par rapport à la clé primaire ?

    Récupérer le rang d'un joueur:
    Cette requête suppose que tu as déjà récupéré le score du joueur.

    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
    Récupérer les joueurs classés entre le rang (begin) et le rang (begin+length):
    
    
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    12345678
     
    SET @RANK:=[begin]-1; 
    SELECT @RANK:=@RANK+1 AS RANK, c.*, p.* 
        FROM charts_yearly c, profiles p
        WHERE c.PERIOD = [period] 
        AND c.ID_PROFILE = p.ID_PROFILE
        ORDER BY c.SCORE DESC, c.ID_PROFILE ASC 
        LIMIT [begin], [length]
    Tu ne vas pas afficher l'avatar ni l'ID_PROFILE du joueur ? Alors ne demande pas l'extraction de ces données, c'est du temps de perdu pour rien !
    La syntaxe normalisée depuis 1992 pour les jointures utilise l'opérateur JOIN !

    Si vous aviez des conseils avisés, je serais évidemment preneur
    Pour plus d'infos sur l'optimisation des BDD et des requêtes, regarde chez SQLPro.
    http://sqlpro.developpez.com/cours/quoi-indexer/
    http://sqlpro.developpez.com/optimis...ntenanceIndex/
    http://sqlpro.developpez.com/optimisation/indexation/
    http://sqlpro.developpez.com/optimis...ntenanceIndex/
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  3. #3
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Tout d'abord merci pour avoir pris la peine de répondre à mon roman-fleuve

    Citation Envoyé par CinePhil Voir le message
    Tu as beaucoup de copains toi !
    À ce niveau là ce n'est plus de l'amateurisme !
    Disons que c'est de "l'amateurisme éclairé" ; mon jeu ayant profité d'une forte visibilité, d'où le grand nombre de joueurs et de parties jouées (+ de 7 millions en 5 mois).

    Commence par ajouter un index unique sur le pseudo. Quand un joueur se connecte, c'est bien par là que tu le cherches dans la table non ?
    Le cas de mon jeu est un peu spécial ; l'auth se faisant par l'IP du joueur qui a également un index (mais ne fait pas partie du profil, il y a un niveau de plus, 'account' qui peut avoir [1..n] 'profils') ; je n'ai pas tout présenté ici pour une question de lisibilité.

    Je pense que tu as moins de 150 000 périodes dans une table mais que par contre tu peux avoir potentiellement les 150 000 joueurs dans la table ? Autrement dit, dans une table de classement, les joueurs sont beaucoup plus nombreux que les périodes ?
    [...]
    Dans un index multi-colonnes, que ce soit la clé primaire ou un index ordinaire, seule la première colonne est indexée individuellement
    Oui, il me semblait avoir lu que l'ordre des champs dans l'index était très important et mes requêtes sont toujours sur une PERIOD en particulier et c'est la première clause de mon WHERE, d'où ce choix.

    Mais vu mon (non) niveau en BdD, je pense qu'il vaut mieux que je te fasse confiance

    Comme je pense que tu dois chercher autant par chacune des deux colonnes, il te faut cet index supplémentaire sur la seconde colonne de la clé primaire.
    Non, justement: même pour un joueur en particulier, je cherche toujours pour une et une seul PERIOD.

    Quel est l'intérêt d'ajouter SCORE dans un index multi-colonnes par rapport à la clé primaire ?
    Probablement aucun, je pensais (à tort ?) que ça pouvait aider pour le 'sort' sur le score quand je cherche un classement ou le rang d'un joueur.

    Cette requête suppose que tu as déjà récupéré le score du joueur.
    Oui, et c'est le cas: quand je fais cette requête, c'est pour un joueur connecté et le serveur a déjà le profil du joueur incluant cette info.

    Tu ne vas pas afficher l'avatar ni l'ID_PROFILE du joueur ? Alors ne demande pas l'extraction de ces données
    Si: ce sont des infos que je transmets au client, soit pour l'affichage, soit pour avoir un ID pour savoir de qui on parle
    J'ai également quelques spécificités pour le serveur (qui se doit d'être relativement générique par rapport à la structure de la BdD, c'est un peu long à expliquer), mais merci pour l'info, je porterai une attention particulière à cette histoire de '*'.

    La syntaxe normalisée depuis 1992 pour les jointures utilise l'opérateur JOIN !
    Ok, je jetterai un oeil, même si mon objectif n'est pas de faire la chose la plus 'propre' possible.

    Et merci pour les liens.

    Questions subsidiares:

    1) j'effectue différentes requêtes pour différents besoins ; certaines ne sont pas critiques (genre récupérer le classement), mais d'autres ont besoin d'être traitées en priorité (authentification par exemple) ; les LOW_PRIORITY et HIGH_PRIORITY peuvent-ils m'aider de façon efficace ? A défaut:
    - peut-on définir plusieurs utilisateurs sur ma BdD avec différents niveaux de priorité
    - éventuellement lancer deux démons de deux BdD sur le serveur, celle avec les requêtes 'non prioritaires' étant une réplication (slave) de la première.

    Bref, quelles solutions s'offrent à moi pour prioriser mes requêtes ?

    2) question configuration du démon Mysql, j'imagine que le plus important est de laisser un maximum de RAM à MySql pour qu'il mette en cache un maximum de choses. Dois-je tripoter les variables de mon '/etc/mysql/my.cnf' ou il est capable de s'adapter tout seul en fonction de la mémoire inutiliée sur la machine (ce qui ne semble pas être le cas, vue la RAM non utilisée actuellement) ? Genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    key_buffer              = 256M
    query_cache_limit       = 1M
    query_cache_size        = 16M
    3) mon serveur de jeu fait des requêtes qui sont reconstruites à chaque fois: je n'utilise pas de PreparedStatement pour le moment ; cela pourrait-il avoir une conséquence importante sur les performances de mes requêtes ? Perso, J'ai pas trop l'impression quand je regarde les stats du profiling dans mon premier post, mais je me trompe peut-être...
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  4. #4
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par nouknouk Voir le message
    Oui, il me semblait avoir lu que l'ordre des champs dans l'index était très important et mes requêtes sont toujours sur une PERIOD en particulier et c'est la première clause de mon WHERE, d'où ce choix.

    Mais vu mon (non) niveau en BdD, je pense qu'il vaut mieux que je te fasse confiance
    Le principe, expliqué par SQLPro, est qu'il faut mettre en premier la colonne ayant le plus grand nombre de valeurs différentes. Même dans ta table de classement hebdo, pour que le nombre de périodes soit plus grand que le nombre de joueurs, ça te ferait 150 000 / 52 = 2884 ans !
    Donc, bis repetita :
    => Met ID_PROFILE en premier dans la clé primaire.
    Non, justement: même pour un joueur en particulier, je cherche toujours pour une et une seul PERIOD.
    Oui mais tu cherches aussi le joueur dans la table ! Tes requêtes le montrent d'ailleurs !
    En plus, ID_PROFILE sert dans une condition de jointure ! Raison de plus pour mettre cette colonne en premier afin qu'elle ait son index individuel et pour mettre un index individuel sur la période. Quoique si en fait tu cherches toujours joueur + période, l'index individuel sur la période devient inutile. Mais si tu cherches aussi la période sans le joueur, alors il te faut l'index individuel.

    Probablement aucun, je pensais (à tort ?) que ça pouvait aider pour le 'sort' sur le score quand je cherche un classement ou le rang d'un joueur.
    Ajouter SCORE à l'index multicolonnes (ID_PROFILE, PERIOD) n'a d'intérêt que si tu cherches dans une requête quelques chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    WHERE ID_PROFILE = :idProfile
      AND PERIOD = :period
      AND SCORE = :score
    Peut-être que ça peut être utile en cas de BETWEEN sur le score mais c'est pas sûr. Ce serait à vérifier par un EXPLAIN de la requête en question.




    Questions subsidiares:

    1) j'effectue différentes requêtes pour différents besoins ; certaines ne sont pas critiques (genre récupérer le classement), mais d'autres ont besoin d'être traitées en priorité (authentification par exemple) ; les LOW_PRIORITY et HIGH_PRIORITY peuvent-ils m'aider de façon efficace ? A défaut:
    - peut-on définir plusieurs utilisateurs sur ma BdD avec différents niveaux de priorité
    - éventuellement lancer deux démons de deux BdD sur le serveur, celle avec les requêtes 'non prioritaires' étant une réplication (slave) de la première.

    Bref, quelles solutions s'offrent à moi pour prioriser mes requêtes ?
    es-tu sûr d'en avoir besoin ?

    Même avec 150 000 joueurs, une requête pour chercher un joueur lors de son authentification ne doit prendre qu'une toute petite fraction de seconde.

    Avant d'envisager ces choses compliquées que je n'ai moi-même jamais mises en oeuvre, teste sans !

    2) question configuration du démon Mysql, j'imagine que le plus important est de laisser un maximum de RAM à MySql pour qu'il mette en cache un maximum de choses. Dois-je tripoter les variables de mon '/etc/mysql/my.cnf' ou il est capable de s'adapter tout seul en fonction de la mémoire inutiliée sur la machine (ce qui ne semble pas être le cas, vue la RAM non utilisée actuellement) ? Genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    key_buffer              = 256M
    query_cache_limit       = 1M
    query_cache_size        = 16M
    3) mon serveur de jeu fait des requêtes qui sont reconstruites à chaque fois: je n'utilise pas de PreparedStatement pour le moment ; cela pourrait-il avoir une conséquence importante sur les performances de mes requêtes ? Perso, J'ai pas trop l'impression quand je regarde les stats du profiling dans mon premier post, mais je me trompe peut-être...
    C'est aussi un domaine que ne maîtrise pas et je n'ai jamais eu à y toucher.

    Ce qui est sûr, c'est qu'un serveur de bases de données doit être généreusement pourvu en mémoire vive car le SGBD doit travailler quasi exclusivement avec.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  5. #5
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    es-tu sûr d'en avoir besoin ?
    Même avec 150 000 joueurs, une requête pour chercher un joueur lors de son authentification ne doit prendre qu'une toute petite fraction de seconde.
    Pour ce point, ce qui me fait peur ce n'est pas tant la longueur des requêtes en elle-même, c'est le risque que j'ai x requêtes 'non prioritaires' en file d'attente alors que j'ai la (x+1)ième qui gagnerait à passer avant toutes les autres (je sais pas si je suis bien clair).
    Avant d'envisager ces choses compliquées que je n'ai moi-même jamais mises en oeuvre, teste sans !
    En fait, je teste déjà actuellement sans, et j'ai parfois quelques soucis, peut-être en partie dûs à ça (ou pas ) même si la version actuelle est moins avancée, moins bien designée (requêtes synchrones du serveur de jeu) et propose moins de fonctionnalités que celles présentées dans le post.
    Mes questions portent en fait sur l'implémentation en cours d'une 'v2' de mon serveur actuel.



    Ce qui est sûr, c'est qu'un serveur de bases de données doit être généreusement pourvu en mémoire vive car le SGBD doit travailler quasi exclusivement avec.
    On est d'accord, c'est ce que j'ai crû comprendre de mes diverses lectures.

    Dans mon cas, j'ai 2Go de RAM pour le serveur. Admettons que le système + mon démon de serveur de jeu mangent en tout 300Mo de RAM. Ce serait bête qu'à cause d'une bêtise de configuration, le serveur MySQL se limite à quelques centaines de Mo alors que je pourrais lui filer sans problème 1.5Go pour lui tout seul.

    D'où ma question de savoir si les valeurs exprimées dans le fichier de configuration sont des valeurs 'minimales' (que le serveur MySQL peut dépasser) ou si ce sont des valeurs 'plafond' qu'il ne dépassera pas même s'il reste de la RAM inutilisée).
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  6. #6
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par nouknouk Voir le message
    Pour ce point, ce qui me fait peur ce n'est pas tant la longueur des requêtes en elle-même, c'est le risque que j'ai x requêtes 'non prioritaires' en file d'attente alors que j'ai la (x+1)ième qui gagnerait à passer avant toutes les autres (je sais pas si je suis bien clair).
    Tu sais, une "file d'attente" de nos jours en informatique, c'est infiniment plus rapide qu'à la caisse de l'hypermarché ! De l'ordre de la seconde en principe !

    Si la durée moyenne d'une requête est de 10 millisecondes, le serveur peut exécuter 100 requêtes par seconde.

    Avec 5 millions de parties en 1 mois, ça te fait une moyenne de 5M / 30 / 24 / 3600 = 1,92 partie par seconde. Même avec plusieurs requêtes pour une partie, je pense que tu n'auras pas de problème de ce côté.

    Pour le reste, je m'en remets à plus spécialistes que moi.

    Au fait, c'est quoi ton jeu ? Celui de ta signature ? BounceBox ?
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  7. #7
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Tu sais, une "file d'attente" de nos jours en informatique, c'est infiniment plus rapide qu'à la caisse de l'hypermarché ! De l'ordre de la seconde en principe !

    Si la durée moyenne d'une requête est de 10 millisecondes, le serveur peut exécuter 100 requêtes par seconde.
    Le souci c'est que mes tests montrent que certaines requêtes peuvent prendre 600ms (cf. premier post). Si j'en ai 30 d'un coup et que ma requête d'auth passe seulement après, ça va devenir problématique.

    Même avec plusieurs requêtes pour une partie, je pense que tu n'auras pas de problème de ce côté.
    C'est surtout les requêtes 'hors parties' qui posent souci ; typiquement quand le joueur navigue dans le classement.

    Au fait, c'est quoi ton jeu ?
    la réponse (partielle) est dans ma signature
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  8. #8
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par nouknouk Voir le message
    Le souci c'est que mes tests montrent que certaines requêtes peuvent prendre 600ms (cf. premier post). Si j'en ai 30 d'un coup et que ma requête d'auth passe seulement après, ça va devenir problématique.
    Mais si tu mets en oeuvre les conseils d'optimisation que je t'ai donnés, il y a des chances que ça réduise la durée des requêtes.
    Lis notamment le lien que je t'ai donné sur une étude d'optimisation par l'exemple. Je crois que c'est dans ce document que SQLPro démontre comment réduire de plus de 90% le temps d'exécution d'une requête. Ça s'applique à SQL Server et certaines choses ne sont pas applicables à MySQL mais d'autres telles que celles que je t'ai dites sont tout à fait applicables.

    C'est surtout les requêtes 'hors parties' qui posent souci ; typiquement quand le joueur navigue dans le classement.
    Mais celles-là sont moins critiques non ? L'utilisateur peut bien attendre une seconde pour voir son nouveau classement ?

    J'irai jeter un oeil à ton jeu, même si je préfère jouer sur vrai billard... carambole, pas américain !
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  9. #9
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par CinePhil Voir le message
    Mais si tu mets en oeuvre les conseils d'optimisation que je t'ai donnés, il y a des chances que ça réduise la durée des requêtes.
    J'ai mis en oeuvre le changements d'ordre dans la clef primaire et l'ajout d'un index sur PERIOD. Je n'ai pas vu de changement dans la durée de ma requête de test (récupérer les 100 000 à 100 010 èmes classés dans charts_weekly pour la 12ème semaine de 2010): toujours des temps de l'ordre de 600ms (ci-dessous).

    Grosso modo, même chose si je réduis le nombre de champs récupérés dans la jointure avec 'profiles' (par exemple en remplaçant p.* par un simple p.PSEUDO).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    SET @RANK:=99999;
    SELECT @RANK:=@RANK+1 AS RANK, c.*, p.* 
        FROM charts_weekly c, profiles p 
        WHERE PERIOD=2010012 AND c.ID_PROFILE=p.ID
        ORDER BY SCORE DESC, ID_PROFILE ASC 
        LIMIT 99999, 10;
    Puis j'ai effectué quelques tests supplémentaires:

    Première surprise: si je supprime complètement la jointure avec 'profiles' pour ne travailler que sur la table 'charts_weekly', la requête prend alors trois fois moins de temps (200ms environ).

    Deuxième surprise: j'ai poussé la logique un peu plus loin en séparant clairement les deux requêtes: d'abord un SELECT sur charts_weekly dont le résultat est repris et joint ensuite dans un second SELECT sur profiles.

    Cette requête (ci-dessous) qui retourne exactement le même résultat que le tout premier test tourne également aux alentours de 200ms. J'ai donc une réduction du temps d'exécution d'un facteur 3 juste pour une réécriture de la requête !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    SET @RANK:=99999;
    SELECT c.*, p.* FROM (
        SELECT 
            @RANK:=@RANK+1 AS RANK, c.* 
            FROM charts_weekly c 
            WHERE PERIOD=2010012 
            ORDER BY SCORE DESC, ID_PROFILE ASC 
            LIMIT 99999, 10
        ) AS c, 
        profiles p 
        WHERE c.ID_PROFILE = p.ID;
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  10. #10
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Je reviens vers vous pour deux questions supplémentaires:

    1/ MySQL semble supporter la partitionnement de tables depuis la version 5.1 ; je me dis que ça pourrait être plus intelligent de regrouper l'ensemble de mes tables (charts_yearly, charts_monthly, ...) dans une seule table partitionnée (pour les perfs). Cela a-t-il des conséquences (genre sur les locks, les perfs, ...) ?

    2/ Admettons qu'on n'aie plus un seul jeu mais plusieurs qui partagent le même serveur physique ; vaut-il mieux avoir un démon MySQL distinct pour chaque jeu (et donc chaque base) ou bien vaut-il mieux avoir un seul démon MySQL qui héberge toutes les bases ? Quels avantages / inconvénients dans chaque cas ?

    Merci d'avance
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

Discussions similaires

  1. Réponses: 2
    Dernier message: 24/03/2011, 15h18
  2. Qui calcule dans un jeu multi joueur ?
    Par Davidbrcz dans le forum Réseau et multijoueurs
    Réponses: 16
    Dernier message: 18/08/2010, 09h37
  3. Déterminer le classement en temps réél dans un jeu de course
    Par j0o0 dans le forum Algorithmes et structures de données
    Réponses: 3
    Dernier message: 19/09/2008, 20h16
  4. soumettre un formulaire contenu dans un jeu de cadre
    Par nicoulou dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 02/06/2005, 21h50
  5. Réponses: 2
    Dernier message: 13/10/2004, 15h32

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