Forum: Italian Ruby user group Performance driver pg e mysql2

7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2015-05-21 09:51
Ho dovuto portare uno script di seeding di database da PostgreSQL 9.4 a
MariaDB 10 (un cliente ha scelto così e con poco entusiasmo mi devo
adeguare) e questo ha dato il via ad una serie interessante di scoperte.
Una di queste riguarda i driver Ruby pg e mysql2.

A parte alcune amenità [1] [2] [3] [4] ho subito notato che lo script
con MariaDB girava 20 volte (venti) più lentamente che con PostgreSQL:
21 minuti contro 1 minuto e 3 secondi. Inutilizzabile.

Una differenza così grande non può essere dovuta al database, per cui mi
sono messo ad indagare la configurazione. Anche il MySQL della
distribuzione (Ubuntu 12.04) era ugualmente lento e mi posso aspettare
che quello sia configurato ragionevolmente bene. A questo punto il
sospetto diventa il driver.

Ho aperto questa issue https://github.com/brianmario/mysql2/issues/623 e
mi hanno dato due preziosi consigli: usare un profiler
(https://github.com/ruby-prof/ruby-prof) e la gemma activerecord-import
(https://github.com/zdennis/activerecord-import).

L'uso del profiler ha evidenziato che il driver pg usa prepared
statements, che alla scala del numero di record creati dal mio script
(poco più di 32 mila) danno dei vantaggi. mysql2 invece non usa prepared
statement e pari numero di chiamate al db questo pare faccia la
differenza (x20!).

Accorpare le chiamate usando activerecord-import (fa una sola insert per
tutto un array di oggetti) ha significato riscrivere lo script in modo
un po' innaturale, perché mi servivano gli id dei record creati per
passarli in quelli dei record associati, ma i tempi di esecuzione per
mysql2 sono scesi da 21 minuti a 1 minuto e 33". Ne è valsa la pena. Ci
sono solo 1045 chiamate al db e ciò nonostante è sempre più lento delle
32k chiamate fatte da pg, ma ora si ragiona di nuovo. Lo script con pg e
activerecord-import però è sceso a 47 secondi.

Pur con tutte le ottimizzazioni introdotte a activerecord-import le
chiamate a PostgreSQL del mio script sommano a 9.4 secondi contro i 49.8
delle chiamate a MariaDB (Ruby aggiunge una quarantina di secondi,
indipendentemente dal database usato).

Riassumendo:

1) Con Ruby ci sono dei vantaggi di performance a lavorare su
PostgreSQL.

2) Pare che la versione 0.4.0 di mysql2 avrà i prepared statement e
quando sarà rilasciata vedrò di ricordarmi di rifare i test senza
activerecord-import

3) Per i dettagli (tempi di profiling, etc) leggete
https://github.com/brianmario/mysql2/issues/623.



Le "amenità":

[1]
https://mariadb.com/kb/en/mariadb/installing-maria...

[2] Per MariaDB installare la gemma mysql2 dopo aver dato bundle config
build.mysql2 --with-mysql-config=/path/to/mariadb/bin/mysql_config
(occhio che questa è globale, date un
--with-mysql-config=/usr/bin/mysql_config per quando vi servirà per
mysql)

[3] MySQL e MariaDB non hanno il TRUNCATE CASCADE, quindi
  connection = ActiveRecord::Base.connection
  connection.execute("SET FOREIGN_KEY_CHECKS = 0;")
  [ tutti i modelli ].each do |model|
    connection.execute("TRUNCATE #{model.table_name}")
  end
  connection.execute("SET FOREIGN_KEY_CHECKS = 1;")

[4] Ma neppure ActiveRecord ha il TRUNCATE, quindi o usate gemme che lo
aggiungono o anche per PostgreSQL vi serve un loop come quello, con
connection.execute("TRUNCATE #{model.table_name} CASCADE") e senza le
set foreign_key_checks.


Paolo
9daa9b4739a6e95078cbcfb624d7bb8e?d=identicon&s=25 David Welton (Guest)
on 2015-05-22 11:54
(Received via mailing list)
> 1) Con Ruby ci sono dei vantaggi di performance a lavorare su
> PostgreSQL.

Ottimo post!

Aggiungerei al performance la cosa pi critica: Postgres tende ad
essere "corretto".  Per esempio,  transazionale con la DDL: metti
CREATE TABLE, UPDATE, ecc.... dentro una transaction e o esegue tutto,
o niente.  Quando avevo scoperto che Mysql con InnoDB non faceva
questo, sono rimasto male.

