Dans la continuité de MySQL Connect, les speakers nous ont offert des sessions de très grandes qualités sur les thèmes de la performance et de l’optimisation relative aux versions 5.6 et 5.7 de MySQL.
A noter que c’est la 1ère fois en France (à ma connaissance) que les 3 grandes tendances de MySQL étaient représentées à un événement, à savoir SkySQL/MariaDB, Percona et bien sûr Oracle.
Prochain pari, des confs avec ces 3 tendances…
Le 10 octobre 2013, Oracle et Le MySQL User Group Francophone (lemug.fr) vous invitent au MySQL Tech Day à Paris.
Une journée technique bien pleine où les différentes équipes MySQL chez Oracle vont nous présenter les améliorations présentes et futures des versions 5.6 et 5.7 de MySQL.
Au programme:
Overview: MySQL Innovation @Oracle
MySQL Performance: Latest improvements in MySQL 5.6 & 5.7
MySQL Optimizer: What is new, what is coming
50 tips to boost MySQL Performance
MySQL Performance Schema: Overview / HOWTO
MySQL Performance Schema: Hands-on (live lab)
Bref du lourd, du très lourd !!!
Last but not least, c’est gratuit, mais les places sont limités…
Inscription et renseignements, veuillez contacter Alexis Bensa:
We’re happy to announce you that MySQL Tech Day will take place in Paris on Oct 10, 2013 in Oracle main office. It’ll be a full day event giving you an occasion to listen directly from Oracle developers about most of the improvements made recently within MySQL 5.6 and 5.7 development.
The agenda is the following :
Overview: MySQL Innovation @Oracle
MySQL Performance: Latest improvements in MySQL 5.6 & 5.7
MySQL Optimizer: What is new, what is coming
50 tips to boost MySQL Performance
MySQL Performance Schema: Overview / HOWTO
MySQL Performance Schema: Hands-on (live lab)
Don’t miss such an opportunity to get most of the hot news about MySQL improvement directly from the source! It’ll be just two weeks after MySQL Connect, so there will be really a lot of news! So, book this date in your agenda right now and then don’t forget to register yourself for the event – attending is totally free, but places are limited, so registration is mandatory to secure enough seats.
L’association LeMug.fr (MySQL User Group Francophone) fut l’invité à l’émission Cyberculture de la radio Ici et Maintenant samedi 6 juillet pour parler des bases de données webs et principalement du sgbdr le plus utilisée sur internet: MySQL.
L’idée ici est de répondre aux interrogations de svar et d’en profiter pour explorer les nouvelles possibilités de la variante stable de MySQL qui possède l’optimiseur le plus avancé, c’est à dire MariaDB 5.5.
La valeur par défaut sur MariaDB 5.5.30 de la variable optimizer_switch est:
mariadb5.5> SHOW VARIABLES LIKE ‘optimizer_switch’\G*************************** 1. row ***************************Variable_name: optimizer_switch Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=off
Le truc super lisible en fait !
Mais comme je suis un mec super sympa, j’ai mis en gras les directives qui vont nous intéresser dans cet article.
Les patterns des tests
Pour atteindre les objectifs fixés, j’ai effectué 2 niveaux de tests. Le premier niveau va me fournir des données “objectives”. Il consiste à exécuter la requête avec différentes directives d’optimisation et d’analyser les “status” ainsi que les plans d’exécution associés.
Niveau stats
SET optimizer_switch=’default’ : réinit des directives d’optimisation
SET optimizer_switch=’valeur adéquate’ : choix des directives d’optimisation
SHOW WARNINGS : pour savoir comment l’optimiseur à réécrit la requête
FLUSH STATUS : réinit des stats à 0
exécution de la requête
SHOW STATUS : pour récupérer les variables d’états, Handler* & Created_tmp
Avec le second niveau de tests, je vais récupérer une info “subjective”, le temps. Cela consiste à récupérer la durée minimale d’exécution de la requête avec les différentes directives d’optimisations à l’aide de l’outil mysqlslap.
Niveau temps
SET GLOBAL optimizer_switch=’default’
SET GLOBAL optimizer_switch=’valeur adéquate’
mysqlslap -c1 -i200 -q »la requête« : requête unitaire jouée plusieurs fois ie buffers chaud
A noter que le nombre d’itération (200) à été réduit à 5 lorsque la requête dure de l’ordre de la 10aine de minutes (bah oui, j’ai une vie aussi).
SELECT DISTINCT OtherID
FROM MyTable
WHERE Status = 1
AND Stage = 4
AND Invisible = 0
AND MainID IN
( SELECT MainID FROM MyTable WHERE OtherID = 2779262 AND Stage = 0 AND Invisible = 0 )
+———-+
| OtherID |
+———-+
…
7 rows in set (44 min 56.66 sec) <= A froid
L’idée ici n’est pas de comparer le temps d’exécution de la requête sur MariaDB par rapport à Percona server (les serveurs et les paramètres sont différents), mais bien de voir et de comprendre les (nouveaux) choix de l’optimiseur de MariaDB 5.5.
Traditional behavior (IN to EXISTS strategy)
TEST 1
Traditionnellement l’optimiseur converti la sous-requête IN en EXIST. Pour simuler ce comportement il faut désactiver la matérialisation et la semi-jointure.
SET optimizer_switch=’materialization=off,semijoin=off’;
Intuitivement on peut penser que la requête interne est exécutée en premier, puis qu’elle génère une liste d’ID (MainID) qui est ensuite passée dans la clause IN, du genre :
… MainID IN (123, 456, 789).
Eh ben non !
Comme convenu, le QEP est semblable à celui utilisé avec MySQL / percona server 5.5.
En fait, l’optimiseur transforme le clause IN en EXISTS. La table externe est lue la première sur environ 50% de ses lignes (rows: 153455020), ce qui dans notre cas fait beaucoup et est jointe avec la table interne.
La sous-requête dépend alors des données de la requête externe (select_type: DEPENDENT SUBQUERY).
La requête réécrite par l’optimiseur ressemble alors à peu près à :
SELECT DISTINCT OtherID
FROM MyTable AS MyExtTable
WHERE Status = 1
AND Stage = 4
AND Invisible = 0
AND EXISTS
(
SELECT 1
FROM MyTable AS MyInnTable
WHERE OtherID = 2779262
AND Stage = 0
AND Invisible = 0
AND MyExtTable.MainID = MyInnTable.MainID
)
Optimizer rewriting
select distinct OtherID AS OtherID from MyTablewhere ((Status = 1) and (Stage = 4) and (Invisible = 0) and <expr_cache><MainID>(<in_optimizer>(MainID,<exists>(<index_lookup>(<cache>(MainID) in MyTable on MainIDOtherIDStatuswhere (((OtherID = 2779262) and (Stage = 0) and (Invisible = 0)) and (<cache>(MainID) = MainID) and (OtherID = 2779262) and (Stage = 0) and (Invisible = 0)))))))
Confirmation que l’on a bien une transformation IN to EXISTS.
Beaucoup de lecture dans l’index (Handler_read_key | 44655686). Le problème ici est le nombre élevé de lectures transverse de l’index (Handler_read_next | 153049748) qui est très coûteux, surtout si les IO sont sur disque, et c’est encore pire s’ils sont aléatoires ie random IO disk.
Contrairement à MySQL 5.5 où il n’y a qu’une seule table temporaire créée, ici il y en a 2 (Created_tmp_table | 2), l’une pour le dédoublonnage lié à la clause DISTINCT et l’autre… ben je ne sais pas :/
En profilant la requête avec la commande SHOW PROFILE, il y est bien noté que 2 tables temporaires sont supprimées, mais dans le même temps, une seule est créée…
Si quelqu’un à une explication, je suis preneur.
Bench
daz@sql:~/mariadb5.5.30$ mysqlslap –create-schema=test -c1 -i5 -q »SELECT DISTINCT OtherID FROM MyTable WHERE Status = 1 AND Stage = 4 AND Invisible = 0 AND MainID IN ( SELECT MainID FROM MyTable WHERE OtherID = 2779262 AND Stage = 0 AND Invisible = 0 ); »
Benchmark
Minimum number of seconds to run all queries: 550.444 seconds
Les meilleures performances de cette requête, dans ces conditions, sont de l’ordre de la 10aine de minutes, ce qui est rarement acceptable en OLTP.
Pour simuler l’”intelligence” de l’optimiseur de MySQL / Percona Server 5.5, il a fallu désactiver 2 directives d’optimisation (la matérialisation et la semi-jointure). Voyons ce qu’il en est en ne désactivant qu’une seule d’entre elles, à tour de rôle.
MATERIALIZATION strategy (disable semi-join)
TEST 2
Pour désactiver la semi-jointure, il suffit de mettre la directive suivante à OFF:
SET optimizer_switch=’semijoin=off’;
La stratégie d’optimisation utilisée par l’optimiseur est alors la matérialisation. En 2 mots, le résultat de la sous-requête est stocké dans une table temporaire qui contient un index unique. Cet index sert à dédoublonner les valeurs insérée et à récupérer les infos de manière performante (comme un index classique), qui vont alimenter la requête externe.
Là encore, selon le QEP la table externe est lue la première sur environ 50% de ses lignes (rows: 153455020). Ce qui change là, c’est que la stratégie d’optimisation retenue est la matérialisation.
Autres différences avec le QEP originel, un covering index (Extra: Using where; Using index) sur les 235 (une estimation) enregistrements (rows: 235) de la table matérialisée.
Optimizer rewriting
select distinct OtherID AS OtherID from MyTablewhere ((Status = 1) and (Stage = 4) and (Invisible = 0) and <expr_cache><MainID>(<in_optimizer>(MainID,MainID in( <materialize> (select MainID from MyTablewhere ((OtherID = 2779262) and (Stage = 0) and (Invisible = 0)) ), <primary_index_lookup>(MainID in <temporary table> on distinct_key where ((MainID = <subquery2>.MainID)))))))
Confirmation que l’on à bien la matérialisation de la sous-requête sous la forme d’une table temporaire indexée(<primary_index_lookup>MainID in <temporary table> on distinct_key).
A noter que l’index à également servi au dédoublonnage.
Niveau table temporaires, on a 1 table temporaire pour le DISTINCT + 1 table pour la matérialisation + 1 table temporaire mystère (Created_tmp_tables | 3).
Avec une table temporaire supplémentaire, il y a alors plus d’écriture (Handler_tmp_write | 442).
442 (Handler_tmp_write) – 235 (MATERIALIZED rows) = 207 (à rapporter au 206 du Handler_tmp_write du QEP originel)
A l’aide de toutes ces informations, lequel des 2 modes est le plus performant ?
D’un point de vu status, il y a 1 table temporaire de plus pour T2, mais les tables étant créées entièrement en mémoire, le coût reste donc faible. De plus le nombre de Handler_read_next est sensiblement égale. Même s’il n’est pas réparti de la même manière.
Bref, niveau status, exaequo entre T1 & T2
Avec explain, le nombre de rows de T2 est 235 fois plus important que pour T1. Cependant, T2 fournit une matérialisation et index covering index alors qu’avec T1 on est dans de la sous-requête corrélée !
Bref niveau explain, c’est pas clair non plus 😀 mais la matérialisation se faisant en mémoire, elle devrait être moins coûteuse.
Bench
daz@sql:~/mariadb5.5.30$ mysqlslap –create-schema=test -c1 -i5 -q »SELECT DISTINCT OtherID FROM MyTable WHERE Status = 1 AND Stage = 4 AND Invisible = 0 AND MainID IN ( SELECT MainID FROM MyTable WHERE OtherID = 2779262 AND Stage = 0 AND Invisible = 0 ); »Benchmark Minimum number of seconds to run all queries: 430.712 seconds
T2 s’exécute avec 2 minutes de moins que T1.
SEMIJOIN strategy (disable materialization)
TEST 3
Pour désactiver la matérialisation, il suffit de mettre la directive suivante à OFF:
SET optimizer_switch=’materialization=off’;
La stratégie d’optimisation utilisée par l’optimiseur est alors la semi-jointure (une sorte de jointure interne où seule une occurrence des valeurs jointes est récupérée).
QEP
*************************** 1. row *************************** id: 1select_type: PRIMARY table: MyTable type: indexpossible_keys: MainIDOtherIDStatus,OtherIDStageStatusInvisibleMainID,StageMainIDOtherID key: OtherIDStageStatusInvisibleMainID key_len: 15 ref: NULL rows: 306910041 filtered: 0.00 Extra: Using where; Using index; Using temporary; LooseScan*************************** 2. row *************************** id: 1select_type: PRIMARY table: MyTable type: refpossible_keys: MainIDOtherIDStatus,StageMainIDOtherID key: MainIDOtherIDStatus key_len: 4 ref: MyTable.MainID rows: 1 filtered: 100.00 Extra: Using index condition; Using where
Le QEP obtenu est un full index scan aka FIS (type: index) avec covering index (Extra: Using index;). En général un FIS est plutôt une mauvaise nouvelle pour les perfs, sauf dans le cas d’un index covering.
L’algorithme utilisé par l’optimiseur pour la semi-jointure est le LooseScan (Extra: LooseScan).
A noter l’utilisation de l’optimisation index condition pushdown ou ICP (Extra: Using index condition). On en y revient plus tard.
Optimizer rewriting
select distinct OtherID from MyTable semi join (MyTable)where ((Status = 1) and (Stage = 4) and (Invisible = 0) and (OtherID = 2779262) and (Stage = 0) and (Invisible = 0)and (MainID = MainID))
Confirmation que l’on a bien une transformation en semi-jointure.
Les informations d’état sont assez claires (si si vous allez voir):
Le FIS est un parcours transverse complet de l’arbre d’index: (Handler_read_next | 306909498 et Handler_read_first | 1)
La sous-requête elle, est responsable de: (Handler_read_key | 236)
Le distinct est responsable de la création de la table temporaire: (Created_tmp_tables | 1)
Remplissage de la table temporaire: (Handler_tmp_write | 7)
Lecture des données de la table temporaire: (Handler_read_rnd_next | 8)
T2 vs T3 ça donne quoi ?
Là encore ce n’est pas trivial de comparer les QEP et status des 2 requêtes. On peut cependant raisonnablement penser que le FIS avec covering index de T3 va générer moins de random IO que T2.
Bench
daz@sql:~/mariadb5.5.30$ mysqlslap –create-schema=test -c1 -i200 -q »SELECT DISTINCT OtherID FROM MyTable WHERE Status = 1 AND Stage = 4 AND Invisible = 0 AND MainID IN ( SELECT MainID FROM MyTable WHERE OtherID = 2779262 AND Stage = 0 AND Invisible = 0 ); »Benchmark Minimum number of seconds to run all queries: 130.531 seconds
T3 s’exécute avec 3m20 de moins que T2.
T3 s’exécute avec 5m20 de moins que T1.
Optimizer choice
TEST 4
Comme je l’ai mentionné en introduction, l’optimiseur de MariaDB est le plus avancé (en matière de possibilités d’optimisation) dans l’univers MySQL. Voyons comment cet optimiseur, avec les réglages par défaut, gère cette requête.
La commande suivante permet de réinitialiser, les directives d’optimisation:
daz@sql:~/mariadb5.5.30$ mysqlslap –create-schema=test -c1 -i200 -q »SELECT DISTINCT OtherID FROM MyTable WHERE Status = 1 AND Stage = 4 AND Invisible = 0 AND MainID IN ( SELECT MainID FROM MyTable WHERE OtherID = 2779262 AND Stage = 0 AND Invisible = 0 ); »Benchmark Minimum number of seconds to run all queries: 0.026 seconds
T4 est plus rapide que T1 de plus de 9 minutes.
T4 est plus rapide que T2 de plus de 7 minutes.
T4 est plus rapide que T3 de plus de 2 minutes.
En image, en temps relatif par rapport à T1 (la requête la plus longue) ça donne :
Avec les versions de MySQL / Percona server inférieures ou égales à 5.5 et les versions de MariaDB inférieures ou égales à 5.2 (de mémoire), les sous-requêtes peuvent poser de gros problèmes de performances. Il faut donc bien tester ces requêtes avant de les passer en prod.
Une bonne raison supplémentaire pour tester (et migrer) vers MariaDB 5.5 ou MySQL 5.6 (qui possède également les optimisations semijoin et materialization).
A suivre, dans la deuxième partie de cet article : Index Condition Pushdown et Multi Range Read…
Jusqu’en MySQL 5.5 inclus, l’utilisation de sous-requêtes peut, dans certain cas, être la cause de problèmes de performances (l’optimiseur est bien meilleur en MySQL 5.6, MariaDB 5.5 et MariaDB 10).
Récemment j’ai eu un souci en prod, après une MEP, avec une requête qui durait en moyenne plus de 1000 secondes…
Inutile de préciser que dans un environnement OLTP, application web plus précisément, ce n’est pas jouable, elle a donc été virée très rapidement (mais pas le développeur qui l’a créée…)
Alors passons sur le fait que ce genre de problème devrait être identifié avant d’arriver sur la prod, et voyons à quoi ressemble une requête qui peut prendre 45 minutes:
MySQL
1
2
3
4
5
6
7
8
9
10
11
12
13
SELECTDISTINCTOtherID
FROMMyTable
WHEREStatus=1
ANDStage=4
ANDInvisible=0
ANDMainIDIN
(
SELECTMainID
FROMMyTable
WHEREOtherID=2779262
ANDStage=0
ANDInvisible=0
)
7 rows in set (44 min 56.66 sec)
On a donc une belle sous-requête dans la clause IN de la requête externe. La table est en InnoDB, elle contient 310 millions de lignes pour une taille de 62Go.
Malgré la présence d’index (pertinent ?), l’optimiseur analyse la moitié des lignes de la table (155316386), ce qui sur une grosse table peut prendre pas mal de temps, vous en conviendrez.
Niveau status variables
1
2
3
4
5
6
7
8
9
10
11
|Created_tmp_disk_tables|0|
|Created_tmp_files|0|
|Created_tmp_tables|1|
|Handler_read_first|0|
|Handler_read_key|44383976|
|Handler_read_last|0|
|Handler_read_next|151576920|
|Handler_read_prev|0|
|Handler_read_rnd|0|
|Handler_read_rnd_next|8|
|Handler_write|7|
Cette vision un peu plus bas niveau montre un nombre élevé de lecture lors du parcours transverse de l’index (Handler_read_next), en clair ce n’est pas vraiment bon pour les performances.
A noter que la clause DISTINCT génère la création d’une table temporaire, son remplissage et évidemment sa lecture (Created_tmp_tables, Handler_write, Handler_read_rnd_next) .
Temps d’exécution unitaire (w/ mysqlslap)
En mode unitaire, sur un serveur en configuration prod mais sans charge, mysqlslap nous donne:
Minimum number of seconds to run all queries: 478.936 seconds.
Comment faire pour rendre cette requête utilisable ?
Plusieurs choix, le plus simple est de la réécrire (pas de modification de la table ou des index). Une sous-requête de ce type se converti facilement en jointure:
MySQL
1
2
3
4
5
6
7
8
SELECTDISTINCTr1.OtherID
FROMMyTabler1INNER JOINMyTabler2USING(MainID)
WHEREr1.Status=1
ANDr1.Stage=4
ANDr1.Invisible=0
ANDr2.OtherID=2779262
ANDr2.Stage=0
ANDr2.Invisible=0
Qu’en dit le QEP ?
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: r2
type: ref
possible_keys: OtherIDStageStatusInvisibleMainID, autre index…
key: OtherIDStageStatusInvisibleMainID
key_len: 5
ref: const,const
rows: 234
Extra: Using where; Using index; Using temporary
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: r1
type: ref
possible_keys: MainIDOtherIDStatus,StageMainIDOtherID
key: MainIDOtherIDStatus
key_len: 4
ref: MyTable.r2.MainID
rows: 1
Extra: Using where
234 lignes à analyser au lieu de 155316386. Ça à l’air meilleur 🙂
Un index couvrant en prime (Extra: Using index), c’est toujours bon à prendre.
Niveau status variables
1
2
3
4
5
6
7
8
9
10
11
|Created_tmp_disk_tables|0|
|Created_tmp_files|0|
|Created_tmp_tables|1|
|Handler_read_first|0|
|Handler_read_key|236|
|Handler_read_last|0|
|Handler_read_next|705|
|Handler_read_prev|0|
|Handler_read_rnd|0|
|Handler_read_rnd_next|8|
|Handler_write|7|
Avec beaucoup moins de ligne analysées, le nombre de Handler_read_next est extrêmement bas par rapport à la la requête précédente: 705 au lieu de 151576920. On a donc un faible nombre de lecture lors parcours transverse de l’arbre d’index. En clair, c’est plutôt une bonne chose.
Le temps d’exécution de la requête à froid est: 7 rows in set (1.17 sec)
A mettre en perspective avec les 45 minutes de la première version…
Temps d’exécution unitaire (w/ mysqlslap)
Minimum number of seconds to run all queries: 0.001 seconds
Le faisceau d’indice valide bien un “meilleur” (c’est un euphémisme) temps d’exécution minimal unitaire (0.001 s vs 478.936 s)
Je pense que l’on va garder cette version de la requête pour la prod :).
Alors que faut il en tirer comme conclusion ?
Il est légitime de se poser la question de proscrire ou pas les sous-requêtes si on n’est pas en MySQL 5.6+ ou MariaDB 5.5+
Je conseillerai surtout de toujours tester ses requêtes avant de les pousser en prod… notamment en la passant au révélateur EXPLAIN.
En effet dans le cas de la 1ère requête, rows: 155316386, est un bon indicateur de “ça risque de partir en couille !”.
La requête peut également être réécrite de la manière suivante:
MySQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECTDISTINCTOtherID
FROM
(
SELECTMainID
FROMMyTable
WHEREInvisible=0
ANDStage=0
ANDOtherID=2779262
)dt
INNER JOINMyTabler
WHEREr.MainID=dt.MainID
ANDr.Invisible=0
ANDr.Stage=4
ANDr.Status=1
oh la belle sous requête ! (ou table dérivée pour les puristes)
Qu’en dit le QEP ?
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 235
Extra: Using temporary
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: r
type: ref
possible_keys: MainIDOtherIDStatus,StageMainIDOtherID
key: MainIDOtherIDStatus
key_len: 4
ref: dt.MainID
rows: 1
Extra: Using where
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: MyTable
type: ref
possible_keys: OtherIDStageStatusInvisibleMainID, autre index…
key: OtherIDStageStatusInvisibleMainID
key_len: 5
ref:
rows: 234
Extra: Using where; Using index
54990 (235 x 1 x 234) lignes à analyser + index couvrant. Ça n’a pas l’air trop mal tout ça !
Cette requête combine sous-requête + jointure et jusqu’ici… même pas peur.
Niveau status variables
1
2
3
4
5
6
7
8
9
10
11
|Created_tmp_disk_tables|0|
|Created_tmp_files|0|
|Created_tmp_tables|2|
|Handler_read_first|0|
|Handler_read_key|236|
|Handler_read_last|0|
|Handler_read_next|705|
|Handler_read_prev|0|
|Handler_read_rnd|0|
|Handler_read_rnd_next|244|
|Handler_write|242|
Les indications bas niveau fournit par les handlers sont assez proche de celles fournit par l’exécution de la version jointure seule, de la requête.
Seules les informations (Created_tmp_tables, Handler_read_rnd_next, Handler_write) liées aux 2 tables temporaires créées (1 pour la table dérivée + 1 pour le DISTINCT) sont différentes.
En(un peu plus) clair: Handler_write = 235 lignes écrites dans la table dérivée + 7 lignes du résultat final après dédoublonnage (DISTINCT) = 242
Ces 2 tables étant suffisamment petite pour tenir entièrement en mémoire (Created_tmp_disk_tables=0) le surcoût devrait être négligeable par rapport à la la version jointure seule de la requête.
Le temps d’exécution de cette requête à froid est:7 rows in set (1.23 sec)
Il est équivalent à la jointure.
Temps d’exécution unitaire (w/ mysqlslap)
Minimum number of seconds to run all queries: 0.001 seconds
On retrouve bien des temps d’exécution équivalent au millième près. CQFD
Il y a souvent plusieurs façon d’écrire une requête, mais elles sont rarement équivalentes en matière de temps d’exécution. La commande EXPLAIN fait partie du package minimal que tout développeur doit connaître. Ajouter à cela un environnement de test et un zeste de bon sens et il devient alors possible d’éliminer une partie des soucis de prod et d’éviter de perdre du temps inutilement.
Il y a quelques jours j’ai passé mon premier entretien, coté recruteur, pour un poste de DBA MySQL stagiaire chez Viadeo. Ce fut une expérience particulièrement enrichissante. Pour ne rien vous cacher j’ai eu un peu d’appréhension et une petite dose de stress 🙂 mais beaucoup moins que le candidat reçu de jour là.
Ça m’a rappelé le fait que j’ai passé beaucoup beaucoup beaucoup d’entretiens dans ma vie professionnelle et voici ce que j’en ai tiré:
– Stress
C’est normal d’avoir de l’appréhension surtout lorsque l’on débute sa carrière professionnelle (et même plus tard). Différentes méthodes existent pour gérer le stress, la mienne est déjà de l’accepter (que celui qui n’a jamais stressé jette le premier CV). C’est humain et dans certaines situations il peut sauver notre vie. J’essaye néanmoins de le contenir en diminuant la part de la part d’incertitude sur laquelle j’ai une influence (la cause principale du stress).
Comment diminuer cette part de part d’incertitude ?
Préparer l’entretien en se renseignant sur l’entreprise et sur le poste à pourvoir. Cela permet de montrer son l’intérêt pour l’entreprise et pour le poste. Ça permet aussi de s’auto-rassurer et d’avoir des choses à dire qui ne sont pas que du registre “moi je”
Maîtriser l’histoire de son parcours scolaire et/ou professionnel. C’est à dire pouvoir notamment expliquer les éventuelles “aspérités” de son parcours et en donner une image positive.
La meilleure façon que j’ai trouvé pour améliorer tout ça a été de passer beaucoup d’entretiens, notamment pour des postes qui ne m’intéressait pas :). L’avantage est de roder son discours dans un cadre où la pression est moindre. Et ça reste un superbe moyen de s’entraîner en condition réelle. Je le recommande fortement !
– Sur un pied d’égalité
La chose la plus important que j’ai comprise dans le monde de l’entreprise et notamment lors du processus d’entretien, c’est que l’entreprise ne vous donne pas un boulot pour vous faire plaisir mais qu’elle veut acheter une compétence, une expertise voir un potentiel car elle va lui (r)apporter quelque chose. Alors comme tout processus d’achat, l’idée est d’acheter à un moindre prix et d’en tirer un maximum, c’est le jeu, mais la chose importante à garder en tête est que l’état d’esprit à avoir est “échange de compétence / potentiel / expertise contre rémunération” et surtout pas celui de “subalterne à supérieur”. Surtout que, pour la petite histoire, il y a des chances que le recruteur en face de vous soit dans un statut précaire avec un salaire inférieur ou égale à celui de vos prétentions… ça remet les choses en perspective !
– Salaire
Combien je vaux (dans le monde de l’entreprise) ??? c’est une question que je me suis souvent posée et je me suis rendu compte qu’il est difficile d’y répondre, en tout cas dans mon domaine, mais je ne pense pas être le seul. Il y a pas mal de ressources sur internet et dans les magasines spécialisés mais je me demande où ils trouvent leurs chiffres car ils n’ont jamais correspondu à mon salaire. Autre difficulté, la question du salaire est tabou en France, ce n’est pas évident de savoir combien gagne quelqu’un à poste et niveau équivalent.
Le salaire est important mais il ne dit pas tout (productivité, efficacité,…). De plus ce n’est pas le seul paramètre à prendre en compte: les avantages en nature, l’expérience que l’on va en tirer (surtout pour les jeunes diplômés), le CV (une belle expérience ou une grande entreprise peuvent permettre de faire de grands bonds niveau salaire par la suite), le lieu géographique, les perspectives d’évolutions,… sont autant de variables qui peuvent (doivent) entrer en compte dans le choix du job.
Je n’ai pas de méthodes miracles (si vous en avez faite tourner :D), mais là encore je n’ai pas trouvé mieux que de passer des entretiens. Ça donne une bonne idée de ce que le marché peut payer pour un poste précis. Et puis si vous n’êtes pas satisfait par votre salaire (ou autre) rien ne vous empêche d’aller voir ailleurs par la suite.
– Réponse négative
Rien de personnel ! Inadéquation entre le poste et le candidat ou un autre candidat semble tout simplement mieux adapté. La réponse négative (ou la non réponse) fait partie intégrante du processus de recherche d’emploi. Eh ben oui, on ne peut pas plaire à tout le monde !
C’est comme ça et il faut l’accepter. Cet entretien aura servi d’entraînement pour être meilleur pour le prochain.
Une fois n’est pas coutume, un article pour vous parler d’une offre de stage (avec possibilité d’embauche) en tant que DBA MySQL.
Je vais faire simple, Viadeo (réseau social professionnel) cherche à étoffer son équipe MySQL. On cherche donc un DBA MySQL (en devenir). Si tu souhaites rejoindre une boite en plein expansion, où il fait bon travailler et où il y a plein de challenges technologiques à relever, tente ta chance 🙂
A oui, j’ai failli oublier, tu vas travailler avec moi… 😉
Les deux conférenciers qui animeront cette soirée sont à la hauteur du sujet choisi puisqu’il s’agit de Olivier Dasini, expert MySQL chez Viadeo qui présentera une méthodologie pour optimiser les performances des requêtes d’une application et Thomas Reiss, expert PostgreSQL à Dalibo, nous détaillera les fonctionnalités de PostgreSQL, en s’appuyant sur différents retours d’expérience pour mieux nous en faire découvrir les capacités.
Pour clôturer la soirée, Serge Frezefond, Cloud Solutions Architect chez SkySQL, présentera le driver MySQL sous licence PHP.
Comme toujours le RDV estgratuit et ouvert à tous, n’hésitez donc pas en parler autour de vous !
Le rendez-vous se tiendra le jeudi 28 février dans les locaux de chez Linagora, à partir de 18h30 (ouverture des portes), 80 Rue Roque de Fillol 92800 Puteaux
Début des sessions à 19h00 et nombre de places limités