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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    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 : 334
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 confirmé
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 698
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 698
    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
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    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 : 229
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 confirmé
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 698
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 698
    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
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 30
    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 Expert

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

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, 19h47
  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, 21h12
  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, 11h04
  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, 12h58
  5. Réponses: 1
    Dernier message: 23/10/2005, 00h55

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