Guida su come ottimizzare WordPress per i blog ad alto traffico

WordPress è una piattaforma ottima per quel che riguarda la gestione e la pubblicazione dei post. Si adatta a tutto in vitù anche del fatto che esiste una comunità di sviluppatori enorme, chi sviluppa plugins e chi i temi. Purtroppo, nonostante la sua enorme diffusione, rimane un grosso problema di fondo, è un divoratore di risorse, e di fatto quando un blog creato in questa piattaforma supera i 100 utenti contemporanei cominciano i primi dolori e i primi problemi sono relativi a MySql.

La prima cosa da fare per evitare una crisi di MySql è quella di implementare un po’ di page caching così diminuisce il numero di volte il motore di WP vada ad accedere ai dati reali. Così il vostro server non deve elaborare pagine php e mysql non deve interrogare il DB. Non si tratta di avere dati sempre vecchi ma si può raggiungere un ragionevole compromesso tra offline ed online. Per fortuna Riccardo Galli ha pensato e sviluppato un plugin che ci evita la fatica di andare a perdere ore ed ore nella configurazione di apache, stiamo parlando del plugin WP-Cache, si istalla come un normalissimo plugin ed ha alcuni parametri di configurazione nella sezione Options.

Una volta installata questa plugin bisogna attivarla ed andando nella pagina di configurazione andiamo a cambiare 1 parametro soltanto: Expire time (in seconds) lo impostiamo a 240 secondi (4 minuti) così che le nostre pagine non siamo nè troppo vecchie nè create all’istante. Possiamo monitorare l’andamento della cache con i 2 parametri che compaiono in fondo alla pagina di configurazione. Solitamente per un sito con 200-300 utenti contemporanei (parliamo di un sito molto esteso) le pagine in cache si attestano attorno alle 400-500, dipende molto da quante persone richiedono la stessa pagina.

Già solo con quest’operazione si abbattono i consumi di CPU del 20-30% ma a volte non bastano, dobbiamo scendere di un paio di livelli per ottimizzare al meglio le prestazioni del nostro database. Un database è buono quando è progettato bene! Non posso dire che il DB di WordPress sia ottimo, ma con alcuni accorgimenti si può migliorare senza andare a toccare il codice sorgente di WP e per fortuna esistono molti plugin che ci aiutano a mantenere “pulito” il nostro DB.

Ottimizzare il DB per tutte quelle tabelle che vanno in “OverHead” (in eccedenza) è importante perchè in questo modo il nostro database non cresce a dismisura, soprattutto per quei blog che hanno molti post e molti commenti, attenzione che però l’operazione di ottimizzazione alle volta fa crashare il DB, conviene sempre fare prima un Backup. Esiste un plugin che fa solo l’ottimizzazione del DB si chiama OptimizeDB analizza e con un singolo tasto “optimize now” fa tutta l’operazione per voi. Per i più smanettoni ed esperti di Mysql c’è il plugin Wp-phpmyadmin che vi mette a disposizione tutta la potenza di PhpMyAdmin.

Nel caso di Geekissimo purtroppo tutte queste operazioni non sono bastate, di fatto la RAM occupata è rimasta alta , 3,5 Gb occupati su 4 Gb e le CPu erano largamente occupate dal processo di MySql, che ad ogni singolo accesso prendeva un 0,9% della CPU toccando così punte del 270%-300%, ottenendo un rallentamento generale. A questo punto l’unica cosa da fare è quella di metter mano al Database, prima a livello di creazione di indici (WP ne è esente) e poi a livello di database lato server. Armiamoci di razionalità e pazienza.


Creazione di indici aggiuntivi:
Il primo indice che manca a tutti gli effetti è sulla tabella options che viene utilizzata per trovare tutti i record che hanno autoload.
perciò andiamo a creare l’indice in questione con questa query SQL:

create index idx_options_autoload on wp_options (autoload);

Un altro indice da creare quello relativo alle categorie, di fatto ne esiste uno sul campo category_nicename, siccome male non fa, noi ne creiamo uno su category_name

create unique index idx_cat_name on wp_categories (cat_name);

Ora veniamo all’indice più importante, quello dei posts, infatti per ogni pagina viene interrogata la data del post per vedere se è visualizzabile o meno.
Per facilitare il compito a MySql basta creare 2 indici, uno sul campo date ed uno sul campo date_gmt:

create index idx_post_date_gmt on wp_posts (post_date_gmt);
create index idx_post_date on wp_posts (post_date);

Con gli indici abbiamo finito, a questo punto MySql ha cambiato atteggiamento, è diventato un po’ più agile, ma ancora una volta a geekissimo non basta! Resta di fatto molto importante che MySql mantenga una dimensione accettabile e che non utilizzi mai la partizione di swap, in caso contrario dobbiamo inevitabilmente metter mano alla configurazione del server, perciò dobbiamo limitare l’utilizzo di ram.

Dobbiamo scendere a livello server e impostare alcuni parametri nel file my.cnf che solitamente si trova in /etc/my.cnf. Questo file configura le variabili di ambiente del nostro database, dobbiamo fare in modo che accetti molte connessioni contemporanee e che faccia query solo quando una query non è già stata fatta (qcache). Il numero massimo di connessioni contemporanee su MySql di default (se non specificato) è impostato a 100, andiamo ad aumentarlo, impostiamolo a 500.

Alcuni altri parametri tipo i thread vanno misurati e configurati a seconda del server, dell’hardware a disposizione e del numero di accessi simultanei per il blog in questione.
Dopo un po’ di prove di performance e di stress ho raggiunto questa configurazione che va bene un po’ per tutti quei server che hanno intenso traffico:

[mysqld]
safe-show-database
skip-innodb
max_connections = 500
key_buffer = 32M
myisam_sort_buffer_size = 64M
join_buffer_size = 1M
read_buffer_size = 1M
sort_buffer_size = 2M
table_cache = 1800
thread_cache_size = 384
wait_timeout = 35
connect_timeout = 10
tmp_table_size = 64M
max_heap_table_size = 64M
max_allowed_packet = 64M
max_connect_errors = 10
thread_concurrency = 2
read_rnd_buffer_size = 524288
bulk_insert_buffer_size = 8M
query_cache_limit = 3M
query_cache_size = 64M
query_cache_type = 1
query_prealloc_size = 163840
query_alloc_block_size = 32768
default-storage-engine = MyISAM

[myisamchk]
key_buffer = 64M
sort_buffer = 64M
read_buffer = 16M
write_buffer = 16M

Rinominiamo quindi il file my.cnf in my.cnf.old e creiamone uno nuovo con la configurazione di cui sopra. A questo punto non ci resta altro che riavviare il server mysql con un /etc/init.d/mysql restart (nel caso di geekissimo) e goderci lo spettacolo! Ora geekissimo viaggia con 1.7 Gb occupati su 4 totali mentre l’utilizzo della CPU si attesta tra il 4% ed il 150% in momenti di intenso traffico.

Per maggiori informazioni su quali parametri utilizzare per ottimizzare MySql seguite la guida ufficiale di MySql. Resta da ottimizzare Apache ma questo spero lo faremo un altro giorno!

Ringrazio di cuore K76 per avermi aiutato a risolvere i problemi che ormai affliggevano Geekissimo da quasi una settimana. Grazie a lui anche per questa splendida guida originale scritta e messa in pratica apposta per Geekissimo. Grazie ancora!