Comment bien optimizer un Drupal 7

Share and options

Après de longues heures de benchmarks divers et variés, j'ai enfin réussi à avoir obtenir une configuration optimum pour faire tourner un Drupal 7 correctement. Comme des bons exemples valent mieux que mille mots, voici en image comment faire :

Pour le benchmark, je vais utiliser la commande ab -n 100 -c 4 http://blog-d7.guinevere/

1. Site de base, sans APC, cache sur la base MySQL

Concurrency Level:      4
Time taken for tests:   9.183 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2437200 bytes
HTML transferred:       2399800 bytes
Requests per second:    10.89 [#/sec] (mean)
Time per request:       367.319 [ms] (mean)
Time per request:       91.830 [ms] (mean, across all concurrent requests)
Transfer rate:          259.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   328  366  22.5    360     496
Waiting:      312  343  21.3    337     465
Total:        328  366  22.5    360     497

Percentage of the requests served within a certain time (ms)
  50%    360
  66%    364
  75%    367
  80%    370
  90%    377
  95%    414
  98%    478
  99%    497
100%    497 (longest request)

2. Avec APC, cache sur la base MySQL

Concurrency Level:      4
Time taken for tests:   2.740 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2437200 bytes
HTML transferred:       2399800 bytes
Requests per second:    36.50 [#/sec] (mean)
Time per request:       109.601 [ms] (mean)
Time per request:       27.400 [ms] (mean, across all concurrent requests)
Transfer rate:          868.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    95  109  11.0    107     166
Waiting:       90  101  11.1     99     159
Total:         95  109  11.1    107     167

Percentage of the requests served within a certain time (ms)
  50%    107
  66%    108
  75%    109
  80%    109
  90%    112
  95%    134
  98%    167
  99%    167
100%    167 (longest request)

3. Avec APC, un peu tuné

Ces options-ci ont été surchargées :

  • apc.include_once_override : 1
  • apc.canonicalize : 1
Concurrency Level:      4
Time taken for tests:   2.713 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2437200 bytes
HTML transferred:       2399800 bytes
Requests per second:    36.86 [#/sec] (mean)
Time per request:       108.506 [ms] (mean)
Time per request:       27.126 [ms] (mean, across all concurrent requests)
Transfer rate:          877.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    80  107   8.8    105     151
Waiting:       75   99   8.5     97     141
Total:         80  107   8.9    105     152

Percentage of the requests served within a certain time (ms)
  50%    105
  66%    107
  75%    108
  80%    108
  90%    111
  95%    116
  98%    145
  99%    152
100%    152 (longest request)

4. Avec APC, sévèrement en mode production

Nous ajoutons l'option magique:

  • apc.stat : 0
Concurrency Level:      4
Time taken for tests:   2.648 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2437200 bytes
HTML transferred:       2399800 bytes
Requests per second:    37.77 [#/sec] (mean)
Time per request:       105.909 [ms] (mean)
Time per request:       26.477 [ms] (mean, across all concurrent requests)
Transfer rate:          898.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    81  104   7.0    103     136
Waiting:       76   97   6.9     95     129
Total:         81  104   7.0    103     136

Percentage of the requests served within a certain time (ms)
  50%    103
  66%    104
  75%    105
  80%    106
  90%    108
  95%    111
  98%    136
  99%    136
100%    136 (longest request)

5. Avec APC, sévèrement en mode production, et du cache!

Pour continuer, le test prochain ajoute quelques cache bin dans le cache APC, pas tous, uniquement les tables les plus critiques :

  • cache_boostrap
  • cache_menu
Concurrency Level:      4
Time taken for tests:   2.417 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      2437200 bytes
HTML transferred:       2399800 bytes
Requests per second:    41.37 [#/sec] (mean)
Time per request:       96.681 [ms] (mean)
Time per request:       24.170 [ms] (mean, across all concurrent requests)
Transfer rate:          984.71 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    90   96   8.0     94     137
Waiting:       83   88   7.8     87     130
Total:         90   96   8.0     95     137

Percentage of the requests served within a certain time (ms)
  50%     95
  66%     95
  75%     96
  80%     96
  90%    100
  95%    104
  98%    134
  99%    137
100%    137 (longest request)

Vous remarquerez que la différence avec les caches stoqués dans APC ne font aucune différence, ceci est dû à plusieurs facteurs :

  • Le serveur MySQL est local, sur la même machine qu'Apache, par conséquent PHP y accède par socket, ce qui rend le temps de connection et de requêtage aussi rapide que celui d'APC en local.
  • Ces caches sont réellement petits, la requête SQL qui remonte leur contenu a un overhead minimal (voir négligeable).
  • Le site à une très faible volumétrie de données, et peu d'accès concurrents sont effectués (4 seulement), ce n'est pas suffisant pour avoir un impact sur la base.

A terme, il est certain qu'en utilisant cette technique, des sites avec une plus forte volumétrie de donnée et dont le serveur MySQL (ou le disque dur sur lequel il est) soit plus lent, APC sera plus rapide, car il travaille exclusivement en RAM. MySQL dans notre cas maximise son query cache, dû à la faible volumétrie de données, mais sur un site en production un plus volumineux, il y aura de fortes chances que ce dernier soit purgée régulièrement (et donc beaucoup moins efficace), ce qui forcera MySQL à effecter des accès disque.

Ceci était une simple mise en bouche, à terme un article plus complet va venir.