--
David N. Welton

http://www.welton.it/davidw/

http://www.dedasys.com/
Cb8e3a1650513848561ca38f84399fa1?d=identicon&s=25 Fabrizio Regini (freegenie)
on 2015-05-22 12:32
(Received via mailing list)
Complimenti per la ricerca, molto utile.

2015-05-21 9:51 GMT+02:00 Paolo Montrasio <paolo@paolomontrasio.com>:
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2015-05-22 18:51
@David io sono rimasto un po' male ieri quando ho scoperto che le stored
procedure di MySQL ritornano record solo se finiscono con una SELECT,
altrimenti si deve creare una temporary table (engine=memory altrimenti
ci mette tanto quanto un emanuense, non come le table normali), usare un
cursor per scandire la select, fare una insert into la tabella
temporanea e poi fare una select * da quest'ultima. Due p...e.

Comunque, per completare il mio post iniziale, è andata a finire così.

Qualche ora dopo il post ho scoperto di non aver accorpato tutte le
INSERT con la activerecord-import. Ottimizzando meglio, i tempi sono
scesi a 46, 47, 53 secondi (PostgreSQL, MySQL, MariaDB). Per questo
scenario il problema è risolto, al prezzo di una complicazione nel
codice dello script di seeding.

In generale resta il maggior tempo speso da mysql2 nel codice che
comunica con il database. Non tutto si può accorpare. Ad esempio le
INSERT in un normale workflow da web server sono tutte separate tra di
loro e qui dovrebbe farsi sentire il vantaggio di performance del driver
pg. Andrebbero però fatti benchmark. Fino ad allora i miei rimarranno
solo ragionevoli sospetti.

Di nuovo, per una volta questo non sembra colpa di MySQL ma tant'è.

Poiché il destino raggruppa sempre eventi simili, cercate pmontra in
https://news.ycombinator.com/item?id=9586504 e troverete qualche
considerazione in più sulla vicenda (ieri era partita una thread MySQL
pro e contro su HN).


Paolo
656a424090d82cf108c754be9e07d5b0?d=identicon&s=25 Giorgio Robino (solyaris)
on 2015-09-21 20:42
> Poiché il destino raggruppa sempre eventi simili,

dott. Sartorius, ora credi nella sincronicità (Carl Gustav Jung) ?! ;-)
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2015-09-22 17:41
44cd5b1856e1bbf7db3a295b7c8ab00a?d=identicon&s=25 Akash Khan (akash64)
on 2015-10-12 09:56
Gli Cappa a flusso laminare[/url] sono utilizzati nelle più svariate
realtà, dalle
... dalla progettazione fino al montaggio e collaudo sono innumerevoli
tra cui cappe ...

http://pluriserviceitalia.it/cappa-a-flusso-laminare/
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2015-12-15 13:02
Approfittando del lavoro descritto poco fa in
https://www.ruby-forum.com/topic/6877377 ho fatto qualche prova di
performance con mysql2 e i suoi nuovi prepared statement.

Premesso che forse sto comparando mele con arance, perché ho un
PostgreSQL moderno e un MySQL vecchiotto, ecco i risultati. Ho messo
dentro anche MongoDB perché era parte del progetto su cui lavoravo.

Dieci run dello script a https://github.com/pmontrasio/codici-stati

PostgreSQL, MySQL, MongoDB
0.033463352, 0.033122892, 0.227229571
0.041719425, 0.041613518, 0.235716736
0.016899566, 0.058329496, 0.2415223
0.033444892, 0.04158105, 0.23954115
0.033343821, 0.041789415, 0.234358035
0.025210378, 0.100504027, 0.22563008
0.025150277, 0.058183244, 0.237807435
0.033553869, 0.041665126, 0.242589368
0.016842638, 0.033267797, 0.190134758
0.016789275, 0.049795993, 0.249314104

Medie:

0.0276417493, 0.0499852558, 0.2323843537

Attenti a notare gli zeri subito dopo la virgola: gli ordini di
grandezza non sono gli stessi ma nel caso di MongoDB ho usato mongoid
senza accedere direttamente al driver. Che sia più lento ci sta.

Versioni:

PostgreSQL 9.4.5, pg-0.18.4
MySQL 5.5.46, mysql2-0.4.2
MongoDB 2.6.11, mongo-2.2.0 e mongoid-5.0.1
This topic is locked and can not be replied to.