<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>dasini.net - Journal d'un expert MySQL &#187; optimisation</title>
	<atom:link href="http://dasini.net/blog/category/optimisation/feed/" rel="self" type="application/rss+xml" />
	<link>http://dasini.net/blog</link>
	<description>Repousser les frontières de la connaissance</description>
	<lastBuildDate>Thu, 02 Feb 2012 09:06:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Meetup MySQL Viadeo / LeMUG.fr, les photos</title>
		<link>http://dasini.net/blog/2011/12/07/meetup-mysql-viadeo-lemug-fr-les-photos/</link>
		<comments>http://dasini.net/blog/2011/12/07/meetup-mysql-viadeo-lemug-fr-les-photos/#comments</comments>
		<pubDate>Wed, 07 Dec 2011 18:03:38 +0000</pubDate>
		<dc:creator>freshdaz</dc:creator>
				<category><![CDATA[Conférence]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimisation]]></category>
		<category><![CDATA[Présentation]]></category>

		<guid isPermaLink="false">http://dasini.net/blog/?p=1149</guid>
		<description><![CDATA[Vous pouvez consulter les photos du meetup Viadeo/LeMUG sur le compte FB du MUG:

https://www.facebook.com/media/set/?set=a.10150393843136937.345851.46154571936&#038;type=3

]]></description>
			<content:encoded><![CDATA[<p>Vous pouvez consulter les photos du <a title="Meetup MySQL Viadeo / LeMUG.fr à Paris" href="http://dasini.net/blog/2011/10/20/meetup-mysql-viadeo-lemug-fr-a-paris/" target="_blank">meetup Viadeo/LeMUG</a> sur le compte FB du MUG:</p>
<p><a href="https://www.facebook.com/media/set/?set=a.10150393843136937.345851.46154571936&amp;type=3">https://www.facebook.com/media/set/?set=a.10150393843136937.345851.46154571936&amp;type=3</a></p>
<p>&nbsp;</p>
<p>A venir les vidéos</p>
<p>enjoy</p>
]]></content:encoded>
			<wfw:commentRss>http://dasini.net/blog/2011/12/07/meetup-mysql-viadeo-lemug-fr-les-photos/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Un disque SSD comme buffer pour InnoDB</title>
		<link>http://dasini.net/blog/2011/02/01/un-disque-ssd-comme-buffer-pour-innodb/</link>
		<comments>http://dasini.net/blog/2011/02/01/un-disque-ssd-comme-buffer-pour-innodb/#comments</comments>
		<pubDate>Tue, 01 Feb 2011 18:25:41 +0000</pubDate>
		<dc:creator>freshdaz</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimisation]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://dasini.net/blog/?p=1044</guid>
		<description><![CDATA[MySQL, la base de données open source la plus populaire, inspire toujours autant les développeurs. David, propose un patch qui permet de créer un buffer pool supplémentaire pour InnoDB, qui est stocké sur un disque SSD ou de la mémoire flash.

Cette fonctionnalité créée un thread qui en tache de fond récupère les pages de données virées du buffer pool pour les copier dans le buffer pool supplémentaire du SSD au lieu du disque classique. L'idée étant d'éviter les accès au disque classique ( beaucoup plus lents notamment lors d'accès aléatoir?es).

De plus selon ses tests, ?????? les résultats restent également bien meilleurs avec une configuration SSD et un seul buffer pool.

Sysbench ]]></description>
			<content:encoded><![CDATA[<p>MySQL, la base de données open source la plus populaire, inspire toujours autant les développeurs. <a title="David Tools" href="http://code.google.com/p/david-mysql-tools/" target="_blank">David</a>, propose un patch qui permet de créer un <a title="InnoDB buffer pool" href="http://dev.mysql.com/doc/refman/5.5/en/innodb-buffer-pool.html" target="_blank">buffer pool</a> supplémentaire pour InnoDB, qui est stocké sur un disque SSD ou de la mémoire flash.</p>
<p>Cette fonctionnalité créée un thread qui en tache de fond récupère les pages de données virées du buffer pool pour les copier dans le buffer pool supplémentaire du SSD au lieu du disque classique. L&#8217;idée étant d&#8217;éviter les accès au disque classique ( beaucoup plus lents notamment lors d&#8217;accès aléatoires).</p>
<p>De plus selon ses tests, les résultats restent également bien meilleurs avec une configuration SSD et un seul buffer pool.</p>
<h1>Sysbench OLTP benchmark</h1>
<p>Server: Intel(R) Xeon Quad-Core E5405 2.00GHz X 2<br />
Database: ~19G ( created by sysbench, row: 80000000 )<br />
disk: 4 disk RAID 10 flash storage: Intel X25-M 80G SSD<br />
test command:<br />
sysbench &#8211;test=oltp &#8211;oltp-table-size=80000000 &#8211;oltp-read-only=off &#8211;init-rng=on &#8211;num-threads=16 &#8211;max-requests=0 &#8211;oltp-dist-type=uniform &#8211;max-time=7200 &#8211;mysql-user=root &#8211;mysql-socket=/tmp/mysql.sock &#8211;db-driver=mysql run</p>
<table>
<tbody>
<tr>
<td><strong>innodb_buffer_pool</strong></td>
<td><strong>innodb_secondary_buffer_pool</strong></td>
<td><strong>read-write(tps)</strong></td>
</tr>
<tr>
<td>8G</td>
<td>0G</td>
<td>55.03</td>
</tr>
<tr>
<td>8G(sbtest on SSD)</td>
<td>0G</td>
<td>60.8</td>
</tr>
<tr>
<td>8G</td>
<td>30G</td>
<td>86.23</td>
</tr>
</tbody>
</table>
<p><a title="innodb_secondary_buffer_pool" href="http://code.google.com/p/david-mysql-tools/wiki/innodb_secondary_buffer_pool" target="_blank">la suite&#8230;</a></p>
<p>A tester <img src='http://dasini.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://dasini.net/blog/2011/02/01/un-disque-ssd-comme-buffer-pour-innodb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL Query cache</title>
		<link>http://dasini.net/blog/2009/10/12/mysql-query-cache/</link>
		<comments>http://dasini.net/blog/2009/10/12/mysql-query-cache/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 15:21:50 +0000</pubDate>
		<dc:creator>freshdaz</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimisation]]></category>
		<category><![CDATA[query cache]]></category>

		<guid isPermaLink="false">http://dasini.net/blog/?p=788</guid>
		<description><![CDATA[Le cache est toujours à jour car en cas de modification d'une table, toutes les requêtes en relations avec cette table sont invalidées.


Le cache de requêtes est  en général utile lorsque:
Les modifications sur les tables ne sont pas très fréquentes
Beaucoup de requêtes de lectures identiques
Utilisation de tables MyISAM. Moins intéressant pour InnoDB]]></description>
			<content:encoded><![CDATA[<p><!-- 		@page { margin: 2cm } 		P { margin-bottom: 0.21cm } 		A:link { so-language: zxx } --></p>
<p style="margin-bottom: 0cm;">En tant que boulimique  de  MySQL, je me promène souvent sur la toile à la recherche d&#8217;informations, de bonnes et de moins bonnes&#8230;</p>
<p style="margin-bottom: 0cm;">Je suis tombé sur un article traitant du cache de requêtes de MySQL (<a title="noidea.ca - Patrick Lafontaine" href="http://www.noidea.ca/2009/10/10/mysql-query-cache/" target="_blank">MySQL Query Cache</a>) sur le blogue de<a title="noidea.ca - Patrick Lafontaine" href="http://www.noidea.ca/2009/10/10/mysql-query-cache/" target="_blank"> Patrick Lafontaine</a> (http://www.noidea.ca/)<strong><br />
</strong></p>
<p style="margin-bottom: 0cm;">Je me permet de faire quelques commentaires ici.</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">En préambule, quelques informations nécessaires sur le cache de requêtes:</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Système de cache interne à MySQL qui ne stocke que les requêtes <strong>SELECT</strong> et leur résultat ie pas d&#8217;INSERT, UPDATE, DELETE&#8230;</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Les requêtes ( SELECT donc)  doivent être <strong>strictement identiques</strong> ie même casse, mêmes espaces entre les mots,&#8230;</p>
<p style="margin-bottom: 0cm;"><strong>Ex 3 requêtes différentes pour le cache</strong> :</p>
<p style="margin-bottom: 0cm;">
<address style="margin-bottom: 0cm;">SELECT nom, prenom FROM client WHERE client_id=123;</address>
<address style="margin-bottom: 0cm;">
</address>
<address style="margin-bottom: 0cm;">select nom, prenom FROM client WHERE client_id=123; /* la casse du select*/</address>
<address style="margin-bottom: 0cm;">
</address>
<address style="margin-bottom: 0cm;">SELECT nom, prenom FROM client                 WHERE client_id=123; /*plusieurs espaces entre client et WHERE*/</address>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Le cache est <strong>toujours à jour</strong> car en cas de modification d&#8217;une table, toutes les requêtes en relations avec cette table sont invalidées.</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Le cache de requêtes est  en général utile lorsque:</p>
<ul>
<li>Les modifications sur les tables 	ne sont pas très fréquentes</li>
<li>Beaucoup de requêtes de lectures 	identiques</li>
<li>Utilisation de tables MyISAM. 	Moins intéressant pour InnoDB</li>
</ul>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Pour rebondir sur l&#8217;article de Patrick Lafontaine</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><em>« puisqu’elle est activée par défaut.</em> »</p>
<p style="margin-bottom: 0cm;">Le cache de requêtes n&#8217;est pas activé par défaut, car la variable <strong>query_cache_size</strong> vaut <strong>0</strong>. Si vous voulez vous en servir, il faut lui donner une taille en octet. Mettez le tout dans votre fichier de configuration. Assurez vous également que la variable  <strong>query_cache_type</strong> est différente de <strong>OFF</strong></p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><em>« c’est-à-dire que la ou les applications qui s’en servent n’ont pas besoin d’être modifiées »</em></p>
<p style="margin-bottom: 0cm;">Pour une optimisation optimale, il est parfois nécessaire de modifier les requêtes SELECT avec <strong>SQL_CACHE</strong> ou <strong>SQL_NO_CACHE</strong>. On choisit alors quelles requêtes seront mis en cache.</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">«<em> Si quelqu’un modifie la valeur directement dans MySQL, la cache possèdera la vieille valeur jusqu’à ce qu’un processus l’invalide</em>. »</p>
<p style="margin-bottom: 0cm;">Sur une machine de production, ce type de manipulation reste quand même exceptionnel, sinon c&#8217;est qu&#8217;il y a des choses à revoir dans les process.</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">« <em>Puisque les données ne changent pratiquement jamais, je ne me casserais pas la tête à réinventer la roue. MySQL fait déjà pour vous ce que APC ferait, sans le moindre effort.</em> »</p>
<p style="margin-bottom: 0cm;">Si le contenu ne change JAMAIS, il n&#8217;a à priori rien à faire en base ! Il vaut mieux utiliser un menu statique et laisser la base faire son boulot avec du contenu dynamique. Dans le même ordre d&#8217;idée, plus le cache est éloigné du disque plus il est performant. En d&#8217;autres termes, le goulet d&#8217;étranglement est souvent <em>(parfois)</em> la base de données, de plus elle est souvent <em>(parfois)</em> plus difficilement scalable que le reste. L&#8217;utilisation d&#8217;un cache applicatif est rarement une mauvaise idée (il suffit de connaître l&#8217;identité du plus gros consommateur de memcached au monde <a title="FaceBook - memcached" href="http://www.facebook.com/note.php?note_id=39391378919" target="_blank">http://www.facebook.com/note.php?note_id=39391378919</a>)</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">« <em><span style="font-weight: normal;">Il est donc plus avantageux de cacher les processus lourds que les légers</span></em>. »</p>
<p style="margin-bottom: 0cm;">Malheureusement, avec le cache de requêtes ce n&#8217;est pas aussi simple. Admettons qu&#8217;une requête renvoyant un gros résultat prenne plus de temps qu&#8217;une renvoyant  un plus petit. Si cette grosse requête vire toutes les autres requêtes du cache, l&#8217;apport du cache pour les autres requêtes est perdu, elle devront être misent à nouveau dans le cache ca qui implique des recherches inutiles dans le caches et de nouveaux accès disque&#8230;</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">« <em>Lorsque la Query Cache de MySQL est activée, le processus de cacher les résultats et de les invalider s’effectue tout seul de manière invisible. Ainsi, d’autres requêtes que vous ne soupçonnez même pas bénéficient de la cache </em>»</p>
<p style="margin-bottom: 0cm;">L&#8217;efficacité du cache de requêtes est vraiment lié à l&#8217;application. Il dépend du type de requêtes SELECT, de leur fréquence et de la fréquence des écritures dans les tables. Le gain n&#8217;est pas évident et est loin d&#8217;être immédiat, car pour chaque requête « cachable » MySQL devra l&#8217;analyser, devra la hacher, devra  vérifier s&#8217;il elle est dans le cache ou non. Et ceci à un coût&#8230;</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Vous pouvez calculer le taux d&#8217;efficacité du cache de requêtes avec la formule suivant:</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span style="color: #800000;"><strong>Qcache_hits / (Qcache_hits + Com_select )</strong></span></p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">Pour finir, quelques paramètres et commandes</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span style="text-decoration: underline;"><strong>Paramètres principaux</strong></span>:</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><strong>query_cache_size</strong>: Doit être différent de zéro pour activer le cache</p>
<p style="margin-bottom: 0cm;"><strong>query_cache_type</strong>:</p>
<ul>
<li>
<p style="margin-bottom: 0cm;"><strong>ON</strong>: les requêtes select  	sont misent en cache</p>
<ul>
<li>
<p style="margin-bottom: 0cm;">Sauf (<strong>SQL_NO_CACHE</strong>, result set 		trop grand, fonction non déterministe..)</p>
</li>
</ul>
</li>
<li>
<p style="margin-bottom: 0cm;"><strong>DEMAND</strong>: SELECT <strong>SQL_CACHE</strong>&#8230;</p>
</li>
<li>
<p style="margin-bottom: 0cm;"><strong>OFF</strong></p>
</li>
</ul>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span style="text-decoration: underline;"><strong>Commandes principales</strong>:</span></p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><strong>FLUSH QUERY CACHE</strong></p>
<ul>
<li>
<p style="margin-bottom: 0cm;">Défragmente le cache de requêtes</p>
</li>
<li>
<p style="margin-bottom: 0cm;">Ne vide pas le cache</p>
</li>
</ul>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span style="text-decoration: underline;"><strong>Vider le cache de requêtes</strong></span>:</p>
<ul>
<li>
<p style="margin-bottom: 0cm;"><strong>RESET QUERY CACHE</strong></p>
</li>
<li>
<p style="margin-bottom: 0cm;"><strong>FLUSH TABLES</strong></p>
</li>
<li>
<p style="margin-bottom: 0cm;">Redémarrer le serveur</p>
</li>
</ul>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><span style="text-decoration: underline;"><strong>Variables d&#8217;état</strong></span>: <strong>SHOW STATUS LIKE &#8216;Qcache%&#8217; </strong>;</p>
<p style="margin-bottom: 0cm;">
<p style="margin-bottom: 0cm;"><strong>Qcache_free_blocks</strong> : nombre de blocs libres</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_free_memory</strong> : mémoire libre</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_hits</strong> : nombre de fois qu&#8217;il a servi</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_inserts</strong> : nombre de requêtes insérées</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_lowmem_prunes</strong> : nombre de requêtes supprimées car plus de place</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_not_cached </strong>: nombre de requêtes non « cachables »</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_queries_in_cache </strong>: nombre de requêtes dans le cache</p>
<p style="margin-bottom: 0cm;"><strong>Qcache_total_blocks</strong> : nombre de blocs de mémoire</p>
<p style="margin-bottom: 0cm;">
]]></content:encoded>
			<wfw:commentRss>http://dasini.net/blog/2009/10/12/mysql-query-cache/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Optimisation de requêtes: comprendre l&#8217;optimiseur de MySQL</title>
		<link>http://dasini.net/blog/2009/02/18/optimisation-de-requetes-comprendre-loptimiseur-de-mysql/</link>
		<comments>http://dasini.net/blog/2009/02/18/optimisation-de-requetes-comprendre-loptimiseur-de-mysql/#comments</comments>
		<pubDate>Wed, 18 Feb 2009 22:19:25 +0000</pubDate>
		<dc:creator>freshdaz</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimisation]]></category>
		<category><![CDATA[group by]]></category>

		<guid isPermaLink="false">http://dasini.net/blog/?p=453</guid>
		<description><![CDATA[Le but de cet article est d'optimiser une simple requête  (SELECT avg(Population) FROM city GROUP BY CountryCode) et surtout de comprendre comment l'optimiseur procède, en étudiant les résultats donnés par les variables qui permettent de surveiller le serveur MySQL.]]></description>
			<content:encoded><![CDATA[<p><!-- 	 	 --><br />
Le but de cet article est d&#8217;optimiser une simple requête  (<strong>SELECT avg(Population) FROM city GROUP BY CountryCode</strong>) et surtout de comprendre comment l&#8217;optimiseur procède, en étudiant les résultats donnés par les variables qui permettent de surveiller le serveur MySQL.</p>
<p>Le schéma utilisé est le schéma <em>world </em>téléchargeable <a title="world database" href="http://dev.mysql.com/doc/#sampledb" target="_blank">ici</a><br />
<!-- 	 	 --><br />
Voici la structure de la table <em>city</em>:</p>
<address>SHOW CREATE TABLE city\G<br />
*************************** 1. row ***************************<br />
Table: city<br />
Create Table: CREATE TABLE city (<br />
ID int(11) NOT NULL AUTO_INCREMENT,<br />
Name char(35) NOT NULL DEFAULT &nbsp;&raquo;,<br />
CountryCode char(3) NOT NULL DEFAULT &nbsp;&raquo;,<br />
District char(20) NOT NULL DEFAULT &nbsp;&raquo;,<br />
Population int(11) NOT NULL DEFAULT &#8217;0&#8242;,<br />
PRIMARY KEY (ID)<br />
) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1<br />
<!-- 	 	 --><br />
<!-- 	 	 --><br />
</address>
<p>La commande <strong>EXPLAIN</strong>, permet d&#8217;avoir le plan d&#8217;exécution:</p>
<address>EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\G<br />
*************************** 1. row ***************************<br />
id: 1<br />
select_type: SIMPLE<br />
table: c<br />
<span style="color: #ff0000;"><strong>type: ALL</strong></span><br />
possible_keys: NULL<br />
<strong><span style="color: #ff0000;"> key: NULL</span></strong><br />
key_len: NULL<br />
ref: NULL<br />
rows: 4079<br />
<span style="color: #ff0000;"><strong> Extra: Using temporary; Using filesort</strong></span><br />
<!-- 	 	 --><br />
</address>
<p>L&#8217;optimiseur fait un full table scan (<strong>type: ALL</strong>) ce qui n&#8217;est pas forcement une bonne nouvelle. De plus aucun index n&#8217;est utilisé (<strong>key: NULL</strong>), ce qui est logique car la table n&#8217;en contient pas (sic !)<br />
<strong>Using temporary; Using filesort</strong> indiquent la création d&#8217;un table temporaire et le tri des données (pas très bon pour les performances surtout si la table temporaire est créée sur le disque)<br />
<!-- 	 	 --><br />
La variable <strong>Last_query_cost</strong> permet de récupérer le coût de la requête:</p>
<address>SHOW STATUS LIKE &#8216;Last_query_cost&#8217;\G</address>
<p>*************************** 1. row ***************************<br />
Variable_name: Last_query_cost<br />
Value: 4963.520924<br />
<!-- 	 	 --><br />
Ajoutons un index sur la colonne <em>countrycode</em>:</p>
<address>ALTER TABLE city ADD INDEX Idx_cc(CountryCode);</address>
<p><!-- 	 	 --></p>
<address>SHOW CREATE TABLE city\G</address>
<address>*************************** 1. row ***************************</address>
<address> Table: city</address>
<address>Create Table: CREATE TABLE city (</address>
<address> ID int(11) NOT NULL AUTO_INCREMENT,</address>
<address> Name char(35) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> CountryCode char(3) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> District char(20) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> Population int(11) NOT NULL DEFAULT &#8217;0&#8242;,</address>
<address> PRIMARY KEY (ID),</address>
<address><strong> KEY Idx_cc (CountryCode)</strong></address>
<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1</address>
<p><!-- 	 	 --></p>
<address>EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\G</address>
<address>*************************** 1. row ***************************</address>
<address> id: 1</address>
<address> select_type: SIMPLE</address>
<address> table: city</address>
<address> type: ALL</address>
<address>possible_keys: NULL</address>
<address><strong><span style="color: #ff0000;"> key: NULL</span></strong></address>
<address> key_len: NULL</address>
<address> ref: NULL</address>
<address> rows: 4079</address>
<address> Extra: Using temporary; Using filesort</address>
<p><!-- 	 	 --><br />
<!-- 	 	 --><br />
L&#8217;index <em>Idx_cc</em> ne <span style="text-decoration: underline;">sert à rien</span><br />
<!-- 	 	 --><br />
Ajoutons alors un index sur la colonne <em>population </em></p>
<address>ALTER TABLE city ADD INDEX Idx_population(Population);</address>
<p><!-- 	 	 --></p>
<address> SHOW CREATE TABLE city\G</address>
<address>*************************** 1. row ***************************</address>
<address> Table: city</address>
<address>Create Table: CREATE TABLE city (</address>
<address> ID int(11) NOT NULL AUTO_INCREMENT,</address>
<address> Name char(35) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> CountryCode char(3) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> District char(20) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> Population int(11) NOT NULL DEFAULT &#8217;0&#8242;,</address>
<address> PRIMARY KEY (ID),</address>
<address> KEY Idx_cc (CountryCode),</address>
<address><strong> KEY Idx_population (Population)</strong></address>
<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1</address>
<address>1 row in set (0.00 sec)</address>
<p><!-- 	 	 --></p>
<address> EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\G</address>
<address>*************************** 1. row ***************************</address>
<address> id: 1</address>
<address> select_type: SIMPLE</address>
<address> table: c</address>
<address> type: ALL</address>
<address>possible_keys: NULL</address>
<address><strong><span style="color: #ff0000;"> key: NULL</span></strong></address>
<address> key_len: NULL</address>
<address> ref: NULL</address>
<address> rows: 4079</address>
<address> Extra: Using temporary; Using filesort</address>
<p><!-- 	 	 --><br />
Pas mieux ! L&#8217;index <em>Idx_population</em> ne sert à rien.<br />
<!-- 	 	 --><br />
Effacement des 2 index:</p>
<address>ALTER TABLE city DROP INDEX Idx_cc, DROP INDEX Idx_population;</address>
<p><!-- 	 	 --><!-- 	 	 --><br />
Ajoutons un index composite sur les colonnes <em>population, countrycode</em>:</p>
<address>ALTER TABLE city ADD INDEX Idx_population_cc(Population, CountryCode);</address>
<p><!-- 	 	 --></p>
<address> SHOW CREATE TABLE city\G</address>
<address>*************************** 1. row ***************************</address>
<address> Table: city</address>
<address>Create Table: CREATE TABLE `city` (</address>
<address> `ID` int(11) NOT NULL AUTO_INCREMENT,</address>
<address> `Name` char(35) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `CountryCode` char(3) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `District` char(20) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `Population` int(11) NOT NULL DEFAULT &#8217;0&#8242;,</address>
<address> PRIMARY KEY (`ID`),</address>
<address><strong> KEY `Idx_population_cc` (`Population`,`CountryCode`)</strong></address>
<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1</address>
<p><!-- 	 	 --></p>
<address> EXPLAIN SELECT avg(Population) FROM city GROUP BY CountryCode\G</address>
<address>*************************** 1. row ***************************</address>
<address> id: 1</address>
<address> select_type: SIMPLE</address>
<address> table: c</address>
<address> type: index</address>
<address>possible_keys: NULL</address>
<address><span style="color: #ff0000;"><strong> key: Idx_population_cc</strong></span></address>
<address> key_len: 7</address>
<address> ref: NULL</address>
<address> rows: 4079</address>
<address><span style="color: #ff0000;"><strong> Extra: Using index; Using temporary; Using filesort</strong></span></address>
<p><!-- 	 	 --><br />
<!-- 	 	 --><br />
Çaparait un peu meilleur, l&#8217;index<em> Idx_population_cc</em> est utilisé.<br />
<strong>Extra: using index</strong> nous indique que MySQL utilise un index couvrant (covering index), c&#8217;est à dire que l&#8217;information est entièrement  accessible en parcourant l&#8217;index (pas d&#8217;accès aux données).<br />
De  plus l&#8217;optimiseur fait un full index scan:  <strong>type: index</strong><br />
<!-- 	 	 --><br />
Voyons le coût de la requête:</p>
<address>SHOW STATUS LIKE &#8216;Last_query_cost&#8217;\G</address>
<address>*************************** 1. row ***************************<br />
Variable_name: Last_query_cost<br />
Value: 4963.520924</address>
<p><!-- 	 	 --><br />
La valeur de (l&#8217;obscure) Last_query_cost est cependant le même que pour les requêtes précédentes&#8230;<br />
<!-- 	 	 --><br />
Ajoutons un index composite sur les colonnes <em>countrycode,population</em></p>
<address> </address>
<address>ALTER TABLE test2.city ADD INDEX Idx_cc_population(CountryCode,Population);</address>
<p><!-- 	 	 --></p>
<address> SHOW CREATE TABLE city\G</address>
<address>*************************** 1. row ***************************</address>
<address> Table: city</address>
<address>Create Table: CREATE TABLE `city` (</address>
<address> `ID` int(11) NOT NULL AUTO_INCREMENT,</address>
<address> `Name` char(35) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `CountryCode` char(3) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `District` char(20) NOT NULL DEFAULT &nbsp;&raquo;,</address>
<address> `Population` int(11) NOT NULL DEFAULT &#8217;0&#8242;,</address>
<address> PRIMARY KEY (`ID`),</address>
<address> KEY `Idx_population_cc` (`Population`,`CountryCode`),</address>
<address><strong> KEY `Idx_cc_population` (`CountryCode`,`Population`)</strong></address>
<address>) ENGINE=MyISAM AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1</address>
<address> </address>
<p><!-- 	 	 --></p>
<address> EXPLAIN SELECT AVG(Population) FROM city GROUP BY CountryCode\G</address>
<address>*************************** 1. row ***************************</address>
<address> id: 1</address>
<address> select_type: SIMPLE</address>
<address> table: c</address>
<address> type: index</address>
<address>possible_keys: NULL</address>
<address><span style="color: #ff0000;"><strong> key: Idx_cc_population</strong></span></address>
<address> key_len: 7</address>
<address> ref: NULL</address>
<address> rows: 4079</address>
<address><span style="color: #ff0000;"><strong> Extra: Using index</strong></span></address>
<address>1 row in set (0.00 sec)</address>
<address> </address>
<p><!-- 	 	 --></p>
<address>SHOW STATUS LIKE &#8216;Last_query_cost&#8217;\G</address>
<address>*************************** 1. row ***************************<br />
Variable_name: Last_query_cost<br />
Value: 4963.520924</address>
<p><!-- 	 	 --><br />
L&#8217;optimiseur estime que le coût de la requête est toujours le même, cependant l&#8217;index <strong>Idx_cc_population(CountryCode,Population)</strong> <span style="text-decoration: underline;">optimise les performance de notre requête</span> car il n&#8217;y a plus de <strong>Using temporary</strong> ni de <strong>Using filesort</strong>.<br />
Il suffit de pousser encore un peu plus loin notre analyse pour en être définitivement (?) convaincu&#8230;<br />
<!-- 	 	 --><br />
Initialisation des variables de sessions</p>
<address> FLUSH STATUS;</address>
<p><!-- 	 	 --><!-- 	 	 --><br />
En utilisant l&#8217;index <strong>idx_population_cc</strong> (le mauvais index)</p>
<address> SELECT AVG(Population) FROM city use index(idx_population_cc) GROUP BY CountryCode;</address>
<address> &#8230;</address>
<address><span style="color: #ff0000;"><strong> 232 rows in set (0.00 sec)</strong></span></address>
<p><!-- 	 	 --></p>
<address> </address>
<address>SHOW STATUS LIKE &#8216;handler%&#8217;;<br />
</address>
<pre> +----------------------------+-------+</pre>
<pre> | Variable_name              | Value |</pre>
<pre> +----------------------------+-------+</pre>
<pre> | Handler_commit             | 0     |</pre>
<pre> | Handler_delete             | 0     |</pre>
<pre> | Handler_discover           | 0     |</pre>
<pre> | Handler_prepare            | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_first         | 1</strong></span>     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_key           | 4079</strong></span>  |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_next          | 4079</strong></span>  |</pre>
<pre> | Handler_read_prev          | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_rnd           | 232</strong></span>   |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_rnd_next      | 233</strong></span>   |</pre>
<pre> | Handler_rollback           | 0     |</pre>
<pre> | Handler_savepoint          | 0     |</pre>
<pre> | Handler_savepoint_rollback | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_update             | 3847</strong></span>  |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_write              | 232</strong></span>   |</pre>
<pre> +----------------------------+-------+</pre>
<p><!-- 	 	 --><br />
Les handler sont des indicateurs liés au moteur de stockage.<br />
Handler_read_first comptabilise le nombre de fois que la première valeur de l&#8217;index est lue.<br />
Handler_read_key indique le nombre d&#8217;enregistrement récupéré graçe à l&#8217;index.<br />
Handler_read_next indique une lecture ordonnée de l&#8217;index (une valeur, puis la suivante, puis la suivante&#8230;).<br />
<strong> Handler_read_first, Handler_read_key &amp;  Handler_read_next, indiquent là, un <span style="color: #ff0000;">full index scan</span></strong><br />
<!-- 	 	 --><br />
<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 />
<!-- 	 	 --><br />
Handler_update nous donne une indication sur le nombre de mise à jours dans la table temporaire (à cause du tri)<br />
Handler_write indique le nombre de lignes insérées dans la table temporaire<br />
<!-- 	 	 --><br />
Ces 2 derniers paramètres confirme donc la<span style="color: #ff0000;"><strong> création de la table temporaire et l&#8217;opération de tri</strong></span><br />
<!-- 	 	 --><!-- 	 	 --><br />
Pour avoir plus d&#8217;informations sur le tri:</p>
<address>SHOW SESSION STATUS LIKE &#8216;sort%&#8217;;<br />
</address>
<pre> +-------------------+-------+</pre>
<pre> | Variable_name     | Value |</pre>
<pre> +-------------------+-------+</pre>
<pre> | Sort_merge_passes | 0     |</pre>
<pre> | Sort_range        | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Sort_rows         | 232</strong></span>   |</pre>
<pre> | <span style="color: #ff0000;"><strong>Sort_scan         | 1</strong></span>     |</pre>
<pre> +-------------------+-------+</pre>
<p><!-- 	 	 --><br />
Sort_rows: nombre de lignes triées (nombre d&#8217;enregistrements de la table temporaire).<br />
Sort_scan: nombre de tri.<br />
<!-- 	 	 --><!-- 	 	 --></p>
<address>SHOW SESSION STATUS LIKE &#8216;created%&#8217;;<br />
</address>
<pre> +-------------------------+-------+</pre>
<pre> | Variable_name           | Value |</pre>
<pre> +-------------------------+-------+</pre>
<pre> | Created_tmp_disk_tables | 0     |</pre>
<pre> | Created_tmp_files       | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Created_tmp_tables      | 1</strong></span>     |</pre>
<pre> +-------------------------+-------+</pre>
<p><!-- 	 	 --><br />
Created_tmp_tables: nombre de table temporaire crée. La bonne nouvelle est que la table temporaire est créee en mémoire (<strong>Created_tmp_disk_tables=0</strong>)</p>
<p><!-- 	 	 --><!-- 	 	 --><br />
réinitialise les variables de sessions</p>
<address>FLUSH STATUS;<br />
<!-- 	 	 --><!-- 	 	 --><br />
</address>
<p>En utilisant l&#8217;index <strong>idx_cc_population</strong> (le bon index)</p>
<address></address>
<address> SELECT AVG(Population) FROM city use index(idx_population_cc) GROUP BY CountryCode;</address>
<address> &#8230;</address>
<address><span style="color: #ff0000;"><strong> 232 rows in set (0.00 sec)</strong></span></address>
<address><span style="color: #ff0000;"><strong><br />
</strong></span></address>
<p><!-- 	 	 --></p>
<address> SHOW STATUS LIKE &#8216;handler%&#8217;;</address>
<p><!-- 	 	 --></p>
<pre> +----------------------------+-------+</pre>
<pre> | Variable_name              | Value |</pre>
<pre> +----------------------------+-------+</pre>
<pre> | Handler_commit             | 0     |</pre>
<pre> | Handler_delete             | 0     |</pre>
<pre> | Handler_discover           | 0     |</pre>
<pre> | Handler_prepare            | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_first         | 1</strong></span>     |</pre>
<pre> | Handler_read_key           | 0     |</pre>
<pre> | <span style="color: #ff0000;"><strong>Handler_read_next          | 4079</strong></span>  |</pre>
<pre> | Handler_read_prev          | 0     |</pre>
<pre> | Handler_read_rnd           | 0     |</pre>
<pre> | Handler_read_rnd_next      | 0     |</pre>
<pre> | Handler_rollback           | 0     |</pre>
<pre> | Handler_savepoint          | 0     |</pre>
<pre> | Handler_savepoint_rollback | 0     |</pre>
<pre> | Handler_update             | 0     |</pre>
<pre> | Handler_write              | 0     |</pre>
<pre> +----------------------------+-------+</pre>
<p><!-- 	 	 --><br />
Handler_read_first &amp;  Handler_read_next valident le full index scan et puis c&#8217;est tout <img src='http://dasini.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /><br />
<!-- 	 	 --><!-- 	 	 --></p>
<address>SHOW SESSION STATUS LIKE &#8216;sort%&#8217;;<br />
</address>
<pre> +-------------------+-------+</pre>
<pre> | Variable_name     | Value |</pre>
<pre> +-------------------+-------+</pre>
<pre> | Sort_merge_passes | 0     |</pre>
<pre> | Sort_range        | 0     |</pre>
<pre> | Sort_rows         | 0     |</pre>
<pre> | Sort_scan         | 0     |</pre>
<pre> +-------------------+-------+</pre>
<p><!-- 	 	 --><br />
pas de tri<br />
<!-- 	 	 --><!-- 	 	 --></p>
<address>SHOW SESSION STATUS LIKE &#8216;created%&#8217;;<br />
</address>
<pre> +-------------------------+-------+</pre>
<pre> | Variable_name           | Value |</pre>
<pre> +-------------------------+-------+</pre>
<pre> | Created_tmp_disk_tables | 0     |</pre>
<pre> | Created_tmp_files       | 0     |</pre>
<pre> | Created_tmp_tables      | 0     |</pre>
<pre> +-------------------------+-------+</pre>
<p><!-- 	 	 -->pas de table(s) temporaire(s)<br />
<!-- 	 	 --><!-- 	 	 --><br />
cqfd<br />
<!-- 	 	 --></p>
]]></content:encoded>
			<wfw:commentRss>http://dasini.net/blog/2009/02/18/optimisation-de-requetes-comprendre-loptimiseur-de-mysql/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Influencer l&#8217;optimiseur de MySQL</title>
		<link>http://dasini.net/blog/2009/01/29/influencer-loptimiseur-de-mysql/</link>
		<comments>http://dasini.net/blog/2009/01/29/influencer-loptimiseur-de-mysql/#comments</comments>
		<pubDate>Thu, 29 Jan 2009 21:14:08 +0000</pubDate>
		<dc:creator>freshdaz</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[optimisation]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[Last_query_cost]]></category>
		<category><![CDATA[mysqlslap]]></category>
		<category><![CDATA[optimiser]]></category>

		<guid isPermaLink="false">http://dasini.net/blog/?p=430</guid>
		<description><![CDATA[Il est possible d'influencer l'optimiseur pour qu'il choisisse d'utiliser ou de ne pas utiliser un index particulier. Les clauses à placer dans votre requête SELECT sont les suivantes:

USE INDEX : utilise l'index passé en argument (MySQL ne l'utilisera pas si l'index est plus couteux qu'un full table scan)

FORCE INDEX : utilise l'index passé en argument (MySQL ne l'utilisera pas ...s'il ne peut pas l'utiliser :) )

IGNORE INDEX : n'utilise pas l'index passé en argument]]></description>
			<content:encoded><![CDATA[<p>Il est possible d&#8217;influencer l&#8217;optimiseur pour qu&#8217;il choisisse d&#8217;utiliser ou de ne pas utiliser un index particulier. Les clauses à placer dans votre requête <strong>SELECT </strong>sont les suivantes:</p>
<p><strong>USE INDEX</strong> : utilise l&#8217;index passé en argument (MySQL ne l&#8217;utilisera pas si l&#8217;index est plus couteux qu&#8217;un <em>full table scan</em>)</p>
<p><strong>FORCE INDEX</strong> : utilise l&#8217;index passé en argument (MySQL ne l&#8217;utilisera pas &#8230;s&#8217;il ne peut pas l&#8217;utiliser <img src='http://dasini.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  <em></em>)</p>
<p><strong>IGNORE INDEX</strong> : n&#8217;utilise pas l&#8217;index passé en argument</p>
<p>La plus part du temps, il se débrouille trés bien sans indications, mais parfois&#8230;</p>
<p>Dans cet exemple, j&#8217;utilise une table <em>rental_daz</em> inspirée de la table <em>rental </em>de la base de donnée <em><a title="sakila database" href="http://dev.mysql.com/doc/" target="_blank">sakila</a></em>, voici sa structure:</p>
<pre>12:14 daz$sakila&gt; SHOW CREATE TABLE rental_daz\G
*************************** 1. row ***************************
       Table: rental_daz
Create Table: CREATE TABLE `rental_daz` (
  `rental_id` int(11) NOT NULL AUTO_INCREMENT,
<strong>  `rental_date` datetime NOT NULL,</strong>
  `inventory_id` mediumint(8) unsigned NOT NULL,
  `customer_id` smallint(5) unsigned NOT NULL,
  `return_date` datetime DEFAULT NULL,
  `staff_id` tinyint(3) unsigned NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
       ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`rental_id`),
<strong>  UNIQUE KEY `rental_date` (`rental_date`,`inventory_id`,`customer_id`),</strong>
  KEY `idx_fk_inventory_id` (`inventory_id`),
  KEY `idx_fk_customer_id` (`customer_id`),
  KEY `idx_fk_staff_id` (`staff_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8</pre>
<p>Voici ma requête:</p>
<p><span style="color: #ff0000;"><strong>SELECT * FROM rental_daz WHERE  rental_date &gt; SUBDATE(now(), INTERVAL  3 YEAR);</strong></span></p>
<p>La commande <strong>EXPLAIN </strong>me permet de connaitre son plan d&#8217;exécution:</p>
<pre>12:14 daz$sakila&gt; EXPLAIN SELECT * FROM rental_daz
WHERE  rental_date &gt; SUBDATE(now(), INTERVAL  3 YEAR)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental_daz
         type: range
possible_keys: rental_date
<span style="color: #ff0000;">          <strong>key: rental_date</strong></span>
      key_len: 8
          ref: NULL
         rows: 2744
        Extra: Using where</pre>
<p>L&#8217;optimiseur voit l&#8217;index composite <em>rental_date</em> et l&#8217;utilise. Ça à l&#8217;air pas mal&#8230;</p>
<p>Voyons le coût de cette requête,grâce à au paramètre <strong>LAST_QUERY_COST</strong> :</p>
<pre>12:19 daz$sakila&gt; SHOW STATUS LIKE 'Last_query_cost';
+-----------------+-------------+
| Variable_name   | Value       |
+-----------------+-------------+
| Last_query_cost | <strong>3842.609000</strong> |
+-----------------+-------------+</pre>
<p><span style="text-decoration: underline;">Empêchons </span>l&#8217;optimiseur d&#8217;utiliser l&#8217;index <em>rental_date</em>:</p>
<pre>12:20 daz$sakila&gt; EXPLAIN SELECT * FROM rental_daz <strong>IGNORE INDEX(rental_date)</strong>
WHERE  rental_date &gt; SUBDATE(now(), INTERVAL 3 YEAR)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental_daz
         type: ALL
possible_keys: NULL
          <span style="color: #ff0000;"><strong>key: NULL</strong></span>
      key_len: NULL
          ref: NULL
         rows: 16298
        Extra: Using where</pre>
<p>L&#8217;optimiseur n&#8217;utilise donc pas l&#8217;index (il ne le voit même pas). Un <em>full table scan</em> est donc effectué. C&#8217;est à priori plus coûteux que d&#8217;utiliser l&#8217;index. Regardons le coût de cette requêtes.</p>
<pre>16:18 daz$sakila&gt; SHOW STATUS LIKE 'Last_query_cost';
+-----------------+-------------+
| Variable_name   | Value       |
+-----------------+-------------+
| Last_query_cost | <strong>3356.599000</strong> |
+-----------------+-------------+</pre>
<p>Malgré le <em>full table scan</em>, cette requête est moins coûteuse que celle qui utilise l&#8217;index !!!</p>
<p>Essayons de vérifier cela en durée. Le client <strong>mysqlslap </strong>est tout indiqué :</p>
<p>mysqlslap -uroot -proot  &#8211;create-schema=sakila -i50<br />
-q&nbsp;&raquo;<strong>SELECT * FROM rental_daz WHERE  rental_date &gt; SUBDATE(now(), INTERVAL  3 YEAR);</strong>&nbsp;&raquo;</p>
<pre>Benchmark</pre>
<pre>        Average number of seconds to run all queries: <span style="color: #ff0000;"><strong>0.287</strong></span> seconds</pre>
<pre>        Minimum number of seconds to run all queries: 0.140 seconds</pre>
<pre>        Maximum number of seconds to run all queries: 1.172 seconds</pre>
<pre>        Number of clients running queries: 1</pre>
<pre>        Average number of queries per client: 1</pre>
<p>mysqlslap -uroot -proot  &#8211;create-schema=sakila -i50<br />
-q&nbsp;&raquo;<strong>SELECT * FROM rental_daz ignore index(rental_date) WHERE  rental_date &gt; SUBDATE(now(), INTERVAL  3 YEAR);</strong>&nbsp;&raquo;</p>
<pre>Benchmark</pre>
<pre>        Average number of seconds to run all queries: <span style="color: #ff0000;"><strong>0.167</strong></span> seconds</pre>
<pre>        Minimum number of seconds to run all queries: 0.078 seconds</pre>
<pre>        Maximum number of seconds to run all queries: 1.094 seconds</pre>
<pre>        Number of clients running queries: 1</pre>
<pre>        Average number of queries per client: 1</pre>
<p>mysqlslap confirme bien que le full table scan est, dans ce cas précis, plus performant que la recherche indexée par intervalle. On se trouve bien dans un cas où l&#8217;optimiseur se trompe.</p>
]]></content:encoded>
			<wfw:commentRss>http://dasini.net/blog/2009/01/29/influencer-loptimiseur-de-mysql/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

