24 Oct
matte

matte il 24 October 2006 parla di Rails Snippet, Tutorial

Ottimizzazione MySQL con il Plugin Query Analyzer

Durante la fase finale dello sviluppo di una applicazione web può essere utile controllare se il database è ottimizzato per le richieste più comuni. Sempre stando attenti a non abusare dei facili collegamenti al database con il Rails è possibile ottimizzare la ricerca dei record aggiungendo degli indici alle tabelle dove sono necessari. Il database supportato è, come si deduce dal nome del plugin, MySQL.

Il plugin Query Analyzer che vi illustrerò serve proprio ad esaminare le query dell’applicazione per cercare di ottimizzare gli indici delle tabelle.

Come installare il plugin

E’ necessario posizionarsi nella directory della propria applicazione e lanciare da shell il seguente comando:

    1 $ script/plugin install http://svn.nfectio.us/plugins/query_analyzer

Ricordatevi di riavviare il vostro web server altrimenti il plugin non entrerà in azione.

Come utilizzare il plugin

Aprire la pagina della vostra applicazione che volete esaminare e visualizzate il development.log. All’interno troverete sotto ad ogni query (che veniva eseguita anche precedentemente) un report che analizza la query stessa. Non è altro che l’esecuzione della query MySQL della vostra applicazione preceduta dal comando EXPLAIN, il tutto formattato per agevolare la lettura del report.

Supponiamo di eseguire la query

    1 SELECT p.* FROM p INNER JOIN d ON p.id = d.p_id WHERE (d.p_id = 2 AND ((d.type = 'P')))

Otteniamo all’interno del development.log:

    1 P Load (0.008669)  SELECT p.* FROM p INNER JOIN d ON p.id = d.p_id WHERE (d.p_id = 2 AND ((d.type = 'P'))) 
    2 
    3 Analyzing P Load
    4 select_type | key_len | type   | Extra       | id | possible_keys | rows | table  | ref    | key    
    5 ----------------------------------------------------------------------------------------------------
    6 SIMPLE      |         | ALL    | Using where | 1  |               | 74   |    d   |        |        
    7 SIMPLE      | 4       | eq_ref | Using where | 1  | PRIMARY       | 1    |    p   | d.p_id | PRIMARY

Dal report è possibile vedere come per effettuare per ottenere i risultati dal database viene fatto un accesso diretto alla tabella p, mentre 74 righe vengono esaminate nella tabella d. Questo si ha perché non è stato impostato un indice che faciliti la ricerca delle condizioni in WHERE della query. Si vede quindi come il database prima cerchi su tutte le righe della tabella d (infatti type=ALL) e poi utilizzi il risultato per effettuare la JOIN definitiva (type=eq_ref) in cui si vede che viene fatto accesso ad una sola riga.

Come ottimizzare il database

A questo punto non resta che cercare di ottimizzare la situazione. Il Rails ci aiuta consentendoci di aggiungere gli indici alle tabelle utilizzando le migrazioni. Creiamo quindi una nuova migrazione eseguendo da shell:

    1 script/generate Migration AddIndexesToDTable

Aggiungere al file generato (che si chiamerà xxx_add_indexes_to_d_table.rb, dove xxx indica il numero della migrazione):

    1 class AddIndexesToDTable < ActiveRecord::Migration
    2   def self.up
    3     add_index :d, [ :p_id, :type ]
    4     add_index :d, :type
    5   end
    6 
    7   def self.remove
    8     remove_index :d, [ :p_id, :type ]
    9     remove_index :d, :type  
   10   end
   11 end

A questo punto dopo aver eseguitoda shell:

    1 $ rake migrate 

tornando a riesaminare il file di log possiamo osservare i cambiamenti:

    1 P Load (0.009011) SELECT p.* FROM p INNER JOIN d ON p.id = d.p_id WHERE (d.p_id = 2 AND ((d.type = 'P'))) 
    2 
    3 Analyzing P Load
    4 select_type | key_len | type   | Extra       | id | possible_keys                  | rows | table | ref    | key                             
    5 ------------------------------------------------------------------------------------------------------------------------------
    6 SIMPLE      | 255     | ref    | Using where | 1  | d_p_id_type_index,d_type_index | 1    |   d   | const  | d_p_id_type_index
    7 SIMPLE      | 4       | eq_ref | Using where | 1  | PRIMARY                        | 1    |   p   | d.p_id | PRIMARY

Adesso MySQL ha un indice che corrisponde alla condizione del WHERE e si può vedere come esamini una sola riga anche della tabella d senza effettuare la scansione dell’intera tabella.

Conclusioni

Durante lo sviluppo è bene tenere presente l’ottimizzazione degli indici delle tabelle del database. Questo soprattutto quando il numero di record all’interno della tabella diventa molto grande. Infatti tutto va bene fino a quando ci sono centinaia o migliaia di record in una tabella, ma nel momento in cui i dati della vostra applicazione aumentano e i record diventano decine o centinaia di migliaia vedrete rallentare l’applicazione in modo inaspettato.

Vi consiglio inoltre di controllare gli accessi alle tabelle di modelli che utilizzano ereditarietà (spesso viene fatta un accesso del tipo SELECT * FROM p WHERE p.type = ‘model_type’) o che hanno :polymorphic => true (in questo caso sono fatti accessi del tipo SELECT * FROM p WHERE p.polymorphic_type = ‘model_type’ and p.polymorphic_id = ‘model_id’). Consiglio quindi di creare nel primo caso un indice per il campo type, mentre un indice a chiave multipla [:polymorphic_id, :polymorphic_type] nel secondo caso.

Scrivi un commento