Ho appena effettuato il backport delle patch per le prestazioni su Windows da 5.5 a MariaDB 5.3. In MariaDB ce ne sono più che in MySQL 5.5, ma su questo punto tornerò più tardi.

Prima di tutto, ritengo che i miglioramenti alle performance su Windows in 5.5 non sono mai stati descritti adeguatamente, quindi ecco un riassunto. Per coloro che hanno familiarità con la programmazione su Windows, il codice di MySQL aveva dei problemi di prestazioni. Ne ho risolti alcuni quando lavoravo a MySQL/Sun. Il benchmark dei risultati mostra una curva interessante: si veda il post nel blog di Calvin.

Se i grafi di questo blog vi sono familiari, è perché sono stati spesso usati dal marketing di Oracle come prova dell'influenza positiva di "big O" su MySQL :)

Ci sono state tre patch per le performance su Windows. Farò anche qualche commento sulla storia dei bug.

  • Bug#24509. Il fix ha eliminato il limite di 2048 file aperti per MyISAM e come gradito effetto collaterale permette di impostare una cache per le tabelle molto più grande. All'avvio di mysqld legge il massimo di file che possono essere aperti, e corregge il valore della cache delle tabelle, se max_open_files è basso o se max_connection è alto. Questo è ciò che è accaduto anche durante i benchmark. Se si osserva il grafo dei benchmark a sola lettura nel post di Calvin sopracitato, si nota un angolo intorno ai 64 utenti concorrenti. Nessuna meraviglia: il server mysql ha ricalcolato le dimensioni della cache delle tabelle e l'ha impostata al minimo assoluto, cioè 64.

Con il fix è stata creata una sorta di libreria C runtime sopra il puro Win32, che è in grado di gestire più di 2048 file aperti (16K è il default). Anche altri aspetti sono stati migliorati rispetto al runtime di Microsoft C, per esempio non ci sono lock e vi è un'implementazione accittabile di pread()/pwrite(). Il vantaggio principale, come ho detto, è il poter disporre di una cache delle tabelle più grandi - riscrivere il runtime C è probabilmente una strage, ma non sono riuscito a trovare niente di meglio.

  • Bug#52102. Con questo bug sono stati risolti molti punti discutibili di InnoDB, probabilmente scritti ai tempi di NT 3.1.

Prima di tutto occorre capire come viene acquisita la struttura "mutex" da InnoDB. I dettagli sono complessi, il mutex è una struttura complessa che contiene un vero mutex del sistema operativo (che sotto Windows si chiama CRITICAL_SECTION) più un evento InnoDB (che sotto Windows si chiama evento). Ci sono stati un paio di cambiamenti nell'implementazione - il mutex può essere una variabile atomica (chiamata così per i miei amici Unix), sotto Unix un evento è rappresentato come variabile di condizione.

L'acquisizione viene effettuata in due passi - prima un trylock su un mutex di sistema, possibilmente più volte in un ciclo se non ha successo; l'evento viene riservato in una tabella globale di eventi chiamata "sync array"; l'evento entra in stato di attesa. Lo sblocco del mutex sveglia quelli che sono in attesa, se ce ne sono. Non chiedetemi perché l'implementazione sia così complicata: è così :) Forse, questa struttura aiuta a trovare i deadlock.

Variazione di questa implementazione - invece di fare un trylock su mutex, ora c'è un'istruzione compare_exchange sulle variabili atomiche.

Tornando a Windows, l'implementazione sopra spiegata espone un paio di interessanti bug che si compensano a vicenda.

  1. Prima ho corretto os_mutex_trylock() perché faccia davvero quello che dice. L'implementazione era EnterCriticalSection, cioè "riprovaci tante volte", ed effettivamente acquisiva il lock. Un trylock più coscienzioso è del tipo TryEnterCriticalSection. Quando l'ho aggiustato, contrariamente dalle mie aspettative, ha rallentato molto mysqld. Quando trylock() falliva, InnoDB entrava in una parte del codice che non aveva mai visto prima. Per esempio, riservando spazio nel suddetto "sync array". L'accesso a sync array è protetto dal cosiddetto "slow lock" e questo appariva molto spesso nel profiler. Il passo successivo è stato sistemare lo "slow lock"
  2. "slow Innodb mutex" è stato implementato come oggetto kernel, aka Windows mutex (per i miei amici Unix è una specie di semaforo di SysV). Può essere usato per sincronizzare i processi, ma per la sincronizzazione dei thread in un medesimo processo è una tortura. Era un "really slow mutex". E' diventato molto più veloce modificandolo in CRITICAL_SECTION...
  3. Una volta fatto tutto questo, ho capito che gli eventi di Windows non scalavano bene negli scenari con molti thread. Sui Windows più recenti (Vista+), ci sono le CONDITION_VARIABLE che secondo la documentazione scalano meglio, e in effetti secondo le mie misurazioni funzionano molto bene. Così ho utilizzato le variabili di condizione quando ho potuto, il che è ironico, perché gli eventi di InnoDB erano realmente modellati come gli eventi di Windows.
  4. Ho riabilitato l'implementazione dei mutex veloci come variabili atomiche. Prima della patch, i flag del precompilatore relativi alle variabili atomiche erano commentati, con un "Windows atomics do not work well" in CMakeLists.txt. Grande commento, dato che diversamente dagli sviluppatori software, le istruzioni atomiche non hanno preferenze per un particolare OS :)

Quindi "atomics did not work well on Windows" era l'effetto cumulativo di diversi fattori.

Prima della patch. Una volta abilitate le atomiche, l'implementazione dei mutex veloci non usava CRITICAL_SECTION, ma l'istruzione compare_exchange. L'ingegnoso "prova tante volte" che abbiamo visto nel punto 1 non viene più utilizzato, ed è sostituito da un corretto lock "prova" . Quando try_lock() iniziava a fallire con molti thread concorrenti, il ritardo dovuto a sync array implementato come oggetto kernel di Windows, visto al punto 2, è divenuto chiaro e gli eventi di Windows assolutamente inefficienti visti al punto 3 completavano il quadretto.

Questa patch serve semplicemente a compensare gli effetti negativi dei lock sui metadati di 5.5 visti nei benchmark di MyISAM. Il fix utilizza i primitivi delle performance nativi di Vista. La patch di per se non è interessante e riproduce molto di ciò che è stato fatto per InnoDB. Quel che c'è di grande è una discussione precedente alla patch tra me, Davi e Dmitry, sulle differenti implementazioni dei lock in lettura e in scrittura, tra cui due fatte in casa e una di Vance Morrison.

Senza dubbio, le discussioni sono state illuminanti nel breve periodo in cui ho lavorato a Oracle. Inoltre, se si vuole avere una revisione del codice MySQL in stile classico con 17 cose da sistemare, delle quali almeno 10 verrebbero segnate come "Coding Style" (sì, entrambe le parole, con le maiuscole), si provi ad avere Dmitry Lenev come revisore, è grande - eccone la prova http://lists.mysql.org/commits/118295 Comunque, la patch migliora le prestazioni di MyISAM del 10-20%, e penso che vada abbastanza bene. In qualche modo però queste percentuali sono state poi rosicchiate da MDL :)

Note

Preso da una nota su Facebook: https://www.facebook.com/note.php?note_id=238505812835782 di Vladislav Vaintroub.

Commenti

Sto caricando i commenti......