<?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>dbnewz &#187; 4.0</title>
	<atom:link href="http://www.dbnewz.com/category/mysql/40/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.dbnewz.com</link>
	<description>le blog français sur les SGBD - MySQL, Oracle et plus...</description>
	<lastBuildDate>Wed, 28 Jul 2010 14:01:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>15 secondes pour installer une réplication MySQL avec MySQL Sandbox, pari tenu ?</title>
		<link>http://www.dbnewz.com/2008/10/10/15-secondes-pour-installer-une-replication-mysql-avec-mysql-sandbox-pari-tenu/</link>
		<comments>http://www.dbnewz.com/2008/10/10/15-secondes-pour-installer-une-replication-mysql-avec-mysql-sandbox-pari-tenu/#comments</comments>
		<pubDate>Fri, 10 Oct 2008 07:08:28 +0000</pubDate>
		<dc:creator>arnaud</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[réplication]]></category>
		<category><![CDATA[outils]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=75</guid>
		<description><![CDATA[&#171;&#160;Installez-moi une configuration MySQL composée d&#8217;un master et deux slaves, vous avez 15 secondes. Top chrono&#160;&#187;&#8230;
Non, ça n&#8217;est pas la dernière énigme à la mode pour rentrer chez Google mais plutôt une question qui pourrait devenir presque banale pour un entretien d&#8217;embauche pour un DBA MySQL à l&#8217;avenir, qui sait ?
Face à un tel défi, [...]]]></description>
			<content:encoded><![CDATA[<p>&laquo;&nbsp;Installez-moi une configuration MySQL composée d&#8217;un master et deux slaves, vous avez 15 secondes. Top chrono&nbsp;&raquo;&#8230;</p>
<p>Non, ça n&#8217;est pas la dernière énigme à la mode pour rentrer chez Google mais plutôt une question qui pourrait devenir presque banale pour un entretien d&#8217;embauche pour un DBA MySQL à l&#8217;avenir, qui sait ?</p>
<p>Face à un tel défi, trois solutions :</p>
<p>- La fuite (mais faites une croix sur la &laquo;&nbsp;recommandation&nbsp;&raquo; Linkedin)<br />
- Le kernel panic<br />
- MySQL Sandbox !</p>
<p>Bien vu, MySQL Sandbox est la réponse la plus stratégique pour la poursuite de votre carrière.</p>
<p>Giuseppe Maxia (dont le blog figure dans notre blogroll, allez y jeter un oeil) est l&#8217;auteur de cet outil vraiment très pratique.  Que propose t-il ?</p>
<p>L&#8217;idée est d&#8217;automatiser l&#8217;installation de plusieurs serveurs MySQL sur une même machine. Rien que nous ne puissions faire manuellement c&#8217;est vrai, cependant la procédure habituelle consistant à ne pas mélanger les répertoires d&#8217;installation, choisir un port différent par serveur, appliquer <a href="http://www.dbnewz.com/2008/07/14/mysql_secure_installation-pratique-mais-non-parametrable/" target="_blank">mysql_secure_installation</a>&#8230; Tout cela gagnerait à être automatisé non ? De plus ces installations manuelles sont potentiellement sources d&#8217;erreurs.</p>
<p>MySQL Sandbox est compatible avec toutes les versions MySQL de la 3.23 à la 6.0. Les différentes installations effectuées sont indépendantes les unes des autres, on peut ainsi faire cohabiter sans risque de conflits plusieurs versions différentes de MySQL sur une même machine (ports, répertoires et sockets indépendants).</p>
<p>En plus d&#8217;automatiser ces processus d&#8217;installation (gain de temps), MySQL Sandbox ne s&#8217;arrête pas là et  propose également des commandes très simples pour gérer les serveurs une fois installés.</p>
<p>Concernant le gain de temps, son auteur promet (notamment en page 2 de cette <a href="http://datacharmer.org/presentations/mysqluniv_2008/sandbox_2_0.html" target="_blank">présentation</a>) l&#8217;installation d&#8217;une réplication MySQL en 15 secondes. Info ou intox ?</p>
<p><span id="more-75"></span></p>
<p><strong>Installation et pré-requis<br />
</strong></p>
<p>Pour fonctionner MySQL Sandbox n&#8217;a besoin que des binaries des versions que vous souhaitez installer.</p>
<p>L&#8217;installation est très rapide :</p>
<p>Après avoir téléchargé <a href="https://launchpad.net/mysql-sandbox" target="_blank">MySQL Sandbox</a>, il suffit de le décompresser dans le répertoire de votre choix :</p>
<p>debian:/opt# tar xzvf mysql_sandbox_2.0.11.tar.gz</p>
<p>On remarque alors que différentes commandes sont à notre disposition, plutôt explicites :</p>
<p><code>-rwxr-xr-x 1 501 staff  6154 2008-10-05 07:42 <strong>make_multiple_custom_sandbox</strong><br />
-rwxr-xr-x 1 501 staff  9062 2008-10-05 07:42 <strong>make_multiple_sandbox</strong><br />
-rwxr-xr-x 1 501 staff 12180 2008-10-05 07:42 <strong>make_replication_sandbox</strong><br />
-rwxr-xr-x 1 501 staff  5786 2008-10-05 07:42 <strong>make_sandbox</strong></code></p>
<p>make_sandbox : installe un serveur MySQL simple<br />
make_replication_sandbox : installe une réplication (par défaut un master, 2 slaves)<br />
make_multiple_sandbox : pour installer plusieurs serveurs identiques<br />
make_multiple_custom_sandbox : permet d&#8217;installer plusieurs serveurs de versions différentes</p>
<p><strong>Installer une réplication</strong></p>
<p>Une fois le <a href="http://dev.mysql.com/downloads/" target="_blank">binary récupéré</a>, nous utilisons la commande <strong>make_replication_sandbox</strong> pour obtenir un master et deux slaves. Elle prend en paramètre le tar.gz du binary (d&#8217;autres raccourcis sont possibles, je vous renvoie au fichier README de l&#8217;outil).</p>
<p><code>debian:/opt/mysql_sandbox_2.0.11# ./make_replication_sandbox /opt/mysql-5.0.67-linux-i686-glibc23.tar.gz</code></p>
<p>La sortie écran donne ceci :<br />
installing and starting master<br />
installing slave 1<br />
installing slave 2<br />
starting slave 1<br />
. sandbox server started<br />
starting slave 2<br />
.. sandbox server started<br />
initializing slave 1<br />
initializing slave 2<br />
replication directory installed on /root/sandboxes/rsandbox_5_0_67</p>
<p>C&#8217;est fait ! L&#8217;installation en elle-même a duré un peu plus de 15 secondes, disons que les lignes de commande à taper tiennent dans ce laps de temps&#8230;</p>
<p>Par défaut les &laquo;&nbsp;bacs à sable&nbsp;&raquo; installés par MySQL Sandbox se placent dans $HOME/sandboxes/. Dans le cadre de notre réplication, celle-ci a été installée ici : debian:~/sandboxes/rsandbox_5_0_67#</p>
<p>Notre réplication est d&#8217;ores et déjà fonctionnelle, on peut le vérifier en se connectant par exemple au slave numero 2 (faites un ps-ef | grep mysql pour afficher rapidement les noms/localisations des sockets par ex)</p>
<p>debian:/home/user# mysql &#8211;socket=/tmp/mysql_sandbox29473.sock -p</p>
<p>Le password par défaut est &laquo;&nbsp;msandbox&nbsp;&raquo;.</p>
<p>mysql&gt; show slave status\G<br />
Slave_IO_State: Waiting for master to send event<br />
Master_Host: 127.0.0.1<br />
Master_User: msandbox<br />
Master_Port: 29472<br />
Connect_Retry: 60<br />
Master_Log_File: mysql-bin.000001<br />
Read_Master_Log_Pos: 628<br />
Relay_Log_File: mysql_sandbox29473-relay-bin.000002<br />
Relay_Log_Pos: 765<br />
Relay_Master_Log_File: mysql-bin.000001<br />
<strong>Slave_IO_Running: Yes<br />
Slave_SQL_Running: Yes</strong></p>
<p>La réplication fonctionne, le pari est tenu !</p>
<p><strong>Les autres commandes disponibles</strong></p>
<p>Evoquées plus haut les commandes make_multiple_custom_sandbox, make_multiple_sandbox, make_sandbox et make_replication_sandbox vous permettent d&#8217;installer différentes topologies de serveurs MySQL, du master-master au groupe de serveurs aux versions identiques ou pas.</p>
<p>Exemple, pour installer la version MySQL 5.1.28 sur la même machine que notre réplication précédente :</p>
<p>debian:/opt/mysql_sandbox_2.0.11# ./make_sandbox /opt/mysql-5.1.28-rc-linux-i686-glibc23.tar.gz</p>
<p>Résultat :</p>
<p>&laquo;&nbsp;Your sandbox server was installed in /root/sandboxes/msb_5_1_28.&nbsp;&raquo;, simple non ?</p>
<p>D&#8217;autres commandes existent pour gérer votre installation :</p>
<p><strong>start, restart, stop, clear</strong>&#8230; un suffixe &laquo;&nbsp;_all&nbsp;&raquo; pour les réplications vient s&#8217;ajouter aux commandes précédentes, ex &laquo;&nbsp;<strong>stop_all</strong>&laquo;&nbsp;.</p>
<p>&laquo;&nbsp;m&nbsp;&raquo; est le raccourci de master, &laquo;&nbsp;s1&#8243;, &laquo;&nbsp;s2&#8243;, etc concernent les slaves.</p>
<p>Ainsi pour arrêter totalement notre configuration de réplication précédente, on effectue :</p>
<p>debian:~/sandboxes/rsandbox_5_0_67# ./stop_all</p>
<p>A l&#8217;écran :</p>
<p>executing &laquo;&nbsp;stop&nbsp;&raquo; on slave 1<br />
executing &laquo;&nbsp;stop&nbsp;&raquo; on slave 2<br />
executing &laquo;&nbsp;stop&nbsp;&raquo; on master</p>
<p>Comme vous le voyez, MySQL Sandbox est très simple d&#8217;emploi.</p>
<p>Pour aller plus loin je vous conseille vivement la lecture (c&#8217;est rapide) du <a href="http://forge.mysql.com/wiki/MySQL_Sandbox" target="_blank">wiki</a> de l&#8217;outil. Il reprend en fait le fichier README. Clair et concis vous y trouverez certainement votre bonheur : grâce à MySQL Sandbox, tester une nouvelle version de MySQL devient un jeu d&#8217;enfant.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2008/10/10/15-secondes-pour-installer-une-replication-mysql-avec-mysql-sandbox-pari-tenu/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Générer un jeu de données : shell, mysqlslap, et procédure stockée</title>
		<link>http://www.dbnewz.com/2008/08/19/generer-un-jeu-de-donnees-shell-mysqlslap-et-procedure-stockee/</link>
		<comments>http://www.dbnewz.com/2008/08/19/generer-un-jeu-de-donnees-shell-mysqlslap-et-procedure-stockee/#comments</comments>
		<pubDate>Mon, 18 Aug 2008 22:39:46 +0000</pubDate>
		<dc:creator>arnaud</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=61</guid>
		<description><![CDATA[Le prochain article de notre série consacrée aux index MySQL approche et j&#8217;ai besoin pour ce prochain épisode de générer une table de test de la forme suivante :  CREATE TABLE `t` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`date` timestamp NOT NULL,
`flag` tinyint(4) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `flag` (`flag`)
) ENGINE=MyISAM;La structure est définie, [...]]]></description>
			<content:encoded><![CDATA[<p>Le prochain article de <a href="http://www.dbnewz.com/tag/index/" target="_blank">notre série consacrée aux index</a> MySQL approche et j&#8217;ai besoin pour ce prochain épisode de générer une table de test de la forme suivante :<br id="d7d0" /> <br id="zkp0" /> <code>CREATE TABLE `t` (<br />
`id` mediumint(8) unsigned NOT NULL auto_increment,<br />
`date` timestamp NOT NULL,<br />
`flag` tinyint(4) NOT NULL default '0',<br />
PRIMARY KEY  (`id`),<br />
KEY `flag` (`flag`)<br />
) ENGINE=MyISAM;</code><br id="yr-u" /><br id="yr-u0" />La structure est définie, reste à alimenter la table, disons 1 million d&#8217;enregistrements.<br id="ix.u" /> <br id="ix.u0" /> La valeur du champ &laquo;&nbsp;flag&nbsp;&raquo; est importante pour nos tests ultérieurs, sa valeur doit pour le moment être comprise entre 0 et 1, le tout à peu près uniformément distribué.<br id="g-s4" /> <br id="g-s40" /> La requête sera la suivante :<br id="ef7s" /> <br id="g-s41" /> <code>INSERT INTO test.t (flag) VALUES (ROUND(RAND()))</code>;<br id="d0-j" /> <br id="d0-j0" /> Il faut maintenant exécuter celle-ci 1 million de fois.<br id="gx_b" /> Voyons ce que cela donne en utilisant le shell, mysqlslap ou bien encore une procédure stockée.</p>
<p><span id="more-61"></span></p>
<p><strong>Le shell</strong></p>
<p>L&#8217;idée : générer un fichier texte des données à insérer puis exécuter le fichier de sortie comme entrée du client mysql.</p>
<p>Cette méthode n&#8217;est pas la plus efficace du lot, surtout pour 1 million de lignes, mais on peut néanmoins arriver à ses fins :</p>
<p><code>debian:/tmp# (for i in `seq 1 1000000`; do echo "insert into test.t(flag) values(round(rand()));"; done;) &gt; /tmp/insertions.txt</code></p>
<p>Puis :</p>
<p><code>debian:/tmp# mysql test &lt; /tmp/insertions.txt</code></p>
<p>Notre million de lignes est inséré.</p>
<p><strong>mysqlslap</strong></p>
<p>mysqlslap est un outil de benchmark que <a href="http://www.dbnewz.com/2008/07/07/mysqlslap-et-supersmack-deux-outils-de-benchmark-pour-mysql/" target="_blank">nous avons étudié récemment</a>. Nous détournons ici son utilisation première pour finalement exécuter notre million de requêtes :</p>
<p><code>debian:/usr/local/mysql51# ./bin/mysqlslap --user=root --socket=/tmp/mysql.sock --concurrency=1 --iterations=1000000 --query="insert into test.t (flag) values (round(rand()))"</code></p>
<p>Benchmark<br id="bel10" /> Average number of seconds to run all queries: 0.000 seconds<br id="bel11" /> Minimum number of seconds to run all queries: 0.000 seconds<br id="bel12" /> Maximum number of seconds to run all queries: 0.609 seconds<br id="bel13" /> Number of clients running queries: 1<br id="bel14" /> Average number of queries per client: 1</p>
<p>Cette solution est plus rapide que la précédente, aussi si vous disposez de mysqlslap, n&#8217;hésitez pas.</p>
<p><strong>La procédure stockée</strong> : REPEAT&#8230; UNTIL</p>
<p>Dès lors qu&#8217;on envisage d&#8217;insérer 1 million de lignes dans une table, la notion de &laquo;&nbsp;boucle&nbsp;&raquo; n&#8217;est jamais très loin, et finalement la procédure stockée non plus. Celle-ci est en effet un moyen simple et rapide d&#8217;implémenter un traitement répétitif.</p>
<p>Basique et à peine paramétrable (seul le nombre d&#8217;enregistrements à insérer est dynamique), voici à quoi ressemble la procédure stockée permettant d&#8217;insérer notre million de lignes :</p>
<p><code>delimiter //<br id="bfwc" />CREATE PROCEDURE fill_table(nb_rows INT)<br id="bfwc0" />BEGIN<br id="bfwc1" /> DECLARE i INT DEFAULT 0;<br id="bfwc2" /> REPEAT<br id="bfwc3" /> SET i = i + 1;<br id="bfwc4" /> INSERT INTO t (flag) VALUES(round(rand()));<br id="bfwc5" /> UNTIL i &gt;= nb_rows<br id="bfwc6" /> END REPEAT;<br id="bfwc7" />END;<br id="bfwc8" />//<br id="bfwc9" />delimiter ;</code><br id="bfwc10" /><br id="bfwc11" />Pas vraiment besoin de commenter le code, on note simplement les &laquo;&nbsp;delimiter&nbsp;&raquo; qui permettent de définir une autre terminaison que &laquo;&nbsp;;&nbsp;&raquo; le temps d&#8217;écrire la procédure dans le client mysql.</p>
<p>Pour appeler celle-ci :<br id="z6fd" /><br id="z6fd0" /><code>mysql&gt; CALL fill_table(1000000);</code><br id="z6fd1" />Query OK, 1 row affected (1 min 37.98 sec)</p>
<p>Cette solution est la plus rapide des trois présentées ici.</p>
<p>Mission accomplie ? Presque&#8230;</p>
<p>Avant de lancer la procédure stockée, la commande SHOW INDEX affichait une cardinalité de 0 pour la clé primaire et de NULL pour notre index situé sur &laquo;&nbsp;flag&nbsp;&raquo;.</p>
<p>Une fois la procédure terminée, un SHOW INDEX indique cette fois une cardinalité mise à jour pour la clé primaire (1 000 000) mais celle-ci est toujours à NULL pour l&#8217;index &laquo;&nbsp;flag&nbsp;&raquo;.</p>
<p>La commande <a href="http://dev.mysql.com/doc/refman/5.1/en/analyze-table.html" target="_blank">ANALYZE </a>permet de laver cet affront :</p>
<p><code>mysql&gt; ANALYZE TABLE t;<br />
+---------+---------+----------+----------+<br />
| Table   | Op      | Msg_type | Msg_text |<br />
+---------+---------+----------+----------+<br />
| world.t | analyze | status   | OK       |<br />
+---------+---------+----------+----------+</code></p>
<p>Les statistiques de notre index sont désormais à jour et la commande SHOW INDEX indique désormais une cardinalité de 2 pour l&#8217;index &laquo;&nbsp;flag&nbsp;&raquo;, le champ indexé ne comporte effectivement que des 0 ou des 1.</p>
<p>Nous voilà face à un index de cardinalité 2&#8230; Peu sélectif ? Inutile ? Mieux que rien ? Les paris sont ouverts.</p>
<p>Réponse au prochain numéro, stay tuned.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2008/08/19/generer-un-jeu-de-donnees-shell-mysqlslap-et-procedure-stockee/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Les index MySQL : types, placements, efficacité</title>
		<link>http://www.dbnewz.com/2008/06/27/les-index-mysql-types-placements-efficacite/</link>
		<comments>http://www.dbnewz.com/2008/06/27/les-index-mysql-types-placements-efficacite/#comments</comments>
		<pubDate>Fri, 27 Jun 2008 06:52:44 +0000</pubDate>
		<dc:creator>arnaud</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=48</guid>
		<description><![CDATA[Déjà trois semaines d&#8217;écoulées depuis que certains d&#8217;entre vous, les &#171;&#160;héros&#160;&#187;, ont posé leurs questions (oui il est possible de devenir un héros rien qu&#8217;en lisant dbnewz ! Les véritables héros sont d&#8217;ailleurs abonnés au tout nouveau flux feedburner   )
Trois semaines d&#8217;attente, cela mérite un billet digne de ce nom, c&#8217;est parti.
Indexer, pourquoi [...]]]></description>
			<content:encoded><![CDATA[<p>Déjà trois semaines d&#8217;écoulées depuis que certains d&#8217;entre vous, les &laquo;&nbsp;héros&nbsp;&raquo;, ont posé leurs questions (oui il est possible de <a href="http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/" target="_blank">devenir un héros</a> rien qu&#8217;en lisant dbnewz ! Les véritables héros sont d&#8217;ailleurs abonnés au tout nouveau flux <a href="http://feeds.feedburner.com/Dbnewz" target="_blank">feedburner</a> <img src='http://www.dbnewz.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  )</p>
<p>Trois semaines d&#8217;attente, cela mérite un billet digne de ce nom, c&#8217;est parti.</p>
<p><strong>Indexer, pourquoi ?</strong><br id="dz-w" /><br id="yo1g" />L&#8217;indexation peut avoir plusieurs buts : <br id="gpy7" />- Accéder à ses données plus rapidement, les index sont en effet l&#8217;outil le plus puissant pour <strong>accélérer les temps d&#8217;exécution de vos requêtes</strong> jusqu&#8217;à parfois plusieurs centaines de % !<br id="gpy70" />- Définir le degré d&#8217;unicité d&#8217;une colonne donnée : chaque champ doit-il être unique ? les doublons sont-ils autorisés ?<br id="e0_r" /><br id="uby5" /><strong>Principe de fonctionnement</strong><br id="kvk1" /><br id="kvk10" />Lorsque vous envoyez une requête à votre serveur MySQL, celle-ci est d&#8217;abord confiée au &laquo;&nbsp;parseur&nbsp;&raquo; SQL qui a pour but de vérifier si la syntaxe de votre demande est correcte. Cette étape franchie, la requête passe par &laquo;&nbsp;l&#8217;optimiseur&nbsp;&raquo;. Il s&#8217;agit ici de déterminer le <strong>plan d&#8217;exécution</strong> de la requête afin que celle-ci s&#8217;exécute le plus rapidement possible.</p>
<p>L&#8217;optimiseur détecte si d&#8217;éventuels index sont disponibles, si c&#8217;est le cas il décidera de s&#8217;en servir&#8230; ou pas : il est parfois plus rapide de ne pas se servir d&#8217;un index <strong>!</strong> Nous verrons pourquoi au cours de cette série d&#8217;articles.</p>
<p>Une fois le plan d&#8217;exécution achevé, c&#8217;est le moteur de stockage qui prend le relais, celui-ci peut être vu comme un &laquo;&nbsp;module&nbsp;&raquo; de MySQL :</p>
<p><a href="http://dev.mysql.com/tech-resources/articles/mysql_5.0_psea1.html" target="_blank"><img class="alignnone" src="http://dev.mysql.com/tech-resources/articles/mysql_5.0_psea1.jpg" alt="Les moteurs de stockage MySQL sont des \" width="362" height="261" /></a></p>
<p><span id="more-48"></span>Pour schématiser, et dans un monde idéal, lorsqu&#8217;un index est disponible et &laquo;&nbsp;compatible&nbsp;&raquo; avec votre requête, l&#8217;optimiseur MySQL peut décider de l&#8217;utiliser afin d&#8217;éviter de parcourir l&#8217;ensemble des données des tables concernées.</p>
<p>Un exemple couramment employé pour illustrer ce propos consiste à imaginer la difficultée que nous aurions à retrouver quelqu&#8217;un dans l&#8217;annuaire si nous connaissions son nom mais pas l&#8217;alphabet (qui est notre index)&#8230; Transposé au monde informatique cela donne un serveur MySQL qui compare une à une les entrées du botin pour trouver toutes celles qui correspondent au nom recherché. Si au contraire ce nom est indexé, et si celui-ci commence par exemple par &#8216;T&#8217;, le serveur sait directement qu&#8217;il doit démarrer sa recherche à partir du &#8216;T&#8217;. Imaginez l&#8217;impact en terme de gain de temps lorsque plusieurs jointures sont concernées : deux tables de 10 000 lignes chacune forment un produit cartésien de 100 000 000 lignes environ à étudier&#8230;<br id="mxtu" /><br id="mxtu0" />Les index n&#8217;ont hélas pas que des avantages :<br id="mxtu4" />- Les opérations de mises à jour (INSERT, UPDATE, DELETE) sont en effet ralenties puisqu&#8217;en plus des données, les index doivent eux aussi être mis à jour lors de ces opérations, c&#8217;est le prix à payer&#8230; Ce prix peut néamoins se &laquo;&nbsp;négocier&nbsp;&raquo;, nous verrons cela plus tard.<br id="q.-8" /><br id="q.-80" /><strong>Notre terrain de jeu, la base &laquo;&nbsp;world&nbsp;&raquo;</strong><br id="w4ef" /></p>
<p>MySQL propose au téléchargement plusieurs bases d&#8217;exemple dont <a href="http://dev.mysql.com/doc/" target="_blank">&laquo;&nbsp;world&nbsp;&raquo; et &laquo;&nbsp;sakila&nbsp;&raquo;</a>. Elles épargnent le soin aux utilisateurs de MySQL souhaitant tester quelques requêtes de se constituer eux-mêmes une base de test, celles-ci sont prêtes à l&#8217;emploi.</p>
<p>Nous utiliserons pour nos tests la base &laquo;&nbsp;world&nbsp;&raquo;. Très simple puisque constituée uniquement de trois petites tables MyISAM (Country, CountryLanguage et City), elle permet de se concentrer uniquement sur les index sans perdre de temps à assimiler un schéma plus complexe.<br id="m4x6" /><br id="m4x60" />Si vous souhaitez transformer le script de création SQL de la base &laquo;&nbsp;world&nbsp;&raquo; en une version graphique &laquo;&nbsp;presque&nbsp;&raquo; MCD (les relations entre les tables ne sont pas générées automatiquement pour cette base), le billet précédent est fait pour vous, <a href="http://www.dbnewz.com/2008/06/22/dbdesigner-4-generer-son-mcd-par-reverse-engineering/" target="_blank">les étapes d&#8217;installation et de génération du MCD par &laquo;&nbsp;reverse engineering&nbsp;&raquo; avec DBDesigner 4</a> y sont décrites.<br id="lfl2" /><br id="lfl20" />Voici ce qu&#8217;on peut obtenir pour cette base à partir de DBDesigner :<br id="lfl21" /><br id="lfl22" /><a href="http://www.dbnewz.com/wp-content/uploads/2008/06/world_db_small.png" target="_blank"><img class="alignnone size-thumbnail wp-image-50" title="world_db_small" src="http://www.dbnewz.com/wp-content/uploads/2008/06/world_db_small-150x150.png" alt="" width="150" height="150" /></a><br id="lfl23" /><br id="lfl24" /></p>
<p><strong id="xz5z">Quel type d&#8217;index choisir : PRIMARY KEY, UNIQUE, ou INDEX ?</strong><br id="txto" /><br id="txto0" />Choisissez le type de vos index avec soin :<br id="txto1" />- Une clé primaire (<strong>PRIMARY KEY</strong>) est strictement unique, les NULL ne sont pas autorisés.<br id="e.px" />- Un index de type <strong>UNIQUE </strong>est comparable à une clé primaire, mis à part pour les valeurs NULL puisque celles-ci sont autorisées (et potentiellement en plusieurs occurences).<br id="fy.m" />- Un index de type <strong>INDEX </strong>ou <strong>KEY </strong>(c&#8217;est un alias) signifie simplement que l&#8217;on souhaite indexer une colonne susceptible de contenir des doublons.<br id="blrx" /><br id="blrx0" />Les index de type <strong>FULLTEXT</strong> et <strong>SPATIAL</strong> sont particuliers et méritent un épisode à eux seuls, ils seront donc évoqués ultérieurement.<br id="k7_h" /><br id="k7_h0" />Passons rapidement sur l&#8217;étape de <a href="http://dev.mysql.com/doc/refman/5.0/fr/create-index.html" target="_blank">déclaration d&#8217;un index</a>, celle-ci s&#8217;effectue soit au moment de la création de la table, soit plus tard comme ici :<br id="or3." /><br id="or3.0" />mysql&gt; <code>CREATE INDEX idx_district ON City (District);<br id="n0.-" />Query OK, 4079 rows affected (0.04 sec)<br id="n0.-0" />Records: 4079  Duplicates: 0  Warnings: 0</code><br id="n0.-1" /><br id="n0.-2" />Nous venons de créer un index de type INDEX (autorise les doublons) sur la colonne District de la table City.<br id="n0.-3" /><br id="n0.-4" />Attention, si nous avions tenté la même chose avec un index de type UNIQUE&#8230;<br id="l3as" /><br id="l3as0" />mysql&gt; <code>CREATE UNIQUE INDEX idx_district ON City (District);<br id="l3as1" />ERROR 1062 (23000): Duplicate entry 'Zuid-Holland' for key 2</code><br id="dh1k" /><br id="dh1k0" />&#8230; MySQL nous signale qu&#8217;il y&#8217;a déjà des doublons dans la colonne District, impossible donc de créer un index de type UNIQUE sur celle-ci.<br id="najp" /><br id="najp0" /><strong>Pour supprimer cet index :</strong><br id="najp1" />mysql&gt; <code>DROP INDEX idx_district ON City;</code><br id="a8y02" />Ou :<br id="a8y04" />mysql&gt; <code>ALTER TABLE City DROP INDEX idx_district;</code><br id="bim:" /><strong><br id="bim:0" />Pour visualiser les index d&#8217;une table :</strong><br id="zw.m" /><br id="zw.m0" />mysql&gt; SHOW INDEX FROM Country;</p>
<p><strong>Attention aux doublons !</strong><br id="zw.m17" /><br id="bim:2" />Inutile de rajouter un index de type &laquo;&nbsp;INDEX&nbsp;&raquo; ou encore &laquo;&nbsp;UNIQUE&nbsp;&raquo; sur un champ qui est déjà clé primaire par exemple&#8230; Vous dupliqueriez inutilement les index avec à la clé un gaspillage d&#8217;espace disque/mémoire, des ralentissements inutiles lors des mises à jour, davantage de travail pour l&#8217;optimiseur&#8230;<br id="fq0r" /><br id="p-i7" /><strong id="kw9g2">Quels types de champ indexer ?</strong><br id="bo1r" /><br id="bo1r0" />INT, VARCHAR, BLOB&#8230; ? Quels sont les meilleurs candidats à l&#8217;indexation ?</p>
<p><strong>Plus l&#8217;index est court, mieux c&#8217;est</strong> : un index est en permanence comparé à d&#8217;autres valeurs (celles recherchées), ces comparaisons sont plus rapides si la zone à comparer est plus courte. Des index concis occupent également moins de place sur disque, génèrent moins d&#8217;I/O (activité disque s&#8217;ils ne sont pas en mémoire) et peuvent ainsi être stockés en plus grand nombre dans une même quantité de RAM (pensez au <a href="http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#option_mysqld_key_buffer_size" target="_blank">key_buffer_size</a> de MyISAM par exemple).<br id="e1fk" /><br id="e1fk0" />Bref, si vous désirez stocker une liste de noms de villes sous forme de chaînes de caractères, sachez qu&#8217;il est inutile de réserver un champ de type CHAR(255) : rares sont celles qui atteindront cette longueur, pensez plutôt au VARCHAR qui s&#8217;adaptera à la longueur de vos valeurs.<br id="g6j1" />Plus malin encore, lors de la conception de votre base de données, intéressez-vous aux différentes formes de normalisation : 1NF, 2NF, 3NF, ces méthodes permettent d&#8217;obtenir un schéma qui permet de partir sur de bonnes bases.<br id="ist_" />En gardant cet exemple des villes, si vous stockez dans une table &laquo;&nbsp;inscrits&nbsp;&raquo; toutes les infos contextuelles à un utilisateur, dont sa ville, envisagez de stocker dans une table &laquo;&nbsp;ville&nbsp;&raquo;, toutes les infos qui s&#8217;y rapportent : nom, population, etc. Reliez ensuite votre table &laquo;&nbsp;inscrits&nbsp;&raquo; à la table &laquo;&nbsp;ville&nbsp;&raquo; par la valeur de la clé primaire de ville et vous supprimerez ainsi tous ces libellés de villes identiques au profit d&#8217;un ID bien plus rapide et économique.<br id="yxd7" /></p>
<p><strong>Soyez radins !</strong></p>
<p>Vos index seront d&#8217;autant plus efficaces s&#8217;ils sont apposés sur des champs bien adaptés à vos données. <strong>Ne gaspillez pas le capital &laquo;&nbsp;performance&nbsp;&raquo; de vos index</strong> en utilisant un INT pour stocker par exemple la vitesse légale sur autoroute en France (par temps sec) : 130 km/h&#8230;  Un TINY INT UNSIGNED suffira (permet de stocker les valeurs de 0 à 255). Un INT permet lui de stocker des valeurs comprises entre <code id="cps40" class="literal" style="font-family: Verdana;">-2.147.483.648</code> <code id="cps42" class="literal" style="font-family: Verdana;">2.147.483.647, et en rajoutant l'attri</code>but UNSIGNED, on obtient un rayon d&#8217;action de 0 à plus de 4 milliards ! A quoi bon utiliser un INT dans cet exemple ? Quand on sait de plus qu&#8217;un INT occupe quatre fois plus de place qu&#8217;un TINY INT, l&#8217;impact sur les performances et la perte d&#8217;espace avec une table de plusieurs millions d&#8217;enregistrements est évident&#8230;</p>
<p>Prenez connaissance des <a href="http://dev.mysql.com/doc/refman/5.0/en/numeric-types.html" target="_blank">caractéristiques des types de données</a> que vous utilisez. Visualisez également <a href="http://dev.mysql.com/doc/refman/5.0/fr/storage-requirements.html" target="_blank">la taille requise</a>, c&#8217;est un élement qui peut s&#8217;avérer dissuasif.<br id="x-cm" /><br id="x-cm0" />Pour résumer, n&#8217;indexez pas une colonne en fonction de son type, mais prenez soin dans un premier temps de définir celles-ci avec le type de données qui leur convient le mieux, le plus économique. Prévoyez une marge néanmoins : n&#8217;utilisez pas un TINY INT même UNSIGNED pour un identifiant de clé primaire AUTO_INCREMENT concernant une newsletter d&#8217;un grand service commercial : si tout se passe bien vous devriez rapidement dépasser le seuil des 255 inscrits&#8230; Le type de données juste &laquo;&nbsp;au-dessus&nbsp;&raquo;, SMALLINT UNSIGNED, qui permet d&#8217;aller jusqu&#8217;à 65535, est sans doute plus confortable.<br id="t9cg" /><br id="t9cg0" /><strong id="n1bv">Le tips dbnewz : </strong>utilisez la commande <strong id="dwmm">PROCEDURE ANALYSE()</strong><br id="n1bv0" /><br id="n1bv1" />Cette commande analyse pour vous vos tables et vous propose le type idéal pour vos données&#8230; si vous en avez bien sûr, ça ne peut pas vous aider lors d&#8217;une création de table. En revanche, elle permet &laquo;&nbsp;d&#8217;auditer&nbsp;&raquo; vos enregistrements actuels, d&#8217;en tirer quelques statistiques et propose le type le plus adapté :<br id="n3yt" /><br id="uv4e" />mysql&gt; <code>SELECT Name FROM Country PROCEDURE ANALYSE(10,256)\G<br id="uv4e1" /> Field_name: world.Country.<strong>Name</strong><br id="uv4e2" /> Min_value: Afghanistan<br id="uv4e3" /> Max_value: Zimbabwe<br id="uv4e4" /> <strong>Min_length: 4</strong><br id="uv4e5" /> <strong>Max_length: 44</strong><br id="uv4e6" /> Empties_or_zeros: 0<br id="uv4e7" /> Nulls: 0<br id="uv4e8" /><strong>Avg_value_or_avg_length: 10.0962</strong><br id="uv4e9" /> Std: NULL<br id="uv4e10" /> <strong>Optimal_fieldtype</strong>: VARCHAR(44) NOT NULL<br id="uv4e11" />1 row in set (0.00 sec)</code><br id="uv4e12" /><br id="n3yt0" />Vous pouvez bien sûr effectuer cette requête sur l&#8217;intégralité des champs de l&#8217;une de vos tables (SELECT *&#8230;)<br id="lgar" />Les paramètres fournis à <a href="http://dev.mysql.com/doc/refman/5.1/en/procedure-analyse.html" target="_blank">PROCEDURE ANALYSE ()</a> sont à modifier en fonction de vos souhaits. Par défaut cette fonction a tendance à vouloir transformer toutes vos chaînes de caractères en champs <a href="http://dev.mysql.com/doc/refman/5.1/en/enum.html" target="_blank">ENUM</a> (stockés sous forme numérique en interne), à vous de définir combien de champs ENUM vous êtes prêts à utiliser. Réservez-les pour les cas où le champ représente une courte liste &laquo;&nbsp;fermée&nbsp;&raquo;, ex &laquo;&nbsp;M&nbsp;&raquo; ou &laquo;&nbsp;F&nbsp;&raquo;, les jours de la semaine par exemple, etc.<br id="hj59" /><br id="hj590" /><strong id="ny9g">Où placer ses index : quels sont les champs à indexer ?</strong><br id="ny9g0" /><br id="ny9g1" />Les champs concernés par une clause <strong>WHERE, ORDER BY, GROUP BY, MIN(), MAX()</strong>, ainsi que les champs qui permettent de <strong>relier des tables entre elles</strong>, sont de bons candidats à l&#8217;indexation, exemple :<br id="abxv" /><br id="aln7" /><code>SELECT ci.Name, ci.Population <br id="aln70" />FROM City ci INNER JOIN Country co ON ci.CountryCode = co.Code<br id="aln71" />WHERE ci.Population &gt; 5000000<br id="aln72" />ORDER BY ci.District ASC LIMIT 3</code><br id="aln73" /><br id="ulja" />- Le champ CountryCode de la table City ainsi que le champ Code de la table Country sont tous les deux à indexer. C&#8217;est d&#8217;ailleurs le cas ici puisque ces deux champs sont respectivement clés primaires de la table City et Country.<br id="ulja0" />- Le champ Population est intéressant à indexer, il permet à MySQL de parcourir très rapidement les villes par leur population triée et évite de comparer l&#8217;intégralité des populations de chaque ville, une à une.<br id="fv9-" />- Le champ District est également un candidat à l&#8217;indexation, il peut aider MySQL à trier les données plus rapidement.<br id="o0-s" /><br id="o0-s0" />A retenir : on indexe en priorité les champs impliqués dans les clauses évoqués ci-dessus (en gras), pas forcément ceux présents dans le SELECT.<br id="aln75" /><br id="o98j6" /><strong>Les index composés (ou multiples) et la règle du leftmost prefixing</strong><br id="ebx3" /><br id="ebx30" />&laquo;&nbsp;Faut-il préférer un index unique ou composé&nbsp;&raquo; était <a href="http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/#comments" target="_blank">l&#8217;une des questions posées</a> par l&#8217;un d&#8217;entre vous il y&#8217;a quelques semaines&#8230;<br id="zkm0" /><br id="zkm00" />Un index composé doit se construire en fonction des requêtes que vous effectuez sur la table concernée.<br id="ku.e" />Prenons pour exemple la table City et ses cinq champs (ID, Name, CountryCode, District, Population).<br id="ku.e0" /><br id="ku.e1" />Si les seules requêtes que vous avez sur City sont du type :</p>
<p><code>SELECT ... FROM City WHERE Name = "..."</code></p>
<p>&#8230; Indexez Name et tout ira bien.</p>
<p>Si en revanche il vous arrive de trier non seulement sur &laquo;&nbsp;Name&nbsp;&raquo; mais également sur le code du pays :</p>
<p><code>SELECT ... FROM City WHERE Name = "..." AND CountryCode = "..."</code><br id="f50i0" /><br id="f50i1" />Dans ce cas, plutôt que de laisser MySQL comparer tous les CountryCode de la table avec votre recherche (ex : &laquo;&nbsp;FRA&nbsp;&raquo;), indexez la colonne CountryCode&#8230; Oui mais pas toute seule !<br id="lkbv" />Considérez en effet pour l&#8217;instant que MySQL n&#8217;utilise qu&#8217;un index par table, l&#8217;optimiseur MySQL choisit donc le plus restrictif afin que votre requête s&#8217;exécute le plus rapidement possible (nous verrons plus tard les cas particuliers où MySQL peut tirer parti de plusieurs index).<br id="w0mb" />Conséquence, il vous faut trouver un index qui soit utilisable pour vos deux critères de recherche : &laquo;&nbsp;Name&nbsp;&raquo; et &laquo;&nbsp;CountryCode&nbsp;&raquo;. La solution : créez un index multiple sur ces deux champs.<br id="tilr" /><br id="tilr0" />Dès lors que vous utilisez un index multiple, <strong>la règle </strong><strong>du leftmost prefixing</strong> rentre en jeu. Trop souvent méconnue, elle permet pourtant de créer ses index de façon efficace et d&#8217;éviter les doublons.<br id="ny9g2" /><br id="mqai" />Afin d&#8217;illustrer cette règle, ajoutons cette fois à la table City un index multiple sur les champs Name, CountryCode, District et Population :</p>
<p>mysql&gt; <code>CREATE INDEX name_cc_dis_pop ON City (Name, CountryCode, District, Population);</code><br id="ub980" /></p>
<p>Voici ce que l&#8217;on obtient avec le <strong>SHOW INDEX</strong> correspondant (la clé primaire existait déjà à la création de la table, ci-dessous une vue partielle des résultats réels) :</p>
<p>mysql&gt; <code>SHOW INDEX FROM City;<br id="cvh60" /></code></p>
<p>|<code> <strong>Key_name</strong> | <strong>Seq_in_index</strong> | <strong>Column_name</strong> <br id="cvh62" />+-------+------------+-----------------+</code></p>
<p><code><strong>|name_cc_dis_pop</strong> |   <strong>1</strong> |  <strong>Name</strong> </code></p>
<p><strong>|</strong><code><strong>name_cc_dis_pop</strong> |   <strong>2</strong> | <strong>CountryCode</strong> </code></p>
<p><code><strong>|</strong><strong>name_cc_dis_pop</strong> |   <strong>3</strong> | <strong>District</strong> </code></p>
<p><code> <strong>|name_cc_dis_pop</strong> |   <strong>4</strong> | <strong>Population</strong> |  <br id="cvh68" />+-------+------------+-----------------+</code></p>
<p>On remarque par rapport au SHOW INDEX précédent que cette fois-ci nous avons la colonne &laquo;&nbsp;Seq_in_index&nbsp;&raquo; qui s&#8217;incrémente pour chaque colonne qui compose notre index multiple. La position de chaque index dans cette séquence a une importance, c&#8217;est ce que nous allons voir maintenant.</p>
<p>Tel quel, cet index multiple sera potentiellement utilisé pour les requêtes de ce type :</p>
<p><code>SELECT ... FROM City WHERE Name = ... AND CountryCode = ... AND District = ... AND Population = ...<br id="w2qj" />SELECT ... FROM City WHERE Name = ... AND CountryCode = ... AND District = ... <br id="w2qj0" />SELECT ... FROM City WHERE Name = ... AND CountryCode = ... <br id="w2qj1" />SELECT ... FROM City WHERE Name = ...</code><br id="w2qj2" /><br id="w2qj3" />Une fois cette &laquo;&nbsp;logique&nbsp;&raquo; acquise, on comprend qu&#8217;il est inutile de rajouter un index sur Name par exemple puisque cette colonne est déjà indexée grâce à cet index multiple. Idem pour notre exemple précédent  d&#8217;index multiple concernant les champs Name et CountryCode, là encore inutile de recréer un index sur ces deux champs puisque ces derniers sont déjà représentés dans notre dernier exemple.<br id="niuy" /><br id="niuy0" />En revanche, si l&#8217;ordre de vos champs ne respecte pas l&#8217;ordre de séquence de votre index, comme ici :</p>
<p><code>SELECT ... FROM City WHERE Name = ... AND Population = ..</code></p>
<p>Cette requête ne bénéficiera pas complètement de l&#8217;index multiple précedemment crée, cela dit l&#8217;optimiseur tirera sûrement parti de cet index pour la colonne Name, mais pas pour le second critère de recherche.<br id="ewwh0" /><br id="ewwh1" />De même si votre requête est du type :</p>
<p><code>SELECT ... FROM City WHERE CountryCode = ...<br id="ewwh3" />SELECT ... FROM City WHERE District = ...<br id="ewwh4" />SELECT ... FROM City WHERE Population = ...<br id="ewwh5" />SELECT ... FROM City WHERE CountryCode = ... AND District = ... AND Population = ...</code><br id="ewwh6" /></p>
<p>&#8230; et autres variations qui ne débutent pas avec &laquo;&nbsp;Name&nbsp;&raquo; et ne respectent pas ensuite l&#8217;ordre de séquence de l&#8217;index (Name, CountryCode, District, et Population), l&#8217;index ne sera pas utilisé.<br id="z9sx" /><br id="z9sx0" />En conséquence, il est donc tout à fait légitime d&#8217;indexer par ailleurs la colonne Population seule si vous avez des requêtes du type :</p>
<p><code>SELECT ... FROM City WHERE Population &gt; ... </code><br id="p_16" /><br id="ny9g4" /><strong>Mesurez l&#8217;efficacité des index avec EXPLAIN</strong><br id="n6b2" /></p>
<p>Impossible d&#8217;évoquer les index sans parler de la commande <strong>EXPLAIN</strong>. Absolument <strong id="kjkb">fondamentale</strong>, elle affiche le plan d&#8217;exécution décidé par l&#8217;optimiseur MySQL et vous permet de mesurer si oui ou non vos index sont réellement utilisés.<br id="tqy0" /><br id="tqy00" />Reprenons une des premières requêtes de ce billet et rajoutons-lui le mot clé EXPLAIN :<br id="fo2v" />(on considère ici que la table City ne contient que sa clé primaire, pas les index rajoutés précedemment)</p>
<p>mysql&gt; <code><strong>EXPLAIN </strong>SELECT ci.Name, ci.Population<br id="dhkz" />FROM City ci INNER JOIN Country co ON ci.CountryCode = co.Code<br id="dhkz0" />WHERE ci.Population &gt; 5000000<br id="dhkz1" />ORDER BY ci.District ASC LIMIT 3\G<br id="dhkz2" /></code></p>
<p><code>*************************** 1. row *************<br />
id: 1<br />
select_type: SIMPLE<br />
table: ci<br />
type: ALL<br />
<strong>possible_keys: NULL</strong><br />
key: NULL<br />
key_len: NULL<br />
ref: NULL<br />
<strong>rows: 4079</strong><br />
Extra: Using where; Using filesort<br />
*************************** 2. row *************<br />
id: 1<br />
select_type: SIMPLE<br />
table: co<br />
type: eq_ref<br />
<strong>possible_keys: PRIMARY</strong><br />
<strong>key: PRIMARY</strong><br />
key_len: 3<br />
<strong>ref: world.ci.CountryCode</strong><br />
<strong>rows: 1</strong><br />
<strong>Extra: Using index</strong><br />
2 rows in set (0.00 sec)</code></p>
<p>La commande EXPLAIN sera étudiée plus précisemment dans un autre épisode, pour le moment contentons-nous de prêter attention aux champs en gras :</p>
<p>- Sur la première ligne le type ALL signale que MySQL doit effectuer un &laquo;&nbsp;full table scan&nbsp;&raquo; c&#8217;est à dire parcourir entièrement la table City qui compte 4079 enregistrements, ceci afin de repérer quelles sont les villes qui ont une population supérieure à 5M d&#8217;habitants. Aucun index/key n&#8217;a pu être utilisé (possible_keys : NULL) pour résoudre cette partie de la requête. La colonne &laquo;&nbsp;rows&nbsp;&raquo; indique le nombre approximatif d&#8217;enregistrements que MySQL pense devoir analyser pour mener à bien l&#8217;opération.</p>
<p>- La seconde ligne nous indique que cette fois MySQL a un candidat pour l&#8217;indexation, il s&#8217;agit de la clé primaire de la table Country, la jointure s&#8217;effectuant avec le champ &laquo;&nbsp;CountryCode&nbsp;&raquo; de la table City, qui est également une clé primaire. Résultat : MySQL effectue la correspondance très rapidement (rows : 1 et extra : Using index).<br id="sewv" /><br id="sewv0" />Ceci répond à une des questions posées précedemment par un lecteur : &laquo;&nbsp;Quand préférer le FULL TABLE SCAN à l&#8217;index ?&nbsp;&raquo; C&#8217;est en fait le travail de l&#8217;optimiseur, il doit déterminer si oui ou non un index vous fera gagner du temps. Il se peut qu&#8217;il se trompe (rarement), nous verrons comment orienter ses choix si besoin.<br id="ai:q" /><br id="ai:q0" />Rajoutons maintenant un index de type &laquo;&nbsp;INDEX&nbsp;&raquo; sur la colonne Population :<br id="ai:q1" /></p>
<p>mysql&gt; <code>CREATE INDEX idx_pop ON City (Population);</code><br id="j6rd" /><br id="j6rd0" />Puis appliquons à nouveau la même commande EXPLAIN, on obtient cette fois :</p>
<p><code>*************************** 1. row ************<br />
id: 1<br />
select_type: SIMPLE<br />
table: ci<br />
type: range<br />
<strong>possible_keys: idx_pop</strong><br />
<strong>key: idx_pop</strong><br />
key_len: 4<br />
ref: NULL<br />
<strong>rows: 25</strong><br />
Extra: Using where; Using filesort<br />
*************************** 2. row ************<br />
id: 1<br />
select_type: SIMPLE<br />
table: co<br />
type: eq_ref<br />
<strong>possible_keys: PRIMARY<br />
key: PRIMARY</strong><br />
key_len: 3<br />
ref: world.ci.CountryCode<br />
<strong>rows: 1</strong><br />
Extra: Using index<br />
2 rows in set (0.02 sec)<br id="ecl19" /></code><br id="ecl110" />Notre index a permis à MySQL de lire <strong>160 fois moins de lignes</strong> cette fois-ci par rapport à l&#8217;exemple précédent&#8230; Seules 25 lignes de la table City sont lues désormais (au lieu des 4079 lignes précedemment parcourues). C&#8217;est un gain très intéressant en termes de ressources serveur ! Amateurs de chiffres, des benchmarks sont prévus dans la suite de cette série.<br id="laec" /><br id="laec0" />Afin de patienter jusqu&#8217;aux prochains épisodes justement, vous pouvez commencer par appliquer les quelques conseils de ce billet tout en relisant pourquoi pas l&#8217;article concernant <a href="http://www.dbnewz.com/2008/05/19/choisir-limplementation-de-ses-index-b-tree-ou-hash-quelles-differences/" target="_blank">l&#8217;implémentation des index (BTREE ou HASH)</a> selon le type de votre moteur de stockage (MyISAM, InnoDB ou MEMORY). <br id="dhkz10" /><br id="m:ru" />Il reste certaines de vos questions en suspens, elles ne sont pas oubliées et seront débattues ici très prochainement.</p>
<p>Si vous avez des questions ou des remarques concernant cette première étape, n&#8217;hésitez pas.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2008/06/27/les-index-mysql-types-placements-efficacite/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>DBDesigner 4 : générer son MCD par reverse engineering</title>
		<link>http://www.dbnewz.com/2008/06/22/dbdesigner-4-generer-son-mcd-par-reverse-engineering/</link>
		<comments>http://www.dbnewz.com/2008/06/22/dbdesigner-4-generer-son-mcd-par-reverse-engineering/#comments</comments>
		<pubDate>Sun, 22 Jun 2008 10:54:07 +0000</pubDate>
		<dc:creator>arnaud</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[outils]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=46</guid>
		<description><![CDATA[Disposer d&#8217;un MCD (modèle conceptuel de données) lorsqu&#8217;on travaille sur une requête SQL impliquant différentes tables représente un gain de temps.Il est en effet plus rapide de jeter un coup d&#8217;oeil sur un MCD afin de repérer quels sont les champs qui lient une table à une autre plutôt que d&#8217;enchaîner les &#171;&#160;DESC ma_table&#160;&#187;, puis [...]]]></description>
			<content:encoded><![CDATA[<p>Disposer d&#8217;un MCD (modèle conceptuel de données) lorsqu&#8217;on travaille sur une requête SQL impliquant différentes tables représente un <strong id="tqz_">gain de temps</strong>.<br id="d0eo" />Il est en effet plus rapide de jeter un coup d&#8217;oeil sur un MCD afin de repérer quels sont les champs qui lient une table à une autre plutôt que d&#8217;enchaîner les &laquo;&nbsp;DESC ma_table&nbsp;&raquo;, puis repérer la clé primaire et les éventuelles clés étrangères, et rebolote sur la ou les tables de destination&#8230;<br id="ixuk" /><br id="ixuk0" />La prochaine série d&#8217;articles sur les index MySQL va nous amener à enchaîner quelques requêtes sur une des deux bases d&#8217;exemple disponibles sur le site de MySQL : <a href="http://dev.mysql.com/doc/" target="_blank">world et sakila</a>, le prétexte est donc tout trouvé pour évoquer ici la solution que j&#8217;ai retenu pour obtenir le MCD de ces tables : DBDesigner 4.</p>
<p>Cet outil n&#8217;est pas nouveau, son successeur officiel est même déjà connu, il s&#8217;agit de <a href="http://dev.mysql.com/workbench/" target="_blank">MySQL Workbench</a>. Celui-ci n&#8217;étant pas encore disponible sous linux, nous utiliserons son ancêtre et plus particulièrement sa fonctionnalité de &laquo;&nbsp;reverse engineering&nbsp;&raquo; : une fois connecté à votre base, DBDesigner 4 va générer sous forme graphique vos tables, leurs descriptions, et si tout se passe bien, les relations entre vos tables.</p>
<p><span id="more-46"></span><strong>Installation (fastidieuse) sous linux</strong><br id="m-dl0" /><br id="m-dl1" />L&#8217;installation de DBDesigner 4 sous linux (ubuntu 8.04 pour ma part) n&#8217;est pas de tout repos&#8230; J&#8217;ai rencontré toutes les erreurs potentielles croisées sur les différents tutoriels sur le sujet. Résultat des courses : il apparaît plus simple d&#8217;installer DBDesigner 4 en passant par <a href="http://www.winehq.org/" target="_blank">wine</a>, les <strong>problèmes de librairies manquantes</strong> disparaissent et les polices sont plus travaillées.<br id="swio" />Si néanmoins vous souhaitez vous passer de wine, voici quelques pistes : <a href="http://wiki.splitbrain.org/dbdesigner" target="_blank">http://wiki.splitbrain.org/dbdesigner</a> et un <a href="http://ubuntuforums.org/showthread.php?t=125911" target="_blank">how-to</a> tiré des forums ubuntu. Des solutions sont proposées pour ces fameuses libraries manquantes, cela dit j&#8217;avais toujours des problèmes malgré ces manipulations.<br id="tw3i" /><br id="tw3i0" />Une fois wine installé (<code>apt-get install wine</code>), téléchargez simplement <a href="http://www.fabforce.net/downloads.php" target="_blank">le fichier d&#8217;installation de DBDesigner 4</a> pour windows.</p>
<p>L&#8217;installation s&#8217;effectue simplement :</p>
<p><code>root@u200:/opt# wine DBDesigner4.0.5.6_Setup.exe</code> <br id="lq1_" /><br id="vkpd" />Si vous êtes sous MySQL 5, vous devez <strong>modifier le mot de passe</strong> de l&#8217;utilisateur à partir duquel vous vous connectez à DBDesigner de la façon suivante :<br id="vkpd0" /><br id="lq1_0" />mysql&gt; <code>SET PASSWORD FOR 'dbdesign'@'localhost' = OLD_PASSWORD('dbdesign');</code></p>
<p>Depuis sa version 5, MySQL utilise en effet un mécanisme d&#8217;authentification qui est incompatible avec les versions précédentes (&lt; 4.1). Afin d&#8217;assurer une rétro compatibilité, il est néanmoins possible avec MySQL 5 d&#8217;utiliser la commande &laquo;&nbsp;<a href="http://dev.mysql.com/doc/refman/5.1/en/encryption-functions.html#function_old-password" target="_blank">OLD_PASSWORD</a>&laquo;&nbsp;, le format utilisé pour le mot de passe sera alors compatible avec MySQL 4 (short hash contre long hash pour MySQL 5).<br id="fvds" /><br id="rwen" />Lancement de l&#8217;application:</p>
<p><code>root@u200:~/.wine/drive_c/Program Files/fabFORCE# wine DBDesigner4.exe</code></p>
<p><strong>Obtenir le schéma de sa base</strong></p>
<p>Pour générer votre MCD, et selon votre langue d&#8217;installation :</p>
<p>- Choisissez le menu &laquo;&nbsp;Database&nbsp;&raquo;, puis &laquo;&nbsp;Reverse Engineering&nbsp;&raquo;.</p>
<p>- Selectionnez une connexion à la base de données ou créez la vôtre : &laquo;&nbsp;Localhost 127.0.0.1&#8243; par exemple, puis renseignez vos login/pwd et validez, n&#8217;oubliez pas la commande &laquo;&nbsp;OLD_PASSWORD&nbsp;&raquo; si besoin.</p>
<p>- La fenêtre &laquo;&nbsp;Reverse Engineering&nbsp;&raquo; affiche alors la liste des tables de la base, par défaut elles sont toutes cochées. Remarquez un peu plus bas l&#8217;option &laquo;&nbsp;<a href="http://www.fabforce.net/dbdesigner4/doc/db.html#reveng" target="_blank">Build Relations</a>&laquo;&nbsp;, cochez-là puis indiquez &laquo;&nbsp;Build Relations based on primary keys&nbsp;&raquo;. C&#8217;est grâce à cette option que vous indiquez à DBDesigner de relier vos tables entre elles (si possible).</p>
<p>- Si tout s&#8217;est bien passé, l&#8217;étape suivante affiche vos tables, leurs champs, leurs clés&#8230;</p>
<p><strong>Remarques : </strong></p>
<p>- DBDesigner ne parvient pas à joindre les tables de la base &laquo;&nbsp;world&nbsp;&raquo; entre elles, les noms des clés primaires sont différents et les clés étrangères absentes de la définition des tables (MyISAM), vous pouvez néanmoins rajouter vos différentes relations à la main.</p>
<p>- En ce qui concerne la base &laquo;&nbsp;sakila&nbsp;&raquo; les relations entre les tables sont bien présentes :</p>
<p><a href="http://www.dbnewz.com/wp-content/uploads/2008/06/sakila_db_small.png"><img class="alignnone size-full wp-image-47" title="sakila_db_small" src="http://www.dbnewz.com/wp-content/uploads/2008/06/sakila_db_small.png" alt="Vue partielle de la base sakila après reverse engineering" width="400" height="304" /></a></p>
<p>Vous pouvez à présent d&#8217;un seul coup d&#8217;oeil construire vos requêtes, les jointures entre les tables sont ici évidentes.</p>
<p>- La console dans laquelle vous lancez DBDesigner va peut-être contenir quelques messages d&#8217;erreur du type:</p>
<p><code>QPixmap::operator=: Cannot assign to pixmap during painting<br id="yfb50" />QPopupMenu: (unnamed) Popup has invalid menu item</code></p>
<p>Cela n&#8217;a pas bloqué le comportement de l&#8217;application lors de mes tests cependant DBDesigner a la facheuse tendance à <strong>ouvrir des boites de dialogues sous les fenêtres déjà existantes</strong>, n&#8217;hésitez pas notamment lors de votre connexion à la base, à déplacer vos fenêtres (la principale et celle consacrée au reverse engineering) pour faire apparaître la boîte de dialogue vous invitant à renseigner vos identifiants de connexion. A noter qu&#8217;il existe là aussi des solutions pour régler ce problème de pop-up, cf les tutoriels cités en début d&#8217;article.</p>
<p>Pour explorer les autres fonctionnalités de DBDesigner 4, ou bien encore tester (sous windows uniquement pour le moment) son successeur MySQL Workbench, rendez-vous sur <a href="http://www.fabforce.net/dbdesigner4/" target="_blank">fabforce.net</a>, vous y trouverez les fichiers d&#8217;installation, des captures d&#8217;écran et de la documentation.</p>
<p>Si vous connaissez d&#8217;autres outils intéressants disposant d&#8217;un module de &laquo;&nbsp;reverse engineering&nbsp;&raquo;, partagez-les dans les commentaires, ils sont là pour ça.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2008/06/22/dbdesigner-4-generer-son-mcd-par-reverse-engineering/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>&#171;&#160;Les index MySQL&#160;&#187; : la série dont vous êtes le héros</title>
		<link>http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/</link>
		<comments>http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/#comments</comments>
		<pubDate>Thu, 05 Jun 2008 20:51:32 +0000</pubDate>
		<dc:creator>arnaud</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/</guid>
		<description><![CDATA[Un titre sans doute bien étrange pour certains et qui rappelera des souvenirs à d&#8217;autres, surtout à ceux qui ont déjà parcouru un de ces livres &#171;&#160;dont vous êtes le héros&#171;&#160;&#8230;
Afin que les choses soient claires pour tout le monde, je vous propose en fait de participer à la conception du sommaire de la future [...]]]></description>
			<content:encoded><![CDATA[<p>Un titre sans doute bien étrange pour certains et qui rappelera des souvenirs à d&#8217;autres, surtout à ceux qui ont déjà parcouru un de ces livres &laquo;&nbsp;<a href="http://fr.wikipedia.org/wiki/Livre-jeu" target="_blank">dont vous êtes le héros</a>&laquo;&nbsp;&#8230;</p>
<p>Afin que les choses soient claires pour tout le monde, je vous propose en fait de participer à la conception du sommaire de la future série d&#8217;articles sur les index qui sera publiée prochainement sur dbnewz.</p>
<p>L&#8217;indexation est en effet un thème auquel il faut <strong>absolument</strong> s&#8217;intéresser, tout d&#8217;abord pour éviter des catastrophes et bien sûr pour optimiser les performances !</p>
<p><span id="more-43"></span></p>
<p>Les index sont une arme redoutable, à double tranchant : oubliez-les et ils se rappeleront violemment à votre bon souvenir &laquo;&nbsp;ça ne fait pas 5 min que nous sommes en production et la base ne répond plus, la sonde cpu est à 100%, on ne peut même plus se connecter à la base (too many connections), qu&#8217;est ce qui se passe ?!&nbsp;&raquo; Dans le même genre d&#8217;extrêmes, invitez-les partout et vous compliquerez la tâche de l&#8217;optimiseur MySQL (chargé de concevoir le meilleur plan d&#8217;exécution possible pour vos requêtes), ralentirez vos mises à jour et augmenterez la taille de vos bases de données, générant d&#8217;autres problèmes&#8230;<br />
En revanche, une bonne stratégie d&#8217;indexation permet parfois d&#8217;obtenir des gains en performance très importants, parmi les plus importants qu&#8217;on puisse obtenir en agissant sur les réglages d&#8217;une configuration MySQL &laquo;&nbsp;classique&nbsp;&raquo;.</p>
<p>Ce que je vous propose donc, c&#8217;est non pas de m&#8217;envoyer des copies de vos plans d&#8217;exécution ou les schémas de vos tables, mais plutôt de poster ici vos attentes, les zones d&#8217;ombre que vous souhaiteriez éclaircir concernant les index.<br />
Exemples :<br />
&laquo;&nbsp;Pourquoi un index disponible n&#8217;est pas pris en compte ?&nbsp;&raquo;<br />
&laquo;&nbsp;En quoi indexer peut améliorer les performances ?&nbsp;&raquo;<br />
&laquo;&nbsp;Quels sont les champs à indexer ?&nbsp;&raquo;, etc, etc.</p>
<p>Profitez de l&#8217;occasion qui vous est donnée pour poster dans les commentaires de ce billet vos problématiques, vos souhaits, j&#8217;en tiendrai compte lors de la conception des différents chapitres que comportera cette série qui deviendra du &laquo;&nbsp;cousu main&nbsp;&raquo; pour tous les participants, intéressant non ?</p>
<p>A vos claviers <img src='http://www.dbnewz.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2008/06/05/les-index-mysql-la-serie-dont-vous-etes-le-heros/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Lancer un script mysql sans donner ni l&#8217;utilisateur ni le mot de passe sur la ligne de commande</title>
		<link>http://www.dbnewz.com/2007/10/04/lancer-un-script-mysql-sans-donner-ni-lutilisateur-ni-le-mot-de-passe-sur-la-ligne-de-commande/</link>
		<comments>http://www.dbnewz.com/2007/10/04/lancer-un-script-mysql-sans-donner-ni-lutilisateur-ni-le-mot-de-passe-sur-la-ligne-de-commande/#comments</comments>
		<pubDate>Thu, 04 Oct 2007 09:34:18 +0000</pubDate>
		<dc:creator>laurent</dc:creator>
				<category><![CDATA[4.0]]></category>
		<category><![CDATA[4.1]]></category>
		<category><![CDATA[5.0]]></category>
		<category><![CDATA[5.1]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/2007-10/29-lancer-un-script-mysql-sans-donner-ni-lutilisateur-ni-le-mot-de-passe-sur-la-ligne-de-commande.htm</guid>
		<description><![CDATA[Voici mon problème du jour : Comment lancer un script (de maintenance par exemple) qui fait appel à mysql, sans stocker en dur le nom de l&#8217;utilisateur et le mot de passe (ce qui est mal, très très mal). Le but est que seul un utilisateur privilégié (je n&#8217;ai pas forcément dit root ! je [...]]]></description>
			<content:encoded><![CDATA[<p>Voici mon problème du jour : <em>Comment lancer un script (de maintenance par exemple) qui fait appel à mysql, sans stocker en dur le nom de l&#8217;utilisateur et le mot de passe (ce qui est mal, très très mal). Le but est que seul un utilisateur privilégié (je n&#8217;ai pas forcément dit root ! je pense plutôt à un compte système comme mysql par exemple) puisse lancer ce script.</em></p>
<p>C&#8217;est plutôt facile, et je fournis trois solutions pour la peine :</p>
<ol>
<li>Avoir un fichier de configuration pour le script accessible seulement par l&#8217;utilisateur privilégiéOn définit dans le fichier de configuration des variables d&#8217;environnement, une pour le user, une autre pour le mot de passe. Dans le script il ne reste qu&#8217;à utiliser la commande source pour récuperer ses variables (seul l&#8217;utilisateur privilégié pourra lire le fichier). Simple et efficace.</li>
<li>Avoir un fichier de configuration pour mysql accessible seulement par l&#8217;utilisateur privilégiéVariante de la précédente : on crée un fichier de configuration spécifique pour mysql (que l&#8217;on pourra mettre par exemple dans /etc/mysql mais il n&#8217;y a aucune obligation). Et on utilise l&#8217;option &#8211;defaults-file pour que mysql lise le contenu du fichier (il lit notamment les sections [client] et [mysql]). Exemple de fichier:<br />
<code><br />
[client]<br />
host     = localhost<br />
user     = votre_user<br />
password = votre_mot_de_passe<br />
socket   = /var/run/mysqld/mysqld.sock<br />
</code><br />
Bonus : on peut spécifier d&#8217;autres options pour influer sur mysql (en vrac, le nom de serveur, le jeu de caractères&#8230;).</li>
<li>Pour ceux qui ont plusieurs scripts mais qui ont des options qui diffèrent légèrementVous avez des scripts quasiment identiques mais les options diffèrent légèrement (ou vous voulez centraliser tout en un seul fichier) : c&#8217;est possible. C&#8217;est une variante du cas précedent : on écrit un fichier de configuration mysql que l&#8217;on découpe en plusieurs sections. Ensuite, on fait appel au programme my_print_defaults ! my_print_defaults examine le fichier de configuration (option -c pour spécifier le votre&#8230;) et donne en sortie les paramètres à passer à mysql sous forme d&#8217;argument. Exemple :<br />
<code><br />
$ my_print_defaults -c /etc/mysql/maconf.cnf client<br />
--host=localhost<br />
--user=votre_user<br />
--password=votre_mot_de_passe<br />
--socket=/var/run/mysqld/mysqld.sock<br />
</code><br />
Il ne reste plus qu&#8217;à passer cela à mysql en récuperant la sortie dans une variable (ou en utilisant cette petite merveille de <a href="http://en.wikipedia.org/wiki/xargs">xargs</a>).</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2007/10/04/lancer-un-script-mysql-sans-donner-ni-lutilisateur-ni-le-mot-de-passe-sur-la-ligne-de-commande/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
