
{"id":453,"date":"2009-02-18T23:19:25","date_gmt":"2009-02-18T22:19:25","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=453"},"modified":"2011-07-19T15:00:38","modified_gmt":"2011-07-19T14:00:38","slug":"optimisation-de-requetes-comprendre-loptimiseur-de-mysql","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2009\/02\/18\/optimisation-de-requetes-comprendre-loptimiseur-de-mysql\/","title":{"rendered":"Optimisation de requ\u00eates: comprendre l&rsquo;optimiseur de MySQL"},"content":{"rendered":"<p><!-- \t \t --><br \/>\nLe but de cet article est d&rsquo;optimiser une simple requ\u00eate\u00a0 (<strong>SELECT avg(Population) FROM city GROUP BY CountryCode<\/strong>) et surtout de comprendre comment l&rsquo;optimiseur proc\u00e8de, en \u00e9tudiant les r\u00e9sultats donn\u00e9s par les variables qui permettent de surveiller le serveur MySQL.<\/p>\n<p>Le sch\u00e9ma utilis\u00e9 est le sch\u00e9ma <em>world <\/em>t\u00e9l\u00e9chargeable <a title=\"world database\" href=\"http:\/\/dev.mysql.com\/doc\/#sampledb\" target=\"_blank\">ici<\/a><br \/>\n<!-- \t \t --><br \/>\nVoici la structure de la table <em>city<\/em>:<\/p>\n<address>SHOW CREATE TABLE city\\G<br \/>\n*************************** 1. row ***************************<br \/>\nTable: city<br \/>\nCreate Table: CREATE TABLE city (<br \/>\nID int(11) NOT NULL AUTO_INCREMENT,<br \/>\nName char(35) NOT NULL DEFAULT \u00a0\u00bb,<br \/>\nCountryCode char(3) NOT NULL DEFAULT \u00a0\u00bb,<br \/>\nDistrict char(20) NOT NULL DEFAULT \u00a0\u00bb,<br \/>\nPopulation int(11) NOT NULL DEFAULT &lsquo;0&rsquo;,<br \/>\nPRIMARY KEY (ID)<br \/>\n) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<br \/>\n<!-- \t \t --><br \/>\n<!-- \t \t --><br \/>\n<\/address>\n<p>La commande <strong>EXPLAIN<\/strong>, permet d&rsquo;avoir le plan d&rsquo;ex\u00e9cution:<\/p>\n<address>EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\\G<br \/>\n*************************** 1. row ***************************<br \/>\nid: 1<br \/>\nselect_type: SIMPLE<br \/>\ntable: c<br \/>\n<span style=\"color: #ff0000;\"><strong>type: ALL<\/strong><\/span><br \/>\npossible_keys: NULL<br \/>\n<strong><span style=\"color: #ff0000;\"> key: NULL<\/span><\/strong><br \/>\nkey_len: NULL<br \/>\nref: NULL<br \/>\nrows: 4079<br \/>\n<span style=\"color: #ff0000;\"><strong> Extra: Using temporary; Using filesort<\/strong><\/span><br \/>\n<!-- \t \t --><br \/>\n<\/address>\n<p>L&rsquo;optimiseur fait un full table scan (<strong>type: ALL<\/strong>) ce qui n&rsquo;est pas forcement une bonne nouvelle. De plus aucun index n&rsquo;est utilis\u00e9 (<strong>key: NULL<\/strong>), ce qui est logique car la table n&rsquo;en contient pas (sic !)<br \/>\n<strong>Using temporary; Using filesort<\/strong> indiquent la cr\u00e9ation d&rsquo;un table temporaire et le tri des donn\u00e9es (pas tr\u00e8s bon pour les performances surtout si la table temporaire est cr\u00e9\u00e9e sur le disque)<br \/>\n<!-- \t \t --><br \/>\nLa variable <strong>Last_query_cost<\/strong> permet de r\u00e9cup\u00e9rer le co\u00fbt de la requ\u00eate:<\/p>\n<address>SHOW STATUS LIKE &lsquo;Last_query_cost&rsquo;\\G<\/address>\n<p>*************************** 1. row ***************************<br \/>\nVariable_name: Last_query_cost<br \/>\nValue: 4963.520924<br \/>\n<!-- \t \t --><br \/>\nAjoutons un index sur la colonne <em>countrycode<\/em>:<\/p>\n<address>ALTER TABLE city ADD INDEX Idx_cc(CountryCode);<\/address>\n<p><!-- \t \t --><\/p>\n<address>SHOW CREATE TABLE city\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> Table: city<\/address>\n<address>Create Table: CREATE TABLE city (<\/address>\n<address> ID int(11) NOT NULL AUTO_INCREMENT,<\/address>\n<address> Name char(35) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> CountryCode char(3) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> District char(20) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> Population int(11) NOT NULL DEFAULT &lsquo;0&rsquo;,<\/address>\n<address> PRIMARY KEY (ID),<\/address>\n<address><strong> KEY Idx_cc (CountryCode)<\/strong><\/address>\n<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<\/address>\n<p><!-- \t \t --><\/p>\n<address>EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> id: 1<\/address>\n<address> select_type: SIMPLE<\/address>\n<address> table: city<\/address>\n<address> type: ALL<\/address>\n<address>possible_keys: NULL<\/address>\n<address><strong><span style=\"color: #ff0000;\"> key: NULL<\/span><\/strong><\/address>\n<address> key_len: NULL<\/address>\n<address> ref: NULL<\/address>\n<address> rows: 4079<\/address>\n<address> Extra: Using temporary; Using filesort<\/address>\n<p><!-- \t \t --><br \/>\n<!-- \t \t --><br \/>\nL&rsquo;index <em>Idx_cc<\/em> ne <span style=\"text-decoration: underline;\">sert \u00e0 rien<\/span><br \/>\n<!-- \t \t --><br \/>\nAjoutons alors un index sur la colonne <em>population <\/em><\/p>\n<address>ALTER TABLE city ADD INDEX Idx_population(Population);<\/address>\n<p><!-- \t \t --><\/p>\n<address> SHOW CREATE TABLE city\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> Table: city<\/address>\n<address>Create Table: CREATE TABLE city (<\/address>\n<address> ID int(11) NOT NULL AUTO_INCREMENT,<\/address>\n<address> Name char(35) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> CountryCode char(3) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> District char(20) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> Population int(11) NOT NULL DEFAULT &lsquo;0&rsquo;,<\/address>\n<address> PRIMARY KEY (ID),<\/address>\n<address> KEY Idx_cc (CountryCode),<\/address>\n<address><strong> KEY Idx_population (Population)<\/strong><\/address>\n<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<\/address>\n<address>1 row in set (0.00 sec)<\/address>\n<p><!-- \t \t --><\/p>\n<address> EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> id: 1<\/address>\n<address> select_type: SIMPLE<\/address>\n<address> table: c<\/address>\n<address> type: ALL<\/address>\n<address>possible_keys: NULL<\/address>\n<address><strong><span style=\"color: #ff0000;\"> key: NULL<\/span><\/strong><\/address>\n<address> key_len: NULL<\/address>\n<address> ref: NULL<\/address>\n<address> rows: 4079<\/address>\n<address> Extra: Using temporary; Using filesort<\/address>\n<p><!-- \t \t --><br \/>\nPas mieux ! L&rsquo;index <em>Idx_population<\/em> ne sert \u00e0 rien.<br \/>\n<!-- \t \t --><br \/>\nEffacement des 2 index:<\/p>\n<address>ALTER TABLE city DROP INDEX Idx_cc, DROP INDEX Idx_population;<\/address>\n<p><!-- \t \t --><!-- \t \t --><br \/>\nAjoutons un index composite sur les colonnes <em>population, countrycode<\/em>:<\/p>\n<address>ALTER TABLE city ADD INDEX Idx_population_cc(Population, CountryCode);<\/address>\n<p><!-- \t \t --><\/p>\n<address> SHOW CREATE TABLE city\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> Table: city<\/address>\n<address>Create Table: CREATE TABLE `city` (<\/address>\n<address> `ID` int(11) NOT NULL AUTO_INCREMENT,<\/address>\n<address> `Name` char(35) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `CountryCode` char(3) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `District` char(20) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `Population` int(11) NOT NULL DEFAULT &lsquo;0&rsquo;,<\/address>\n<address> PRIMARY KEY (`ID`),<\/address>\n<address><strong> KEY `Idx_population_cc` (`Population`,`CountryCode`)<\/strong><\/address>\n<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<\/address>\n<p><!-- \t \t --><\/p>\n<address> EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> id: 1<\/address>\n<address> select_type: SIMPLE<\/address>\n<address> table: c<\/address>\n<address> type: index<\/address>\n<address>possible_keys: NULL<\/address>\n<address><span style=\"color: #ff0000;\"><strong> key: Idx_population_cc<\/strong><\/span><\/address>\n<address> key_len: 7<\/address>\n<address> ref: NULL<\/address>\n<address> rows: 4079<\/address>\n<address><span style=\"color: #ff0000;\"><strong> Extra: Using index; Using temporary; Using filesort<\/strong><\/span><\/address>\n<p><!-- \t \t --><br \/>\n<!-- \t \t --><br \/>\n\u00c7aparait un peu meilleur, l&rsquo;index<em> Idx_population_cc<\/em> est utilis\u00e9.<br \/>\n<strong>Extra: using index<\/strong> nous indique que MySQL utilise un index couvrant (covering index), c&rsquo;est \u00e0 dire que l&rsquo;information est enti\u00e8rement  accessible en parcourant l&rsquo;index (pas d&rsquo;acc\u00e8s aux donn\u00e9es).<br \/>\nDe\u00a0 plus l&rsquo;optimiseur fait un full index scan:\u00a0 <strong>type: index<\/strong><br \/>\n<!-- \t \t --><br \/>\nVoyons le co\u00fbt de la requ\u00eate:<\/p>\n<address>SHOW STATUS LIKE &lsquo;Last_query_cost&rsquo;\\G<\/address>\n<address>*************************** 1. row ***************************<br \/>\nVariable_name: Last_query_cost<br \/>\nValue: 4963.520924<\/address>\n<p><!-- \t \t --><br \/>\nLa valeur de (l&rsquo;obscure) Last_query_cost est cependant le m\u00eame que pour les requ\u00eates pr\u00e9c\u00e9dentes&#8230;<br \/>\n<!-- \t \t --><br \/>\nAjoutons un index composite sur les colonnes <em>countrycode,population<\/em><\/p>\n<address> <\/address>\n<address>ALTER TABLE test2.city ADD INDEX Idx_cc_population(CountryCode,Population);<\/address>\n<p><!-- \t \t --><\/p>\n<address> SHOW CREATE TABLE city\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> Table: city<\/address>\n<address>Create Table: CREATE TABLE `city` (<\/address>\n<address> `ID` int(11) NOT NULL AUTO_INCREMENT,<\/address>\n<address> `Name` char(35) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `CountryCode` char(3) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `District` char(20) NOT NULL DEFAULT \u00a0\u00bb,<\/address>\n<address> `Population` int(11) NOT NULL DEFAULT &lsquo;0&rsquo;,<\/address>\n<address> PRIMARY KEY (`ID`),<\/address>\n<address> KEY `Idx_population_cc` (`Population`,`CountryCode`),<\/address>\n<address><strong> KEY `Idx_cc_population` (`CountryCode`,`Population`)<\/strong><\/address>\n<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<\/address>\n<address> <\/address>\n<p><!-- \t \t --><\/p>\n<address> EXPLAIN SELECT AVG(Population) FROM city GROUP BY CountryCode\\G<\/address>\n<address>*************************** 1. row ***************************<\/address>\n<address> id: 1<\/address>\n<address> select_type: SIMPLE<\/address>\n<address> table: c<\/address>\n<address> type: index<\/address>\n<address>possible_keys: NULL<\/address>\n<address><span style=\"color: #ff0000;\"><strong> key: Idx_cc_population<\/strong><\/span><\/address>\n<address> key_len: 7<\/address>\n<address> ref: NULL<\/address>\n<address> rows: 4079<\/address>\n<address><span style=\"color: #ff0000;\"><strong> Extra: Using index<\/strong><\/span><\/address>\n<address>1 row in set (0.00 sec)<\/address>\n<address> <\/address>\n<p><!-- \t \t --><\/p>\n<address>SHOW STATUS LIKE &lsquo;Last_query_cost&rsquo;\\G<\/address>\n<address>*************************** 1. row ***************************<br \/>\nVariable_name: Last_query_cost<br \/>\nValue: 4963.520924<\/address>\n<p><!-- \t \t --><br \/>\nL&rsquo;optimiseur estime que le co\u00fbt de la requ\u00eate est toujours le m\u00eame, cependant l&rsquo;index <strong>Idx_cc_population(CountryCode,Population)<\/strong> <span style=\"text-decoration: underline;\">optimise les performance de notre requ\u00eate<\/span> car il n&rsquo;y a plus de <strong>Using temporary<\/strong> ni de <strong>Using filesort<\/strong>.<br \/>\nIl suffit de pousser encore un peu plus loin notre analyse pour en \u00eatre d\u00e9finitivement (?) convaincu&#8230;<br \/>\n<!-- \t \t --><br \/>\nInitialisation des variables de sessions<\/p>\n<address> FLUSH STATUS;<\/address>\n<p><!-- \t \t --><!-- \t \t --><br \/>\nEn utilisant l&rsquo;index <strong>idx_population_cc<\/strong> (le mauvais index)<\/p>\n<address> SELECT AVG(Population) FROM city use index(idx_population_cc) GROUP BY CountryCode;<\/address>\n<address> &#8230;<\/address>\n<address><span style=\"color: #ff0000;\"><strong> 232 rows in set (0.00 sec)<\/strong><\/span><\/address>\n<p><!-- \t \t --><\/p>\n<address> <\/address>\n<address>SHOW STATUS LIKE &lsquo;handler%&rsquo;;<br \/>\n<\/address>\n<pre> +----------------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +----------------------------+-------+<\/pre>\n<pre> | Handler_commit\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_delete\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_discover\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_prepare\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_first\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 1<\/strong><\/span>\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_key\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 4079<\/strong><\/span>\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_next\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 4079<\/strong><\/span>\u00a0 |<\/pre>\n<pre> | Handler_read_prev\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_rnd\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 232<\/strong><\/span>\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_rnd_next\u00a0\u00a0\u00a0\u00a0\u00a0 | 233<\/strong><\/span>\u00a0\u00a0 |<\/pre>\n<pre> | Handler_rollback\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_savepoint\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_savepoint_rollback | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_update\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 3847<\/strong><\/span>\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_write\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 232<\/strong><\/span>\u00a0\u00a0 |<\/pre>\n<pre> +----------------------------+-------+<\/pre>\n<p><!-- \t \t --><br \/>\nLes handler sont des indicateurs li\u00e9s au moteur de stockage.<br \/>\nHandler_read_first comptabilise le nombre de fois que la premi\u00e8re valeur de l&rsquo;index est lue.<br \/>\nHandler_read_key indique le nombre d&rsquo;enregistrement r\u00e9cup\u00e9r\u00e9 gra\u00e7e \u00e0 l&rsquo;index.<br \/>\nHandler_read_next indique une lecture ordonn\u00e9e de l&rsquo;index (une valeur, puis la suivante, puis la suivante&#8230;).<br \/>\n<strong> Handler_read_first, Handler_read_key &amp;\u00a0 Handler_read_next, indiquent l\u00e0, un <span style=\"color: #ff0000;\">full index scan<\/span><\/strong><br \/>\n<!-- \t \t --><br \/>\n<strong>Handler_read_rnd &amp; Handler_read_rnd_next indiquent un<span style=\"color: #ff0000;\"> full table scan sur la table temporaire<\/span><\/strong><br \/>\n<!-- \t \t --><br \/>\nHandler_update nous donne une indication sur le nombre de mise \u00e0 jours dans la table temporaire (\u00e0 cause du tri)<br \/>\nHandler_write indique le nombre de lignes ins\u00e9r\u00e9es dans la table temporaire<br \/>\n<!-- \t \t --><br \/>\nCes 2 derniers param\u00e8tres confirme donc la<span style=\"color: #ff0000;\"><strong> cr\u00e9ation de la table temporaire et l&rsquo;op\u00e9ration de tri<\/strong><\/span><br \/>\n<!-- \t \t --><!-- \t \t --><br \/>\nPour avoir plus d&rsquo;informations sur le tri:<\/p>\n<address>SHOW SESSION STATUS LIKE &lsquo;sort%&rsquo;;<br \/>\n<\/address>\n<pre> +-------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +-------------------+-------+<\/pre>\n<pre> | Sort_merge_passes | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Sort_range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Sort_rows\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 232<\/strong><\/span>\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Sort_scan\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 1<\/strong><\/span>\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> +-------------------+-------+<\/pre>\n<p><!-- \t \t --><br \/>\nSort_rows: nombre de lignes tri\u00e9es (nombre d&rsquo;enregistrements de la table temporaire).<br \/>\nSort_scan: nombre de tri.<br \/>\n<!-- \t \t --><!-- \t \t --><\/p>\n<address>SHOW SESSION STATUS LIKE &lsquo;created%&rsquo;;<br \/>\n<\/address>\n<pre> +-------------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +-------------------------+-------+<\/pre>\n<pre> | Created_tmp_disk_tables | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Created_tmp_files\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Created_tmp_tables\u00a0\u00a0\u00a0\u00a0\u00a0 | 1<\/strong><\/span>\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> +-------------------------+-------+<\/pre>\n<p><!-- \t \t --><br \/>\nCreated_tmp_tables: nombre de table temporaire cr\u00e9e. La bonne nouvelle est que la table temporaire est cr\u00e9ee en m\u00e9moire (<strong>Created_tmp_disk_tables=0<\/strong>)<\/p>\n<p><!-- \t \t --><!-- \t \t --><br \/>\nr\u00e9initialise les variables de sessions<\/p>\n<address>FLUSH STATUS;<br \/>\n<!-- \t \t --><!-- \t \t --><br \/>\n<\/address>\n<p>En utilisant l&rsquo;index <strong>idx_cc_population<\/strong> (le bon index)<\/p>\n<address><\/address>\n<address> SELECT AVG(Population) FROM city use index(idx_population_cc) GROUP BY CountryCode;<\/address>\n<address> &#8230;<\/address>\n<address><span style=\"color: #ff0000;\"><strong> 232 rows in set (0.00 sec)<\/strong><\/span><\/address>\n<address><span style=\"color: #ff0000;\"><strong><br \/>\n<\/strong><\/span><\/address>\n<p><!-- \t \t --><\/p>\n<address> SHOW STATUS LIKE &lsquo;handler%&rsquo;;<\/address>\n<p><!-- \t \t --><\/p>\n<pre> +----------------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +----------------------------+-------+<\/pre>\n<pre> | Handler_commit\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_delete\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_discover\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_prepare\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_first\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 1<\/strong><\/span>\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_read_key\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | <span style=\"color: #ff0000;\"><strong>Handler_read_next\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 4079<\/strong><\/span>\u00a0 |<\/pre>\n<pre> | Handler_read_prev\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_read_rnd\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_read_rnd_next\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_rollback\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_savepoint\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_savepoint_rollback | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_update\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Handler_write\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> +----------------------------+-------+<\/pre>\n<p><!-- \t \t --><br \/>\nHandler_read_first &amp;\u00a0 Handler_read_next valident le full index scan et puis c&rsquo;est tout \ud83d\ude42<br \/>\n<!-- \t \t --><!-- \t \t --><\/p>\n<address>SHOW SESSION STATUS LIKE &lsquo;sort%&rsquo;;<br \/>\n<\/address>\n<pre> +-------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +-------------------+-------+<\/pre>\n<pre> | Sort_merge_passes | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Sort_range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Sort_rows\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Sort_scan\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> +-------------------+-------+<\/pre>\n<p><!-- \t \t --><br \/>\npas de tri<br \/>\n<!-- \t \t --><!-- \t \t --><\/p>\n<address>SHOW SESSION STATUS LIKE &lsquo;created%&rsquo;;<br \/>\n<\/address>\n<pre> +-------------------------+-------+<\/pre>\n<pre> | Variable_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | Value |<\/pre>\n<pre> +-------------------------+-------+<\/pre>\n<pre> | Created_tmp_disk_tables | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Created_tmp_files\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> | Created_tmp_tables\u00a0\u00a0\u00a0\u00a0\u00a0 | 0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<pre> +-------------------------+-------+<\/pre>\n<p><!-- \t \t -->pas de table(s) temporaire(s)<br \/>\n<!-- \t \t --><!-- \t \t --><br \/>\ncqfd<br \/>\n<!-- \t \t --><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Le but de cet article est d&rsquo;optimiser une simple requ\u00eate  (SELECT avg(Population) FROM city GROUP BY CountryCode) et surtout de comprendre comment l&rsquo;optimiseur proc\u00e8de, en \u00e9tudiant les r\u00e9sultats donn\u00e9s par les variables qui permettent de surveiller le serveur MySQL.<\/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":[8,34],"tags":[101,245],"class_list":["post-453","post","type-post","status-publish","format-standard","hentry","category-mysql","category-optimisation","tag-group-by","tag-optimisation"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-7j","jetpack-related-posts":[{"id":237,"url":"https:\/\/dasini.net\/blog\/2008\/11\/28\/les-nouveautes-de-mysql-51-part-15\/","url_meta":{"origin":453,"position":0},"title":"Les nouveaut\u00e9s de MySQL 5.1 &#8212; (part 1\/5)","author":"Olivier DASINI","date":"28 novembre 2008","format":false,"excerpt":"Que de chemin parcouru depuis ce 5 juillet 1999, date de lancement de MySQL 3.23.0. En plus des objectifs de simplicit\u00e9 d'administration, de hautes performances et de fiabilit\u00e9, se sont greff\u00e9es, au fur et \u00e0 mesure, les fonctionnalit\u00e9s les plus demand\u00e9es par les utilisateurs. La nouvelle version du SGBDR open-source\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":56,"url":"https:\/\/dasini.net\/blog\/2008\/11\/02\/auto_increment-differences-myisam-innodb\/","url_meta":{"origin":453,"position":1},"title":"AUTO_INCREMENT: Diff\u00e9rences MyISAM &#8211; InnoDB","author":"Olivier DASINI","date":"2 novembre 2008","format":false,"excerpt":"La clause, AUTO_INCREMENT, permet \u00e0 MySQL de g\u00e9n\u00e9rer un entier unique pour tout nouvel enregistrement d'une table. Cette clause ne peut se mettre que sur les champs de type entier, index\u00e9 et non nul. Elle est donc souvent utilis\u00e9e comme cl\u00e9 primaire. Cependant, sont comportement n'est pas tout \u00e0 fait\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":1163,"url":"https:\/\/dasini.net\/blog\/2012\/01\/09\/ameliorations-de-loptimiseur-dans-mariadb\/","url_meta":{"origin":453,"position":2},"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":1256,"url":"https:\/\/dasini.net\/blog\/2012\/05\/15\/jointure-vs-sous-requete\/","url_meta":{"origin":453,"position":3},"title":"Jointure vs sous-requ\u00eate","author":"Olivier DASINI","date":"15 mai 2012","format":false,"excerpt":"MySQL est connu pour ne pas \u00eatre tr\u00e8s performant avec les sous-requ\u00eates. Ce n'est pas faux, et d'ailleurs c'est encore le cas avec MySQL 5.5. Le contournement consiste en g\u00e9n\u00e9ral \u00e0 r\u00e9\u00e9crire la requ\u00eate, certaines sous-requ\u00eates pouvant \u00eatre ais\u00e9ment r\u00e9\u00e9crite en jointure. C'est le cas de SELECT a FROM T1\u2026","rel":"","context":"Dans &quot;Astuce&quot;","block_context":{"text":"Astuce","link":"https:\/\/dasini.net\/blog\/category\/astuce\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":430,"url":"https:\/\/dasini.net\/blog\/2009\/01\/29\/influencer-loptimiseur-de-mysql\/","url_meta":{"origin":453,"position":4},"title":"Influencer l&rsquo;optimiseur de MySQL","author":"Olivier DASINI","date":"29 janvier 2009","format":false,"excerpt":"Il est possible d'influencer l'optimiseur pour qu'il choisisse d'utiliser ou de ne pas utiliser un index particulier. Les clauses \u00e0 placer dans votre requ\u00eate SELECT sont les suivantes: USE INDEX : utilise l'index pass\u00e9 en argument (MySQL ne l'utilisera pas si l'index est plus couteux qu'un full table scan) FORCE\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":403,"url":"https:\/\/dasini.net\/blog\/2009\/01\/13\/mysql-5-les-vues-part-77\/","url_meta":{"origin":453,"position":5},"title":"MySQL 5 : Les vues &#8212; (part 7\/7)","author":"Olivier DASINI","date":"13 janvier 2009","format":false,"excerpt":"Conserver la structure d'une table si elle doit \u00eatre modifi\u00e9e La probl\u00e9matique est de mettre \u00e0 jour le sch\u00e9ma de l'application en changeant la structure de certaines tables.","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":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/453","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=453"}],"version-history":[{"count":12,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/453\/revisions"}],"predecessor-version":[{"id":1080,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/453\/revisions\/1080"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}