Serveurs : améliorer les performances globale
La base de données et le serveur d’application forment souvent le principal goulot d’étranglement d’une architecture Web. Chaque opération qui prend trop de temps induit un temps latence supplémentaire avant le traitement suivant. A ce niveau, l’écoconception logicielle consiste donc surtout à écrire un code efficient afin de garantir un temps de traitement le plus court possible, pour libérer au plus vite le serveur afin qu’il soit disponible pour un nouveau traitement. On économise ainsi des ressources (nombre de machines physiques nécessaires pour délivrer le service).
Pour garantir qu’aucun goulot d’étranglement n’imposera d’ajouter des machines supplémentaires, il faut partir du fond de l’architecture et la remonter progressivement. En effet, plus le blocage est loin du navigateur dans la chaîne applicative et plus il la paralysera dans son ensemble.
Commençons donc par la base de données. Limiter les requêtes SQL au strict minimum
Le phénomène d’obésiciel (ou « gras numérique ») n’est pas limité au code qui s’exécute sur le serveur d’applications ; les requêtes SQL sont souvent les premières concernées car les développeurs sont rarement des as du SQL. Il faut pourtant les optimiser dès leur écriture, et non en fin de projet (car on ne le fait jamais). La première recommandation, qui semble une évidence, mais qui n’est jamais mise en oeuvre sur le terrain, est de ne jamais écrire de requête du type SELECT * FROM. En effet, le serveur de base de données doit résoudre les champs en fonction du schéma. Si vous connaissez le schéma, nommez les champs.
Par exemple, ne pas écrire :
SELECT * FROM clients;
Préférez :
SELECT raison_sociale, adresse, code_postal, telephone FROM clients;
La seconde bonne pratique consiste à limiter le nombre de résultats retournés par la base de données. Rappelez-vous que chaque donnée inutile provenant de la base de données sera traitée par le serveur d’application, transportée sur le réseau, retraitée par le navigateur, etc. C’est donc en début de chaîne applicative qu’il faut limiter les dégâts. Si vous souhaitez n’afficher que les 10 premiers enregistrements d’une table contenant le nom et le prénom de personnes, lors de la sélection, utilisez la clause LIMIT. Par exemple, remplacer :
SELECT prenom, nom FROM personnes;
Par :
SELECT prenom, nom FROM personnes LIMIT 0, 10;
Utiliser le SGBD/R pour ce qu’il sait faire
Lorsque cette fonctionnalité est disponible et que vous ne recourez pas à un middleware qui gère l’accès aux données pour vous, commencez par utiliser les procédures stockées. Les procédures stockées sont bien plus performantes que les requêtes SQL envoyées par le serveur d’application car les requêtes SQL sont déjà compilées. Il faut donc s’appuyer au maximum sur cette fonctionnalité de la base de données pour réduire les cycles CPU et la mémoire vivre consommée par le SGBD/R.
Evitez également d’effectuer des requêtes SQL à l’intérieur d’une boucle car cela pose de gros problèmes de performance. En effet, les serveurs SQL sont optimisés pour traiter plusieurs sélections, insertions ou modifications dans une seule requête ou une seule transaction. Envoyer un grand nombre de petites requêtes constitue le meilleur moyen de mettre le serveur de base de données à genoux.
Par exemple, éviter d’écrire :
foreach ($userList as $user) { $query = 'INSERT INTO users (first_name,last_name) VALUES (""' . $user['first_name'] . '"", ""' . $user['last_name'] . '"")'; mysql_query($query); }
Ecrire plutôt :
$userData = array(); foreach ($userList as $user) { $userData[] = '(""' . $user['first_name'] . '"", ""' . $user ['last_name'] . '"")'; } $query = 'INSERT INTO users (first_name,last_name) VALUES' . implode(',', $userData); mysql_query($query);
Enfin, le meilleur moyen de réduire la charge du SGBD/R est de ne pas l’utiliser. Déclencher une connexion à un serveur de base de données est coûteux en ressources pour l’applicatif, pour le serveur de base, éventuellement pour le réseau. Chaque fois que l’applicatif peut se passer de la base de données, faites-le. Ce conseil peut paraître évident, mais la segmentation des rôles et des responsabilités dans les équipes de développement nous amène à rencontrer ce type d’absurdité à chaque audit que nous réalisons. Bien souvent, les développeurs ne disposent pas d’assez de temps pour optimiser leur code, ils préfèrent donc interroger la base de données aussi souvent que nécessaire, sans chercher à stocker le résultat des requêtes dans des caches ou des variables locales. C’est pourtant la méthode la plus efficace pour décharger le SGBD/R.
Réduire la quantité de mémoire consommée par le serveur d’applications
Utiliser des variables statiques. Quand c’est possible, l’utilisation de variables statiques limite le gaspillage de temps CPU en évitant d’exécuter plusieurs fois le même code. Cette technique est particulièrement utile pour les procédures gourmandes en ressource.
Par exemple, pour ne charger qu’une seule fois un parser lourd à initialiser, utiliser les variables statiques :
private function dataParserLoad() { static $done = FALSE; if (!$done) { require_once APPLICATION_PATH_APP . '/libraries/DataParser/ Parser.php'; $this->parser = new DataParser(); $done = TRUE; } }
Libérer de la mémoire les variables qui ne sont plus nécessaires. Supprimez notamment les tableaux qui peuvent rapidement contenir des quantités importantes d’informations.
Par exemple, optimiser le code suivant :
$crm = new CrmConnection(); $this_month_clients = $crm->fetchAllClients()->filtersByLast Activity(LAST_MONTH); $recipes = some_long_function_which_extracts_recipes_from_ clients($this_month_clients); $pdfs = generate_pdf_for_recipes($recipes); return $pdfs;
Par :
$crm = new CrmConnection(); $this_month_clients = $crm->fetchAllClients()->filtersByLast Activity(LAST_MONTH); unset($crm); $recipes = some_long_function_which_extracts_recipes_from_ clients($this_month_clients); $pdfs = generate_pdf_for_recipes($recipes); unset($recipes); return $pdfs;
Evidemment, le plus simple est de ne pas assigner inutilement de valeur aux variables. Eviter de déclarer et d’utiliser des variables lorsque ce n’est pas indispensable. En effet, à chaque allocation correspond de la RAM occupée.
Par exemple, évitez :
$clients = $crm->fetchAllClients(); return $clients;
Ecrivez plutôt :
return $crm->fetchAllClients();
Optimiser le code
Comme pour les requêtes SQL, il est préférable de ne pas appeler de fonction dans la déclaration d’une boucle de type for, afin d’éviter que la fonction ne soit appelée à chaque itération de la boucle.
Par exemple, ne pas écrire :
for ($i = 0; $i < count($array); $i++) {}
Mais :
$count = count($array); for ($i = 0; $i < $count; $i++) {}
Utiliser la simple côte (‘) au lieu du guillemet (« ). Pour déclarer une chaîne de caractères en PHP, on peut l’encadrer par des quotes (‘) ou des guillemets ( »). La forme avec les guillemets permet au développeur d’insérer des variables qui seront substituées lors de l’exécution. Si vous n’avez pas de variable dans une chaîne de caractères, utilisez les quotes. Ainsi PHP ne recherchera pas les variables à substituer. Ce qui réduira la consommation de CPU.
Par exemple, optimiser le code suivant :
echo ""Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt."";
Par :
echo 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt.';
Remplacer les $i++ par ++$i. La forme $i++ a l’inconvénient de générer une variable temporaire lors de l’incrémentation, ce qui n’est pas le cas dans le forme ++$i. On économise ainsi de la mémoire et quelques cycles CPU. Par exemple, $i++ génère 4 opcodes (PHP) alors que ++$i n’en génère que 3.
Mettre en cache les données calculées souvent utilisées. Lorsque des calculs de valeurs ou de données sont coûteux en ressource, les mettre en cache afin d’éviter de recommencer ces opérations alors que les valeurs sont inchangées. Par exemple, les systèmes de cache de type Key-Value Store sont prévus pour stocker ces données. Généralement montés uniquement en RAM, ils génèrent d’importantes économies de CPU si les données calculées sont très souvent sollicitées.
Enfin, si le site repose sur un CMS, ce qui est désormais le cas le plus fréquent, utilisez tous les niveaux de cache du CMS.
Ne pas surcharger le serveur inutilement
Dans la mesure du possible, lorsque qu’une ressource est introuvable, ne générez pas de page 404 depuis le serveur d’application et laissez le serveur HTTP (ou le cache) et le navigateur se débrouiller. Lorsque le navigateur demande une ressource qui n’existe pas (image, CSS, fichier JavaScript, etc.), le serveur répond par la page 404. Celle-ci peut être plus lourde que la ressource demandée et donc impacter significativement le serveur d’application. Certains CMS exécutent par exemple leur routine de recherche de contenu (dans la base de données) pour tenter de trouver la page demandée.
Par conséquent, du code serveur est exécuté, le serveur de base de données est sollicité, la génération dynamique de la page HTML est exécutée.
Ce qui aboutit à un gaspillage de CPU, RAM, bande passante. Dans le même esprit évitez les redirections et utilisez la méthode GET pour les requêtes Ajax. Les requêtes en POST utilisent 2 connexions TCP – une pour le header, l’autre pour les données à transférer – alors que les requêtes HTTP en GET n’utilisent qu’une seule requête TCP.
Elles permettent donc une économie en nombre de requêtes.
Enfin, réduisez au maximum la taille des cookies. Ces petits fichiers texte servent à maintenir une connexion entre le navigateur et le serveur HTTP. Le protocole HTTP n’a, en effet, pas été conçu pour des échanges synchrones dans une seule session. Le cookie est donc transféré à chaque requête HTTP. Plus sa taille est réduite et plus vous économisez de bande passante. Dès que la présence d’un cookie n’est plus obligatoire, supprimez-le.