
{"id":1425,"date":"2013-05-27T14:05:36","date_gmt":"2013-05-27T13:05:36","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=1425"},"modified":"2013-05-30T16:02:37","modified_gmt":"2013-05-30T15:02:37","slug":"utiliser-une-sous-requete-cest-mal-suite-part-1-3","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2013\/05\/27\/utiliser-une-sous-requete-cest-mal-suite-part-1-3\/","title":{"rendered":"Utiliser une sous requ\u00eate c\u2019est mal ? (suite) part 1-3"},"content":{"rendered":"<p dir=\"ltr\">Comme promit, voici la suite de l\u2019article<a href=\"http:\/\/dasini.net\/blog\/2013\/04\/23\/utiliser-une-sous-requete-cest-mal\/\"> Utiliser une sous-requ\u00eate c\u2019est mal ?<\/a><\/p>\n<p dir=\"ltr\">L\u2019id\u00e9e ici est de r\u00e9pondre aux interrogations de<a href=\"http:\/\/dasini.net\/blog\/2013\/04\/23\/utiliser-une-sous-requete-cest-mal\/#comment-1803\"> svar<\/a> et d\u2019en profiter pour explorer les nouvelles possibilit\u00e9s de <strong>la variante stable de MySQL qui poss\u00e8de l\u2019optimiseur le plus avanc\u00e9<\/strong>, c\u2019est \u00e0 dire<strong><a href=\"https:\/\/mariadb.org\/\"> MariaDB 5.5<\/a><\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<h2 dir=\"ltr\">Pr\u00e9ambule<\/h2>\n<p dir=\"ltr\">En pr\u00e9-requis, je vous invite \u00e0 explorer la doc officielle de MySQL au sujet de la variable<a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/switchable-optimizations.html\"> optimizer_switch<\/a>, qui permet de s\u00e9lectionner les algorithmes d\u2019optimisation, ainsi que<a href=\"https:\/\/kb.askmonty.org\/en\/query-optimizations\/\"> les diff\u00e9rentes subtilit\u00e9s impl\u00e9ment\u00e9es dans l\u2019optimiseur de MariaDB<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">La valeur par d\u00e9faut sur MariaDB 5.5.30 de la variable <strong><em>optimizer_switch<\/em><\/strong> est:<\/p>\n<p>&nbsp;<\/p>\n<address dir=\"ltr\"><span style=\"color: #000080;\">mariadb5.5&gt; SHOW VARIABLES LIKE &lsquo;optimizer_switch&rsquo;\\G<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 1. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">Variable_name: optimizer_switch<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">index_merge_intersection=on,index_merge_sort_intersection=off,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">engine_condition_pushdown=off,<strong>index_condition_pushdown=on<\/strong>,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">derived_merge=on,derived_with_keys=on,firstmatch=on,<strong>loosescan=on,<\/strong><\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\"><strong>materialization=on,in_to_exists=on,semijoin=on<\/strong>,partial_match_rowid_merge=on,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">partial_match_table_scan=on,subquery_cache=on,<strong>mrr=off<\/strong>,mrr_cost_based=off,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\"><strong>mrr_sort_keys=off<\/strong>,outer_join_with_cache=on,semijoin_with_cache=on,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">optimize_join_buffer_size=off,table_elimination=on,extended_keys=off<\/span><\/address>\n<p dir=\"ltr\">Le truc super lisible en fait !<\/p>\n<p dir=\"ltr\">Mais comme je suis un mec super sympa, j\u2019ai mis en gras les directives qui vont nous int\u00e9resser dans cet article.<\/p>\n<p>&nbsp;<\/p>\n<h2 dir=\"ltr\">Les patterns des tests<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Pour atteindre les objectifs fix\u00e9s, j\u2019ai effectu\u00e9 2 niveaux de tests. Le premier niveau va me fournir des donn\u00e9es \u201cobjectives\u201d. Il consiste \u00e0 ex\u00e9cuter la requ\u00eate avec diff\u00e9rentes directives d\u2019optimisation et d\u2019analyser les \u201cstatus\u201d ainsi que les plans d\u2019ex\u00e9cution associ\u00e9s.<\/p>\n<h3 dir=\"ltr\">Niveau stats<\/h3>\n<ol>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;default&rsquo;<\/strong> : r\u00e9init des directives d\u2019optimisation<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;<\/strong>valeur ad\u00e9quate<strong>\u2019<\/strong> : choix des directives d\u2019optimisation<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong><a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/explain-extended.html\">EXPLAIN EXTENDED<\/a><\/strong> : QEP et infos de r\u00e9\u00e9critures de la requ\u00eate<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SHOW WARNINGS<\/strong> : pour savoir comment l\u2019optimiseur \u00e0 r\u00e9\u00e9crit la requ\u00eate<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>FLUSH STATUS<\/strong> : r\u00e9init des stats \u00e0 0<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><em>ex\u00e9cution de la requ\u00eate<\/em><\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SHOW STATUS<\/strong> : pour r\u00e9cup\u00e9rer les variables d\u2019\u00e9tats, <em>Handler*<\/em> &amp; <em>Created_tmp<\/em><\/p>\n<\/li>\n<\/ol>\n<p dir=\"ltr\">Avec le second niveau de tests, je vais r\u00e9cup\u00e9rer une info \u201csubjective\u201d, le temps. Cela consiste \u00e0 r\u00e9cup\u00e9rer la dur\u00e9e minimale d\u2019ex\u00e9cution de la requ\u00eate avec les diff\u00e9rentes directives d\u2019optimisations \u00e0 l\u2019aide de l\u2019outil<a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.5\/en\/mysqlslap.html\"> mysqlslap<\/a>.<\/p>\n<h3 dir=\"ltr\">Niveau temps<\/h3>\n<ol>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SET GLOBAL optimizer_switch=&rsquo;default&rsquo;<\/strong><\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>SET GLOBAL optimizer_switch=&rsquo;<\/strong><em>valeur ad\u00e9quate<\/em><strong>\u2019<\/strong><\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\"><strong>mysqlslap \u00a0-c1 -i200 -q\u00a0\u00bb<\/strong><em>la requ\u00eate<\/em><strong>\u00ab\u00a0<\/strong> : requ\u00eate unitaire jou\u00e9e plusieurs fois ie buffers chaud<\/p>\n<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">A noter que le nombre d\u2019it\u00e9ration (200) \u00e0 \u00e9t\u00e9 r\u00e9duit \u00e0 5 lorsque la requ\u00eate dure de l\u2019ordre de la 10aine de minutes (bah oui, j\u2019ai une vie aussi).<\/p>\n<h2 dir=\"ltr\">Contexte<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Pour rappel, le probl\u00e8me initiale est une<a href=\"http:\/\/dasini.net\/blog\/2013\/04\/23\/utiliser-une-sous-requete-cest-mal\/\"> sous requ\u00eate probl\u00e9matique<\/a> en MySQL (Percona server) 5.5 :<\/p>\n<p>&nbsp;<\/p>\n<address dir=\"ltr\">SELECT DISTINCT OtherID<\/address>\n<address dir=\"ltr\">FROM MyTable<\/address>\n<address dir=\"ltr\">WHERE Status = 1<\/address>\n<address dir=\"ltr\">\u00a0AND Stage = 4<\/address>\n<address dir=\"ltr\">\u00a0AND Invisible = 0<\/address>\n<address dir=\"ltr\">\u00a0AND MainID IN<\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 <span style=\"color: #000080;\">\u00a0(<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0SELECT MainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0FROM MyTable<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0WHERE OtherID = 2779262<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0AND Stage = 0<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0AND Invisible = 0<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0)<\/span><\/address>\n<address dir=\"ltr\">+&#8212;&#8212;&#8212;-+<\/address>\n<address dir=\"ltr\">| OtherID |<\/address>\n<address dir=\"ltr\">+&#8212;&#8212;&#8212;-+<\/address>\n<address dir=\"ltr\">&#8230;<\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #ff0000;\">7 rows in set (44 min 56.66 sec)<\/span> &lt;= A froid<\/strong><\/address>\n<p dir=\"ltr\">L\u2019id\u00e9e ici n\u2019est <span style=\"text-decoration: underline;\">pas de comparer le temps d\u2019ex\u00e9cution<\/span> de la requ\u00eate sur MariaDB par rapport \u00e0 \u00a0Percona server (les serveurs et les param\u00e8tres sont diff\u00e9rents), mais bien de voir et de comprendre les (nouveaux) choix de l\u2019optimiseur de MariaDB 5.5.<\/p>\n<h2 dir=\"ltr\">Traditional behavior (IN to EXISTS strategy)<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\"><strong>TEST 1<\/strong><\/p>\n<p dir=\"ltr\">Traditionnellement l\u2019optimiseur converti la sous-requ\u00eate IN en EXIST. Pour simuler ce comportement il faut d\u00e9sactiver la<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-materialization-strategy\/\"> mat\u00e9rialisation<\/a> et la<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-subquery-optimizations\/\"> semi-jointure<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;materialization=off,semijoin=off&rsquo;;<\/strong><\/p>\n<p>&nbsp;<\/p>\n<h3 dir=\"ltr\">QEP<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 1. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: StageMainIDOtherID,&#8230;<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: StageMainIDOtherID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: const<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 153455020<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where; <strong>Using temporary<\/strong><\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 2. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 2<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: DEPENDENT SUBQUERY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: index_subquery<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: MainIDOtherIDStatus,&#8230;<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: MainIDOtherIDStatus<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 8<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: func,const<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 1<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where<\/span><\/address>\n<p dir=\"ltr\">Intuitivement on peut penser que la requ\u00eate interne est ex\u00e9cut\u00e9e en premier, puis qu\u2019elle g\u00e9n\u00e8re une liste d\u2019ID (MainID) qui est ensuite pass\u00e9e dans la clause IN, du genre :<\/p>\n<address dir=\"ltr\">\u2026 MainID IN (123, 456, 789).<\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Eh ben non !<\/p>\n<p dir=\"ltr\">Comme convenu, le QEP est semblable \u00e0 celui utilis\u00e9 avec MySQL \/ percona server 5.5.<\/p>\n<p dir=\"ltr\">En fait, l\u2019optimiseur transforme le clause<a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.5\/en\/subquery-optimization-with-exists.html\"> IN en EXISTS<\/a>. La table externe est lue la premi\u00e8re sur environ 50% de ses lignes (<span style=\"color: #000080;\"><strong>rows: 153455020<\/strong><\/span>), ce qui dans notre cas fait beaucoup et est jointe avec la table interne.<\/p>\n<address dir=\"ltr\">La sous-requ\u00eate d\u00e9pend alors des donn\u00e9es de la requ\u00eate externe (<strong><span style=\"color: #000080;\">select_type: DEPENDENT SUBQUERY<\/span><\/strong>).<\/address>\n<p dir=\"ltr\">La requ\u00eate r\u00e9\u00e9crite par l\u2019optimiseur ressemble alors \u00e0 peu pr\u00e8s \u00e0 :<\/p>\n<p>&nbsp;<\/p>\n<address dir=\"ltr\">SELECT DISTINCT OtherID<\/address>\n<address dir=\"ltr\">FROM MyTable <span style=\"color: #000080;\">AS MyExtTable<\/span><\/address>\n<address dir=\"ltr\">WHERE Status = 1<\/address>\n<address dir=\"ltr\">\u00a0AND Stage = 4<\/address>\n<address dir=\"ltr\">\u00a0AND Invisible = 0<\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0AND EXISTS<\/span><\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0(<\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0SELECT 1<\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0FROM MyTable <span style=\"color: #000080;\">AS MyInnTable<\/span><\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0WHERE OtherID = 2779262<\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0AND Stage = 0<\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0AND Invisible = 0<\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0AND MyExtTable.MainID = MyInnTable.MainID<\/span><\/address>\n<address dir=\"ltr\">\u00a0\u00a0\u00a0 \u00a0)<\/address>\n<address dir=\"ltr\">\u00a0<\/address>\n<address dir=\"ltr\">\u00a0<\/address>\n<h3 dir=\"ltr\">Optimizer rewriting<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select distinct OtherID AS OtherID from MyTable<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">where ((Status = 1) and (Stage = 4) and (Invisible = 0) and &lt;expr_cache&gt;&lt;MainID&gt;(&lt;in_optimizer&gt;(MainID,&lt;exists&gt;(&lt;index_lookup&gt;(&lt;cache&gt;(MainID) in MyTable on MainIDOtherIDStatus<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">where (((OtherID = 2779262) and (Stage = 0) and (Invisible = 0)) and (<strong>&lt;cache&gt;(MainID) = MainID<\/strong>) and (OtherID = 2779262) and (Stage = 0) and (Invisible = 0)))))))<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Confirmation que l\u2019on a bien une transformation<a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.5\/en\/subquery-optimization-with-exists.html\"> IN to EXISTS<\/a>.<\/p>\n<h3 dir=\"ltr\">Status<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Variable_name \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_disk_tables \u00a0\u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Created_tmp_tables \u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| 2 \u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_key_refills \u00a0\u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_rowid_refills \u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_prepare \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_first \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_key \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 44655686 \u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_last \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_next \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 153049748 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_prev \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd_deleted \u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_rnd_next \u00a0\u00a0\u00a0 | 8 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_tmp_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 206 \u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Beaucoup de lecture dans l\u2019index (<strong><span style=\"color: #000080;\">Handler_read_key | 44655686<\/span><\/strong>). Le probl\u00e8me ici est le nombre \u00e9lev\u00e9 de lectures transverse de l\u2019index (<strong><span style=\"color: #000080;\">Handler_read_next | 153049748<\/span><\/strong>) qui est tr\u00e8s co\u00fbteux, surtout si les IO sont sur disque, et c\u2019est encore pire s\u2019ils sont al\u00e9atoires ie random IO disk.<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Contrairement \u00e0 MySQL 5.5 o\u00f9 il n\u2019y a qu\u2019une seule table temporaire cr\u00e9\u00e9e, ici il y en a 2 (<strong><span style=\"color: #000080;\">Created_tmp_table | 2<\/span><\/strong>), l\u2019une pour le d\u00e9doublonnage li\u00e9 \u00e0 la clause DISTINCT et l\u2019autre&#8230; ben je ne sais pas :\/<\/p>\n<p dir=\"ltr\">En profilant la requ\u00eate avec la commande<a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.5\/en\/show-profile.html\"> SHOW PROFILE<\/a>, il y est bien not\u00e9 que 2 tables temporaires sont supprim\u00e9es, mais dans le m\u00eame temps, une seule est cr\u00e9\u00e9e&#8230;<\/p>\n<p dir=\"ltr\">Si quelqu\u2019un \u00e0 une explication, je suis preneur.<\/p>\n<h3 dir=\"ltr\">Bench<\/h3>\n<p dir=\"ltr\"><span style=\"color: #993300;\">daz@sql:~\/mariadb5.5.30$ mysqlslap &#8211;create-schema=test -c1 -i5 -q\u00a0\u00bbSELECT DISTINCT OtherID \u00a0FROM MyTable \u00a0WHERE Status = 1 \u00a0AND Stage = 4 \u00a0AND Invisible = 0 \u00a0AND MainID IN \u00a0( SELECT MainID \u00a0FROM MyTable \u00a0WHERE OtherID = 2779262 \u00a0AND Stage = 0 \u00a0AND Invisible = 0 \u00a0);\u00a0\u00bb<\/span><\/p>\n<p dir=\"ltr\"><span style=\"color: #993300;\">Benchmark<\/span><\/p>\n<p dir=\"ltr\"><span style=\"color: #993300;\"><strong>\u00a0\u00a0Minimum number of seconds to run all queries: 550.444 seconds<\/strong><\/span><\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Les meilleures performances de cette requ\u00eate, dans ces conditions, sont de l\u2019ordre de la 10aine de minutes, ce qui est rarement acceptable en<a href=\"http:\/\/en.wikipedia.org\/wiki\/Online_transaction_processing\"> OLTP<\/a>.<\/p>\n<p dir=\"ltr\">Pour simuler l\u2019\u201dintelligence\u201d de l\u2019optimiseur de MySQL \/ Percona Server 5.5, il a fallu d\u00e9sactiver 2 directives d\u2019optimisation (la<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-materialization-strategy\/\"> mat\u00e9rialisation<\/a> et la<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-subquery-optimizations\/\"> semi-jointure<\/a>). Voyons ce qu\u2019il en est en ne d\u00e9sactivant qu\u2019une seule d\u2019entre elles, \u00e0 tour de r\u00f4le.<\/p>\n<h2 dir=\"ltr\">MATERIALIZATION strategy (disable semi-join)<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\"><strong>TEST 2<\/strong><\/p>\n<p dir=\"ltr\">Pour d\u00e9sactiver la semi-jointure, il suffit de mettre la directive suivante \u00e0 OFF:<\/p>\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;semijoin=off&rsquo;;<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">La strat\u00e9gie d\u2019optimisation utilis\u00e9e par l\u2019optimiseur est alors la<a href=\"https:\/\/kb.askmonty.org\/en\/non-semi-join-subquery-optimizations\/#materialization-for-non-correlated-in-subqueries\"> mat\u00e9rialisation<\/a>. En 2 mots, le r\u00e9sultat de la sous-requ\u00eate est stock\u00e9 dans une table temporaire qui contient un index unique. Cet index sert \u00e0 d\u00e9doublonner les valeurs ins\u00e9r\u00e9e et \u00e0 r\u00e9cup\u00e9rer les infos de mani\u00e8re performante (comme un index classique), qui vont alimenter la requ\u00eate externe.<\/p>\n<h3 dir=\"ltr\">QEP<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 1. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: StageMainIDOtherID, &#8230;<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: StageMainIDOtherID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: const<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 153455020<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where; Using temporary<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 2. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 2<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">select_type: MATERIALIZED<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: MainIDOtherIDStatus,OtherIDStageStatusInvisibleMainID,StageMainIDOtherID, &#8230;<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: OtherIDStageStatusInvisibleMainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 5<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: const,const<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 235<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where;<strong> Using index<\/strong><\/span><\/address>\n<p>&nbsp;<\/p>\n<address dir=\"ltr\">L\u00e0 encore, selon le QEP la table externe est lue la premi\u00e8re sur environ 50% de ses lignes \u00a0(<strong><span style=\"color: #000080;\">rows: 153455020<\/span><\/strong>). Ce qui change l\u00e0, c\u2019est que la strat\u00e9gie d\u2019optimisation retenue est la<a href=\"https:\/\/kb.askmonty.org\/en\/non-semi-join-subquery-optimizations\/#materialization-for-non-correlated-in-subqueries\"> mat\u00e9rialisation<\/a>.<\/address>\n<p dir=\"ltr\">Autres diff\u00e9rences avec le QEP originel, un covering index (<strong><span style=\"color: #000080;\">Extra: Using where; Using index<\/span><\/strong>) sur les 235 (une estimation) enregistrements (<strong><span style=\"color: #000080;\">rows: 235<\/span><\/strong>) de la table mat\u00e9rialis\u00e9e.<\/p>\n<h3 dir=\"ltr\">Optimizer rewriting<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select distinct OtherID AS OtherID from MyTable<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">where ((Status = 1) and (Stage = 4) and (Invisible = 0) and &lt;expr_cache&gt;&lt;MainID&gt;(&lt;in_optimizer&gt;(MainID,MainID in<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">( &lt;materialize&gt; (select MainID from MyTable<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\"><strong>where ((OtherID = 2779262) and (Stage = 0) and (Invisible = 0)) ), &lt;primary_index_lookup&gt;(MainID in &lt;temporary table&gt; on distinct_key where ((MainID = &lt;subquery2&gt;.MainID))))))<\/strong>)<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Confirmation que l\u2019on \u00e0 bien la mat\u00e9rialisation de la sous-requ\u00eate sous la forme d\u2019une table temporaire index\u00e9e(<span style=\"color: #000080;\"><strong>&lt;primary_index_lookup&gt;MainID in &lt;temporary table&gt; on distinct_key<\/strong><\/span>).<\/p>\n<p dir=\"ltr\">A noter que l\u2019index \u00e0 \u00e9galement servi au d\u00e9doublonnage.<\/p>\n<h3 dir=\"ltr\">Status<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Variable_name \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_disk_tables \u00a0\u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Created_tmp_tables \u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0| 3 \u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_key_refills \u00a0\u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_rowid_refills \u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_prepare \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_first \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_key \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 44655687 \u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_last \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_next \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 153049981 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_prev \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd_deleted \u00a0\u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_rnd_next \u00a0\u00a0\u00a0 | 8 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_tmp_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 442 \u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<p dir=\"ltr\">Niveau table temporaires, on a 1 table temporaire pour le DISTINCT + 1 table pour la mat\u00e9rialisation + 1 table temporaire myst\u00e8re (<span style=\"color: #000080;\">Created_tmp_tables | 3<\/span>).<\/p>\n<p dir=\"ltr\">Avec une table temporaire suppl\u00e9mentaire, il y a alors plus d\u2019\u00e9criture (<strong><span style=\"color: #000080;\">Handler_tmp_write | 442<\/span><\/strong>).<\/p>\n<p dir=\"ltr\"><em>442 (Handler_tmp_write) &#8211; 235 (MATERIALIZED rows) = 207 (\u00e0 rapporter au 206 du Handler_tmp_write du QEP originel)<\/em><\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">A l\u2019aide de toutes ces informations, lequel des 2 modes est le plus performant ?<\/p>\n<p dir=\"ltr\">D\u2019un point de vu status, il y a 1 table temporaire de plus pour T2, mais les tables \u00e9tant cr\u00e9\u00e9es enti\u00e8rement en m\u00e9moire, le co\u00fbt reste donc faible. De plus le nombre de Handler_read_next est sensiblement \u00e9gale. M\u00eame s\u2019il n\u2019est pas r\u00e9parti de la m\u00eame mani\u00e8re.<\/p>\n<p dir=\"ltr\">Bref, niveau status, exaequo entre T1 &amp; T2<\/p>\n<p dir=\"ltr\">Avec explain, le nombre de rows de T2 est 235 fois plus important que pour T1. Cependant, T2 fournit une mat\u00e9rialisation et index covering index alors qu\u2019avec T1 on est dans de la sous-requ\u00eate corr\u00e9l\u00e9e !<\/p>\n<p dir=\"ltr\">Bref niveau explain, c\u2019est pas clair non plus \ud83d\ude00 mais la mat\u00e9rialisation se faisant en m\u00e9moire, elle devrait \u00eatre moins co\u00fbteuse.<\/p>\n<h3 dir=\"ltr\">Bench<\/h3>\n<address dir=\"ltr\"><span style=\"color: #993300;\">daz@sql:~\/mariadb5.5.30$ mysqlslap &#8211;create-schema=test -c1 -i5 -q\u00a0\u00bbSELECT DISTINCT OtherID \u00a0FROM MyTable \u00a0WHERE Status = 1 \u00a0AND Stage = 4 \u00a0AND Invisible = 0 \u00a0AND MainID IN \u00a0( SELECT MainID \u00a0FROM MyTable \u00a0WHERE OtherID = 2779262 \u00a0AND Stage = 0 \u00a0AND Invisible = 0 \u00a0);\u00a0\u00bb<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\">Benchmark<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\"><strong>\u00a0\u00a0Minimum number of seconds to run all queries: 430.712 seconds<\/strong><\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">T2 s\u2019ex\u00e9cute avec <strong>2 minutes de moins<\/strong> que T1.<\/p>\n<h2 dir=\"ltr\">SEMIJOIN strategy (disable materialization)<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\"><strong>TEST 3<\/strong><\/p>\n<p dir=\"ltr\">Pour d\u00e9sactiver la mat\u00e9rialisation, il suffit de mettre la directive suivante \u00e0 OFF:<\/p>\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;materialization=off&rsquo;;<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">La strat\u00e9gie d\u2019optimisation utilis\u00e9e par l\u2019optimiseur est alors la<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-subquery-optimizations\/\"> semi-jointure<\/a> (une sorte de jointure interne o\u00f9 seule une occurrence des valeurs jointes est r\u00e9cup\u00e9r\u00e9e).<\/p>\n<h3 dir=\"ltr\">QEP<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 1. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: index<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: MainIDOtherIDStatus,OtherIDStageStatusInvisibleMainID,StageMainIDOtherID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: OtherIDStageStatusInvisibleMainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 15<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: NULL<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 306910041<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 0.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where; <strong>Using index; Using temporary; LooseScan<\/strong><\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 2. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: MainIDOtherIDStatus,StageMainIDOtherID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: MainIDOtherIDStatus<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 4<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: MyTable.MainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: <strong>Using index condition<\/strong>; Using where<\/span><\/address>\n<p dir=\"ltr\">Le QEP obtenu est un full index scan aka FIS (<strong><span style=\"color: #000080;\">type: index<\/span><\/strong>) avec covering index (<strong><span style=\"color: #000080;\">Extra: Using index;<\/span><\/strong>). En g\u00e9n\u00e9ral un FIS est plut\u00f4t une mauvaise nouvelle pour les perfs, sauf dans le cas d\u2019un index covering.<\/p>\n<p dir=\"ltr\">L\u2019algorithme utilis\u00e9 par l\u2019optimiseur pour la semi-jointure est le \u00a0<a href=\"https:\/\/kb.askmonty.org\/en\/loosescan-strategy\/\">LooseScan<\/a> (<strong><span style=\"color: #000080;\">Extra: LooseScan<\/span><\/strong>).<\/p>\n<p dir=\"ltr\">A noter l\u2019utilisation de l\u2019optimisation<a href=\"https:\/\/kb.askmonty.org\/en\/index-condition-pushdown\/7594\/\"> index condition pushdown ou ICP<\/a> (<strong><span style=\"color: #000080;\">Extra: Using index condition<\/span><\/strong>). On en y revient plus tard.<\/p>\n<h3 dir=\"ltr\">Optimizer rewriting<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select distinct OtherID from <strong>MyTable semi join (MyTable)<\/strong><\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">where ((Status = 1) and (Stage = 4) and (Invisible = 0) and (OtherID = 2779262) and (Stage = 0) and (Invisible = 0)<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">and <strong>(MainID = MainID)<\/strong>)<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Confirmation que l\u2019on a bien une transformation en<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-subquery-optimizations\/\"> semi-jointure<\/a>.<\/p>\n<h3 dir=\"ltr\">Status<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Variable_name \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_disk_tables | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_files \u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Created_tmp_tables \u00a0\u00a0\u00a0\u00a0 | 1 \u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_key_refills \u00a0\u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_rowid_refills | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_prepare \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_first \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 1 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_key \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 236 \u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_last \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_next \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 306909498 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_prev \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd_deleted \u00a0| 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_rnd_next \u00a0\u00a0\u00a0 | 8 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_tmp_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 7 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Les informations d\u2019\u00e9tat sont assez claires (si si vous allez voir):<\/p>\n<ul>\n<li dir=\"ltr\">\n<p dir=\"ltr\">Le FIS est un parcours transverse complet de l\u2019arbre d\u2019index: (<strong><span style=\"color: #000080;\">Handler_read_next | 306909498<\/span><\/strong> et \u00a0<strong><span style=\"color: #000080;\">Handler_read_first | 1<\/span><\/strong>)<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\">La sous-requ\u00eate elle, est responsable de: (<strong><span style=\"color: #000080;\">Handler_read_key | 236<\/span><\/strong>)<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\">Le distinct est responsable de la cr\u00e9ation de la table temporaire: (<strong><span style=\"color: #000080;\">Created_tmp_tables | 1<\/span><\/strong>)<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\">Remplissage de la table temporaire: (<span style=\"color: #000080;\"><strong>Handler_tmp_write | 7<\/strong><\/span>)<\/p>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\">Lecture des donn\u00e9es de la table temporaire: (<span style=\"color: #000080;\"><strong>Handler_read_rnd_next | 8<\/strong><\/span>)<\/p>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">T2 vs T3 \u00e7a donne quoi ?<\/p>\n<p dir=\"ltr\">L\u00e0 encore ce n\u2019est pas trivial de comparer les QEP et status des 2 requ\u00eates. On peut cependant raisonnablement penser que le FIS avec <em>covering index<\/em> de T3 va g\u00e9n\u00e9rer moins de random IO que T2.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h3 dir=\"ltr\">Bench<\/h3>\n<address dir=\"ltr\"><span style=\"color: #993300;\">daz@sql:~\/mariadb5.5.30$ mysqlslap &#8211;create-schema=test -c1 -i200 -q\u00a0\u00bbSELECT DISTINCT OtherID \u00a0FROM MyTable \u00a0WHERE Status = 1 \u00a0AND Stage = 4 \u00a0AND Invisible = 0 \u00a0AND MainID IN \u00a0( SELECT MainID \u00a0FROM MyTable \u00a0WHERE OtherID = 2779262 \u00a0AND Stage = 0 \u00a0AND Invisible = 0 \u00a0);\u00a0\u00bb<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\">Benchmark<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\"><strong>\u00a0\u00a0Minimum number of seconds to run all queries: 130.531 seconds<\/strong><\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">T3 s\u2019ex\u00e9cute avec <span style=\"color: #000000;\"><strong>3m20 de moins<\/strong><\/span> que T2.<\/p>\n<p dir=\"ltr\">T3 s\u2019ex\u00e9cute avec <span style=\"color: #000000;\"><strong>5m20 de moins<\/strong><\/span> que T1.<\/p>\n<h2 dir=\"ltr\">Optimizer choice<\/h2>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\"><strong>TEST 4<\/strong><\/p>\n<p dir=\"ltr\">Comme je l\u2019ai mentionn\u00e9 en introduction, l\u2019optimiseur de MariaDB est le plus avanc\u00e9 (en mati\u00e8re de possibilit\u00e9s d\u2019optimisation) dans l\u2019univers MySQL. Voyons comment cet optimiseur, avec les r\u00e9glages par d\u00e9faut, g\u00e8re cette requ\u00eate.<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">La commande suivante permet de r\u00e9initialiser, les directives d\u2019optimisation:<\/p>\n<p dir=\"ltr\"><strong>SET optimizer_switch=&rsquo;default&rsquo;;<\/strong><\/p>\n<h3 dir=\"ltr\">QEP<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 1. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: &lt;subquery2&gt;<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ALL<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">possible_keys: distinct_key<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: NULL<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: NULL<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: NULL<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 235<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using temporary<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 2. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select_type: PRIMARY<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: MainIDOtherIDStatus,StageMainIDOtherID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: MainIDOtherIDStatus<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 4<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: MyTable.MainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 1<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: <strong>Using index condition<\/strong>; Using where<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">*************************** 3. row ***************************<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id: 2<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">select_type: MATERIALIZED<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 table: MyTable<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type: ref<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">possible_keys: OtherIDStageStatusInvisibleMainID, StageMainIDOtherID, &#8230;<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: OtherIDStageStatusInvisibleMainID<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 key_len: 5<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ref: const,const<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rows: 235<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0 filtered: 100.00<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">\u00a0\u00a0\u00a0\u00a0\u00a0 Extra: Using where; Using index; Distinct<\/span><\/address>\n<p dir=\"ltr\">La sous-requ\u00eate est<a href=\"https:\/\/kb.askmonty.org\/en\/non-semi-join-subquery-optimizations\/#materialization-for-non-correlated-in-subqueries\"> mat\u00e9rialis\u00e9e<\/a> et on a \u00e9galement une<a href=\"https:\/\/kb.askmonty.org\/en\/semi-join-subquery-optimizations\/\"> semi-jointure<\/a> entre cette derni\u00e8re est la table de la requ\u00eate externe (MyTable).<\/p>\n<h3 dir=\"ltr\">Optimizer rewriting<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">select distinct OtherID AS OtherID from MyTable <strong>semi join<\/strong> (MyTable)<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">where ((Status = 1) and (Stage = 4) and (Invisible = 0) and (OtherID = 2779262) and (Stage = 0) and (Invisible = 0)<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">and <strong>(MainID = MainID)<\/strong>)<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">L\u2019information sur la semi-join est explicite (<span style=\"color: #000080;\">MyTable <strong>semi join<\/strong> (MyTable)&#8230; <strong>(MainID = MainID)<\/strong><\/span>).<\/p>\n<p dir=\"ltr\">Par contre, pas de trace ici de la mat\u00e9rialisation&#8230;<\/p>\n<h3 dir=\"ltr\">Status<\/h3>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Variable_name \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_disk_tables | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Created_tmp_files \u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Created_tmp_tables \u00a0\u00a0\u00a0\u00a0 | 2 \u00a0\u00a0\u00a0 |<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_key_refills \u00a0\u00a0| 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_mrr_rowid_refills | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_prepare \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_first \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_key \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 237 \u00a0\u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_last \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_next \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 243 \u00a0\u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_prev \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_read_rnd_deleted \u00a0| 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_read_rnd_next \u00a0\u00a0\u00a0 | 245 \u00a0\u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><strong><span style=\"color: #000080;\">| Handler_tmp_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 243 \u00a0\u00a0|<\/span><\/strong><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">| Handler_write \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0 \u00a0\u00a0\u00a0 |<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #000080;\">+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+<\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Niveau handler, que des petites valeurs, \u00e7a sent bon la perf tout \u00e7a !!!<\/p>\n<p dir=\"ltr\">Grosso-modo, ces infos nous disent :<\/p>\n<ul>\n<li dir=\"ltr\">\n<p dir=\"ltr\">2 tables temporaires pour le distinct et la mat\u00e9rialisation : (<span style=\"color: #000080;\"><strong>Created_tmp_tables | 2<\/strong><\/span>)<\/p>\n<\/li>\n<\/ul>\n<ul>\n<li dir=\"ltr\">\n<address dir=\"ltr\">La semi-jointure \u00a0: (<span style=\"color: #000080;\"><strong>Handler_read_next | 243<\/strong><\/span>)<\/address>\n<\/li>\n<li dir=\"ltr\">\n<p dir=\"ltr\">La sous-requ\u00eate elle, est responsable de : (<span style=\"color: #000080;\"><strong>Handler_read_key | 237<\/strong><\/span>)<\/p>\n<\/li>\n<li dir=\"ltr\">Remplissage + lecture des tables temporaire : (<strong><span style=\"color: #000080;\">Handler_read_rnd_next | 245<\/span><\/strong> + <strong><span style=\"color: #000080;\">Handler_tmp_write | 243<\/span><\/strong>)<\/li>\n<\/ul>\n<h3 dir=\"ltr\">Bench<\/h3>\n<address dir=\"ltr\"><span style=\"color: #993300;\">daz@sql:~\/mariadb5.5.30$ mysqlslap &#8211;create-schema=test -c1 -i200 -q\u00a0\u00bbSELECT DISTINCT OtherID \u00a0FROM MyTable \u00a0WHERE Status = 1 \u00a0AND Stage = 4 \u00a0AND Invisible = 0 \u00a0AND MainID IN \u00a0( SELECT MainID \u00a0FROM MyTable \u00a0WHERE OtherID = 2779262 \u00a0AND Stage = 0 \u00a0AND Invisible = 0 \u00a0);\u00a0\u00bb<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\">Benchmark<\/span><\/address>\n<address dir=\"ltr\"><span style=\"color: #993300;\"><strong>\u00a0\u00a0Minimum number of seconds to run all queries: 0.026 seconds<\/strong><\/span><\/address>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">T4 est <strong>plus rapide que T1 de plus de 9 minutes<\/strong>.<\/p>\n<p dir=\"ltr\">T4 est <strong>plus rapide que T2 de plus de 7 minutes<\/strong>.<\/p>\n<p dir=\"ltr\">T4 est <strong>plus rapide que T3 de plus de 2 minutes<\/strong>.<\/p>\n<p dir=\"ltr\">En image, <span style=\"text-decoration: underline;\">en temps relatif<\/span> par rapport \u00e0 T1 (la requ\u00eate la plus longue) \u00e7a donne :<\/p>\n<p><img decoding=\"async\" alt=\"\" src=\"https:\/\/lh3.googleusercontent.com\/8zkRFWHP6EnBzoQbslcH8lk9Cq1TJ1NFMT7tjTsN8fgdk-UBh0vaRJqzEd3SbRjCCFV-jdUOoH1U_WUdKcI1nT1G_gz9HHpefwKCxWIMh_S55Gl2iumBn_gNRA\" width=\"648px;\" height=\"327px;\" \/><\/p>\n<p dir=\"ltr\">Avec les versions de MySQL \/ Percona server inf\u00e9rieures ou \u00e9gales \u00e0 5.5 et les versions de MariaDB inf\u00e9rieures ou \u00e9gales \u00e0 5.2 (de m\u00e9moire), les sous-requ\u00eates peuvent poser de gros probl\u00e8mes de performances. Il faut donc bien tester ces requ\u00eates avant de les passer en prod.<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">Une bonne raison suppl\u00e9mentaire pour tester (et migrer) vers<a href=\"https:\/\/downloads.mariadb.org\/\"> MariaDB 5.5<\/a> ou<a href=\"http:\/\/dev.mysql.com\/downloads\/mysql\/\"> MySQL 5.6<\/a> (qui poss\u00e8de \u00e9galement les optimisations semijoin et materialization).<\/p>\n<p>&nbsp;<\/p>\n<p dir=\"ltr\">A suivre, dans la deuxi\u00e8me partie de cet article : Index Condition Pushdown et Multi Range Read&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Comme promit, voici la suite de l\u2019article Utiliser une sous-requ\u00eate c\u2019est mal ?<\/p>\n<p>L\u2019id\u00e9e ici est de r\u00e9pondre aux interrogations de svar et d\u2019en profiter pour explorer les nouvelles possibilit\u00e9s de la variante stable de MySQL qui poss\u00e8de l\u2019optimiseur le plus avanc\u00e9, c\u2019est \u00e0 dire MariaDB 5.5.<\/p>\n<p>Pr\u00e9ambule<\/p>\n<p>En pr\u00e9-requis, je vous invite \u00e0 explorer la doc officielle de MySQL au sujet de la variable optimizer_switch, qui permet de s\u00e9lectionner les algorithmes d\u2019optimisation, ainsi que les diff\u00e9rentes subtilit\u00e9s impl\u00e9ment\u00e9es dans l\u2019optimiseur de MariaDB.<\/p>\n<p>La valeur par d\u00e9faut sur MariaDB 5.5.30 de la variable optimizer_switch est:<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":""},"categories":[167,8,34,181],"tags":[198,196,194,195,197],"class_list":["post-1425","post","type-post","status-publish","format-standard","hentry","category-mariadb","category-mysql","category-optimisation","category-percona-server-2","tag-loosescan","tag-materialisation","tag-optimiseur","tag-optimizer_switch","tag-semi-jointure"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-mZ","jetpack-related-posts":[{"id":1163,"url":"https:\/\/dasini.net\/blog\/2012\/01\/09\/ameliorations-de-loptimiseur-dans-mariadb\/","url_meta":{"origin":1425,"position":0},"title":"Am\u00e9liorations de l&rsquo;optimiseur dans MariaDB","author":"Olivier DASINI","date":"9 janvier 2012","format":false,"excerpt":"Les \u00e9quipes de MariaDB ont \u00e9norm\u00e9ment travaill\u00e9es sur l'optimiseur de la version 5.3, notamment en permettant une r\u00e9elle utilisation des sous-requ\u00eates. Voici un effet visuel de ces optimisations: Avec MySQL 5.5, l'utilisation de tables d\u00e9riv\u00e9es (type de sous-requ\u00eates dans la clause FROM d'un SELECT), donne le plan d'ex\u00e9cution suivant:","rel":"","context":"Dans &quot;bench&quot;","block_context":{"text":"bench","link":"https:\/\/dasini.net\/blog\/category\/bench\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1088,"url":"https:\/\/dasini.net\/blog\/2011\/07\/27\/mariadb-5-3-en-beta\/","url_meta":{"origin":1425,"position":1},"title":"MariaDB 5.3 en beta","author":"Olivier DASINI","date":"27 juillet 2011","format":false,"excerpt":"The MariaDB project would like to announce the availability of MariaDB 5.3.0 Beta, the latest addition to our growing lineup of supported software. MariaDB 5.3.0 beta includes all features in MariaDB 5.2 and is based on MySQL 5.1. Some highlights of new features include: - subquery optimizations that finally make\u2026","rel":"","context":"Dans &quot;MariaDB&quot;","block_context":{"text":"MariaDB","link":"https:\/\/dasini.net\/blog\/category\/mariadb\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1245,"url":"https:\/\/dasini.net\/blog\/2012\/04\/05\/mysql-5-6-rock-suite\/","url_meta":{"origin":1425,"position":2},"title":"MySQL 5.6 rock suite","author":"Olivier DASINI","date":"5 avril 2012","format":false,"excerpt":"Voici la suite du post MySQL 5.6 rock, dans lequel je test MySQL 5.5 & 5.6, MariaDB 5.3 & 5.5 et Percona server 5.5. Pour cet article, toujours un bench. Le contexte est assez proche, \u00e0 la diff\u00e9rence pr\u00e8s que cette fois les serveurs sont test\u00e9s en lecture (65%) et\u2026","rel":"","context":"Dans &quot;bench&quot;","block_context":{"text":"bench","link":"https:\/\/dasini.net\/blog\/category\/bench\/"},"img":{"alt_text":"dasini.net - 95 centile for R\/W i\/o bounds","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-includes\/images\/dasini.net_bench_mysql_95percentil.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1413,"url":"https:\/\/dasini.net\/blog\/2013\/04\/23\/utiliser-une-sous-requete-cest-mal\/","url_meta":{"origin":1425,"position":3},"title":"Utiliser une sous-requ\u00eate c&rsquo;est mal ?","author":"Olivier DASINI","date":"23 avril 2013","format":false,"excerpt":"Jusqu\u2019en MySQL 5.5 inclus, l\u2019utilisation de sous-requ\u00eates peut, dans certain cas, \u00eatre la cause de probl\u00e8mes de performances (l\u2019optimiseur est bien meilleur en MySQL 5.6, MariaDB 5.5 et MariaDB 10). R\u00e9cemment j\u2019ai eu un souci en prod, apr\u00e8s une MEP, avec une requ\u00eate qui durait en moyenne plus de 1000\u2026","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1227,"url":"https:\/\/dasini.net\/blog\/2012\/03\/30\/mysql-5-6-rocks\/","url_meta":{"origin":1425,"position":4},"title":"MySQL 5.6 rock !","author":"Olivier DASINI","date":"30 mars 2012","format":false,"excerpt":"Comme d'habitude, mon but n'est pas de conna\u00eetre les possibilit\u00e9s maximales du serveur (d'autres le font mieux que moi), mais plut\u00f4t d'avoir une id\u00e9e assez pr\u00e9cise de leurs comportements respectifs dans un environnement le plus proche possible de ma prod. pour ce test, les candidats sont, Percona 5.5, MariaDB 5.3\u2026","rel":"","context":"Dans &quot;bench&quot;","block_context":{"text":"bench","link":"https:\/\/dasini.net\/blog\/category\/bench\/"},"img":{"alt_text":"dasini.net - 95 centile","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-includes\/images\/percentil.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1363,"url":"https:\/\/dasini.net\/blog\/2013\/01\/08\/full-table-scan-vs-full-index-scan-part1-2\/","url_meta":{"origin":1425,"position":5},"title":"Full table scan vs Full index scan part1-2","author":"Olivier DASINI","date":"8 janvier 2013","format":false,"excerpt":"MySQL utilise un optimiseur \u00e0 base de co\u00fbts. Le plan d\u2019ex\u00e9cution de la requ\u00eate choisit est celui dont le co\u00fbt est le plus faible. Ce dernier peut \u00eatre visualis\u00e9 gr\u00e2ce \u00e0 la variable Last_query_cost. Son unit\u00e9 est le co\u00fbt (encore lui) des acc\u00e8s al\u00e9atoires en lecture de pages de 4ko.\u2026","rel":"","context":"Dans &quot;optimisation&quot;","block_context":{"text":"optimisation","link":"https:\/\/dasini.net\/blog\/category\/optimisation\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1425","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/comments?post=1425"}],"version-history":[{"count":11,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1425\/revisions"}],"predecessor-version":[{"id":1436,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1425\/revisions\/1436"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=1425"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=1425"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=1425"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}