Précédent   Forum des professionnels en informatique > Bases de données > MySQL > Requêtes
Requêtes Forum d'entraide sur les requêtes MySQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 09/01/2011, 11h07   #1
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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 :
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 :
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 :
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 16h25   #2
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
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 : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
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 !

Citation:
- 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 ?


Citation:
- 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 ?

Citation:
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 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Récupérer les joueurs classés entre le rang (begin) et le rang (begin+length):

Code :
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 !

Citation:
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 de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« 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 Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 19h20   #3
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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).

Citation:
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é.

Citation:
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

Citation:
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.

Citation:
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.

Citation:
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.

Citation:
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 '*'.

Citation:
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 :
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 20h31   #4
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
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 : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
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 :
Citation:
=> Met ID_PROFILE en premier dans la clé primaire.
Citation:
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.

Citation:
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 :
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.




Citation:
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 !

Citation:
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 :
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 de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« 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 Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 20h41   #5
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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).
Citation:
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.



Citation:
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 20h47   #6
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
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 : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
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 de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« 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 Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 20h49   #7
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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.

Citation:
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.

Citation:
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 20h55   #8
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
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 : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
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.

Citation:
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 de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« 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 Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2011, 23h21   #9
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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 :
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 :
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/01/2011, 12h01   #10
Modérateur
 
Avatar de nouknouk
 
Homme
Inscription : décembre 2006
Messages : 1 567
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 31

Informations forums :
Inscription : décembre 2006
Messages : 1 567
Points : 1 635
Points : 1 635
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.
nouknouk est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 18h23.


 
 
 
 
Partenaires

Hébergement Web