(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 :
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 là 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 ) :
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.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
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.
Partager