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

Langage PHP Discussion :

Système de cache pour les tables fixes de la base de données


Sujet :

Langage PHP

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    Points : 39
    Points
    39
    Par défaut Système de cache pour les tables fixes de la base de données
    (je ne sais pas trop si c'est le bon forum / sous-forum pour cette question, si ce n'est pas le cas, désolé )

    Bonjour à tous,

    Dans la droite lignée de mon précédent message concernant le stockage des données d'un utilisateur dans le tableau $_SESSION au lieu de toujours sélectionner les données dans la Base De Données, je suis à la recherche d'un moyen d'optimiser l'utilisation des données statiques de la Base De Données. J'entends par là que, dans le cadre de mon projet de jeu, le contenu de certaines tables ne varie jamais ou alors si peu qu'il devient un non-sens de devoir sans cesse le sélectionner de nouveau alors même que l'on peut être sûr à 99.99% qu'il n'a pas changé depuis la requête précédente ou même depuis le mois précédent. Pour prendre un exemple simple :

    Nom : MPD.png
Affichages : 292
Taille : 21,0 Ko

    Ici donc, la table bâtiment représente les données statiques liées aux bâtiments proposés par le jeu. Autrement dit, lorsque le jeu démarre, le contenu de cette table n'est pas supposé changer à moins d'un changement de version ou d'un équilibrage important à réaliser. Aussi, j'aimerais pouvoir stocker l'intégralité de son contenu, et du contenu lié, à savoir l'inventaire et les ressources de cet inventaire (le tout représentant le coût de construction du bâtiment) dans la mémoire vive du serveur afin de pouvoir y accéder très rapidement et ainsi ne pas perdre de temps avec des requêtes SQL lourdes et sans grand intérêt dans ce genre de situation.

    Seulement, lorsque je cherche avec mon ami Google les possibilités, je suis assez vite noyé par les différentes propositions qui, selon que A ou B en parle, s'avère un coup la meilleure option et le coup suivant la pire (que ce soit en termes de performances ou d'usage)...

    1. Fichiers PHP et système de cache de la Base De Données

    Ces deux options, bien que je ne les ai jamais testées, sont les seules qui semblent mettre tout le monde d'accord, à savoir que les performances sont clairement moins bonnes qu'avec les autres systèmes voire carrément médiocres, ce qui ne m'étonne pas plus que ça puisqu'il s'agit, si j'ai bien compris, d'utiliser le système de fichier et non la mémoire vive. Du coup, je ne suis pas allé voir plus loin de ce côté là.

    2. Memcached

    À la base, tout comme pour mon affaire de $_SESSION, j'espérais tout simplement pouvoir utiliser memcached puisque j'avais lu des articles ici et là n'en disant que du bien. Seulement, il n'y a rien à y faire, il refuse de me générer des identifiants de session pour une raison que je n'explique pas encore (les bugs ici ou pour ne citer que ces deux là sont plus ou moins liés mais ne me permettent pas de résoudre le problème) donc je ne pense pas que ce soit la meilleure des idées pour y stocker les données statiques provenant de la Base De Données tant que son comportement ne sera pas officiellement stabilisé pour la version 7 de PHP.

    EDIT : J'ai réussi à le faire fonctionner, test intégré plus bas.

    3. APC vs SHM

    Il y a des années, lorsque je faisais encore pas mal de PHP, j'avais regardé du côté des appels APC et encore aujourd'hui ça me semble être une solution qui allie une simplicité d'usage à une rapidité d'exécution exemplaire. En essayant de m'y replonger, je suis tombé sur SHM qui, d'après certains blogs, serait un peu plus efficace. J'ai donc réalisé un petit script de comparaison en incluant SQL au passage histoire d'être bien sûr que c'est très lent :

    Code Fichier de test : 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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
     
    <?php
    error_reporting(E_ALL);
     
    ini_set("display_errors", 1);
     
    /**
     *  We want to know what is the best system to cache static database data in the volatile memory.
     *      - APC
     *      - SHM
     *
     *  BE CAREFUL : 
     *      - You need to install php-acpu and php-acpu-bc (backward-compatibility) for ACP
     *      - You need to uncomment these lines in your php.ini file for SHM :
     *          extension=shmop.so
     *          extension=sysvmsg.so
     *          extension=sysvsem.so
     *          extension=sysvshm.so
     *      - You need memcached and php-memcached for Memcached
     */
    require_once('Data.php');
    require_once('generate_database.php');
     
    // We want 20 Data object
    if(!Data::thereIsEnoughLines($_SQL))
        Data::putTwentyLinesInDatabase($_SQL);
     
    // Get the 20 Data object in database
    $data = Data::getTwentyLinesOfData($_SQL);
     
    // Store them with APC cache
    apc_add('data', $data);
     
    // Store them with SHM cache
    $serialized = serialize($data);
     
    $len = strlen($serialized);
     
    $shm_id = shmop_open(0, "c", 0644, $len);
    shmop_write($shm_id, $serialized, 0);
     
    // Store them with Memcached
    $mem = new Memcached();
    $mem->addServer("127.0.0.1", 11211);
     
    $mem->add('data', $data);
     
    // Get Data object from APC
    $start_apc = microtime(true);
     
    apc_fetch('data');
     
    $end_apc = microtime(true);
     
    // Get Data object from SHM
    $start_shm = microtime(true);
     
    unserialize(shmop_read($shm_id, 0, $len));
     
    shmop_close($shm_id);
     
    $end_shm = microtime(true);
     
    // Get Data object from Memcached
    $start_memcached = microtime(true);
     
    $mem->get('data');
     
    $end_memcached = microtime(true);
     
    // Get Data object from Database
    $start_db = microtime(true);
     
    $data = Data::getTwentyLinesOfData($_SQL);
     
    $end_db = microtime(true);
     
    // Print the result
    echo 'Execution time for APC : ' . ($end_apc - $start_apc) . '<br />';
    echo 'Execution time for SHM : ' . ($end_shm - $start_shm) . '<br />';
    echo 'Execution time for Memcached : ' . ($end_memcached - $start_memcached) . '<br />';
    echo 'Execution time for Database : ' . ($end_db - $start_db) . '<br />';
    ?>

    Code Classe Data : 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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
     
    <?php
    class Data {
        public $int_4; // Like an id
        public $string_20; // Kind of username
        public $string_60; // Like password hash
        public $string_80; // Maybe an email address
     
        public function __construct($int_4, $string_20, $string_60, $string_80) {
            $this->int_4 = $int_4;
            $this->string_20 = $string_20;
            $this->string_60 = $string_60;
            $this->string_80 = $string_80;
        }
     
        public static function getTwentyLinesOfData(&$_SQL) {
            $req = 'SELECT * FROM data LIMIT 0, 20';
     
            $results = $_SQL->query($req);
     
            $data = array();
     
            foreach($results as $line)
                $data[] = new Data($line['int_4'], $line['string_20'], $line['string_60'], $line['string_80']);
     
            return $data;
        }  
     
        public static function thereIsEnoughLines(&$_SQL) {
            $req = 'SELECT COUNT(int_4) FROM data';
     
            $result = $_SQL->query($req)->fetch();
     
            if($result['COUNT(int_4)'] >= 20)
                return true;
     
            return false;
        }
     
        public static function putTwentyLinesInDatabase(&$_SQL) {
            $req = 'INSERT INTO data (int_4, string_20, string_60, string_80) VALUES ';
     
            for($i = 0; $i < 20; $i++) {
                $string_20 = substr(hash('sha512', rand()), 0, 20);
                $string_60 = substr(hash('sha512', rand()), 0, 60);
                $string_80 = substr(hash('sha512', rand()), 0, 80);
     
                if($i < 19)
                    $req .= '(' . $i . ', "' . $string_20 . '", "' . $string_60 . '", "' . $string_80 . '"), ';
     
                else
                    $req = $req .= '(' . $i . ', "' . $string_20 . '", "' . $string_60 . '", "' . $string_80 . '")';
            }
     
            try {
                $_SQL->query($req);
            }
     
            catch(PDoException $e) {
                echo 'Erreur PDO : ' . $e->getMessage();
            }
        } 
    }
    ?>

    Code Génération de la BDD : 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
    41
     
    <?php
    // Create database, table and twenty lines of data
    $user = 'SuperAdmin';
    $password = 'SuperAdmin';
     
    try {
        $_SQL = new PDO("mysql:host=localhost", $user, $password);
    }
     
    catch(PDOException $e) {
        echo 'Erreur PDO : ' . $e->getMessage();
     
        exit;
    }
     
    $create_database = 'CREATE DATABASE IF NOT EXISTS `session_vs_database` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
     
    $select_database = 'USE session_vs_database';
     
    $create_data_table = '  CREATE TABLE IF NOT EXISTS `data` (
                                `int_4` int(4) UNSIGNED NOT NULL,
                                `string_20` varchar(20) NOT NULL,
                                `string_60` varchar(60) NOT NULL,
                                `string_80` varchar(60) NOT NULL,                            
                                  PRIMARY KEY (`int_4`),
                                  UNIQUE INDEX `int_4_UNIQUE` (`int_4` ASC)
                            ) ENGINE=InnoDB DEFAULT CHARSET=utf8';
     
    try {
        $_SQL->query($create_database) or die(print_r($_SQL->errorInfo(), true));
        $_SQL->query($select_database) or die(print_r($_SQL->errorInfo(), true));
        $_SQL->query($create_data_table) or die(print_r($_SQL->errorInfo(), true));
    }
     
    catch(PDoException $e) {
        echo 'Erreur PDO : ' . $e->getMessage();
     
        exit;
    }
    ?>

    Le résultat du test semble dire que les deux systèmes se valent (je mets en gras l'exposant puisque la dernière fois je n'étais pas le seul à ne pas l'avoir vu ) :

    Execution time for APC : 3.2901763916016E-5
    Execution time for SHM : 2.9087066650391E-5
    Execution time for Memcached : 7.2956085205078E-5
    Execution time for Database : 0.00011515617370605
    EDIT : memcached deux fois plus lent, ce n'est à mon avis intéressant que lorsque l'on doit fonctionner en cluster, donc pour de gros sites.

    Cependant, du coup, il me faut faire un choix. Là, comme ça, j'aurais tendance à dire que l'utilisation d'APC me semble plus facile, mais n'ayant pas d'expérience particulière dans l'usage des caches en PHP (ou ailleurs d'ailleurs), j'aimerais avoir votre opinion quant-à la meilleure solution à adopter pour pouvoir gérer mes données statiques issues de la Base De Données.

    À noter d'ailleurs que je ne comprends pas pourquoi APC et SHM sont aussi "lents" que l'usage des SESSION dans mon sujet précédent alors que la SESSION est censée être un fichier temporaire et qu'APC et SHM sont censés être des segments dans la mémoire volatile (ou alors je n'ai rien compris, ce qui est possible également).

    Merci d'avance pour vos conseils

    Alfanor.

  2. #2
    Expert éminent sénior
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 223
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 223
    Points : 15 516
    Points
    15 516
    Par défaut
    ce qui prend beaucoup de ressources est le parcours des 4 tables dans une requête. donc si vous voulez optimisez cela, stockez le résultat de la requête dans une nouvelle table de cache.
    ainsi à chaque appel, vous aurez juste besoin d'une requête "SELECT * FROM batiments_cache" ce qui sera plus rapide.
    ensuite faite des tests de vitesse entre la requête complexe et la requête de la table de cache pour vérifier que le gain de temps vous soit utile.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    Points : 39
    Points
    39
    Par défaut
    Si je comprends bien ce que tu proposes, tu voudrais que je fasse une table comme celle que j'ai ajoutée ici, à savoir batiment_cache :

    Nom : MPD.png
Affichages : 200
Taille : 34,0 Ko

    Si tel est le cas, ça ne change concrètement rien à mon problème d'optimisation / de choix de mécanisme.

    Les scripts que j'ai intégrés à mon premier message démontrent que les mécanismes de cache ACP et SHM sont largement plus performants qu'une simple requête SELECT sans jointure ni quoi que ce soit d'autre.

    De manière globale, comme indiqué, mes recherches sur internet m'ont menées à constater que le cache SQL (le vrai, pas une table intermédiaire) est également très lent.

    C'est pourquoi, ici, je cherche à stocker directement en mémoire vive pour gagner un facteur 10 ou plus en temps de récupération des données.

    EDIT : J'ai réussi à faire marcher memcached, j'ai donc édité mon premier message.

  4. #4
    Expert éminent sénior
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 223
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 223
    Points : 15 516
    Points
    15 516
    Par défaut
    je pensais à une table détachée des autres tables. elle contiendre le résultat de la requête qui va lire les 4 autres tables et donc les colonnes "nom batiment", "nom ressource", "quantité", "idBatiement", "idRessource".
    et comme ça en lisant cette table, vous avez directement les données précalculées sans avoir besoin de faire des jointures.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    Points : 39
    Points
    39
    Par défaut
    Oui, ça revient au même dans l'idée.

    Comme je l'ai dis, ça ne correspond pas à mon problème.

    Table isolée ou non, jointures ou non, ce sera toujours bien plus lent qu'APC ou SHM.

    Ce que je cherche c'est à savoir lequel de ces deux mécanismes est le plus intéressant

  6. #6
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Je ne parlerais que de ce que je connais bien et que j'utilise. Essaie avec Redis, en utilisant soit l'extension PHPredis (pour plus de rapidité), soit la bibliothèque Predis en pur PHP.

    C'est facile à facile à utiliser et extrêmement rapide. Je l'utilise pour stocker au format JSON des données issues fichiers CSV comprenant plusieurs centaines de milliers avant un traitement postérieur, et comparé à l'utilisation d'une table avec indexes la vitesse d'écriture et de lecture n'a absolument rien à voir, c'est très, très rapide.

    L'autre avantage c'est que Redis possède des types de données et que tu n'est pas limité à du string partout (contrairement à Memcached et APC), ce qui permet une plus grande optimisation de stockage et de recherche. Le type de stockage parfait pour tes données par exemple est le hash, avec l'id de l'utilisateur en clé et les données en valeur.

    Si tu veux tester, le mieux est de télécharger et compiler la version stable sur redis.io. Les versions proposées par les distributions parce qu'elles sont en retard et sont donc moins rapides/performantes (mais toujours à des années-lumières des bases de données).

    Sinon, si tu veux faire simple, est-ce que tu as testé le moteur de stockage MEMORY de MySQL?

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    Points : 39
    Points
    39
    Par défaut
    Bonjour,

    Je n'avais pas vu Redis, je viens d'un peu me renseigner, sauf erreur de ma part il s'agit d'un système équivalent à memcached, donc fonctionnant en tant que serveur et plutôt destiné à gérer un cache partagé entre de multiples instances d'un site situés sur différentes machines. Dans mon cas ce ne serait donc pas optimal a priori. Plus globalement, d'après ce que je comprends de ce qui est conseillé dans les différentes documentations, tutoriels et discussions, il y a deux architectures de cache selon l'étendu du système que l'on gère :

    • Utilisation d'un cluster
      • Base de données partageant les données entre toutes les machines du cluster
      • Memcached / Redis mettant en cache mémoire pour toutes les machines du cluster
      • APC mettant en cache mémoire les données localement sur chaque machine

    • Utilisation d'un seul serveur
      • Base de données
      • APC stockant les données localement sur la machine

    À l'arrivée donc, Redis et Memcached ne peuvent qu'être plus lents qu'APC ou SHM puisqu'ils opèrent en tant que serveur, ajoutant nécessairement une couche qui réduit les performances lors d'un usage exclusivement local.

    Par contre, Redis a l'air plus performant que Memcached (je n'ai pas encore testé personnellement mais j'ai regardé quelques articles de comparaison) et pourrait donc peut-être me servir pour le stockage des sessions en mémoire vive (s'il peut gérer ce genre de chose mais j'imagine). Par rapport à ce que je disais hier, je viens de comprendre ce pourquoi les sessions sur le disque dur sont quasiment aussi efficaces que celles gérées par Memcached, je réalise mes essais sur un ordinateur portable avec un disque dur SSD et une mémoire vive cadencée à 800Mhz (1 600MT/s me dit mon terminal), autant dire pas du haut de gamme. Considérant que sur un serveur standard c'est tout le contraire, disque lent et mémoire rapide, Memcached devrait devenir bien plus intéressant pour la gestion des SESSION et donc Redis également.

  8. #8
    Membre éclairé Avatar de Geoffrey74
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2007
    Messages
    515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2007
    Messages : 515
    Points : 760
    Points
    760
    Par défaut
    Salut,

    Il y a quelques années, j'ai participé au développement d'un ogame like, et pour palier à cette problématique, nous avions convenu de ne pas stocker les données statiques en BDD, mais plutôt dans un fichier php, sous forme de tableau.
    Nous avions donc un répertoire contenant toutes les définitions des bâtiments, vaisseaux, technologies ect... et ainsi aucune requête en BDD pour récupérer ces infos là.

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    Points : 39
    Points
    39
    Par défaut
    J'avoue avoir également pensé à ça, mais, pour le coup, mon objectif ici étant surtout de me remettre dans le bain du développement en PHP, je trouve dommage de céder à cette option qui n'a pas un grand intérêt ludique si je puis dire ^^

    Après c'est sûr que niveau performance c'est logiquement ce qu'il y a de mieux.

  10. #10
    Membre éclairé Avatar de Geoffrey74
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2007
    Messages
    515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2007
    Messages : 515
    Points : 760
    Points
    760
    Par défaut
    AH oui c'est vrai que pour ce point là, on a été plus faignant

    Cela dis, je vais suivre ton poste avec attention, car ça m’intéresse aussi

  11. #11
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Redis fonctionne aussi bien sur la même machine que l'application ou sur une machine dédiée. Redis est en fait composée de deux binaires principaux: redis-server et redis-client. Tu peux avoir les deux sur la même machine ou sur des machines séparées, le serveur peut servir plusieurs client. Lorsqu'ils sont sur la même machine, la connexion se fait par Unix socket, ce qui est quasi-instantané. La connexion entre différentes machines se fait par TCP, qui est un peu moins rapide (mais dépend du type de connexion, de la distance entre les deux machines etc...).

    Je ne pourrais pas te dire la différence de vitesse entre APC(u) et Redis mais si elle existe elle doit être minimale. Mais dans mon cas le choix est clair parce qu'APC(u) ne propose pas de stockage en list, set, json, pas de pub/sub, pas de transactions, pas d'index secondaire etc... Et est utilisable avec PHP uniquement.

    Redis peut très bien stocker les sessions, et peut même remplacer une base de donnée dans certaines circonstances. Je m'en sers pour stocker rapidement des données afin de décaler à plus tard l'écriture en base de données, qui prends plus de temps.

    Bref, stocker tes données quasi-constantes serait très simple et très rapide avec redis. La seule limite c'est la quantité de RAM allouée.

Discussions similaires

  1. Relation entre les tables (diagramme de la base de données)
    Par unix27 dans le forum Sql Developer
    Réponses: 1
    Dernier message: 13/10/2012, 20h47
  2. masquer les tables system d'une base de donnée
    Par sisi87 dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 12/10/2009, 22h12
  3. alias ou synonymes pour les tables ou les champs ?
    Par nanou9999 dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 16/03/2006, 12h04
  4. [MySQL] Système de votes (pour les membres) : cmt le créer ?
    Par yazerty dans le forum PHP & Base de données
    Réponses: 11
    Dernier message: 20/02/2006, 13h58
  5. Réponses: 1
    Dernier message: 23/10/2005, 01h55

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