<?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</title>
	<atom:link href="http://www.dbnewz.com/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>Fri, 17 Feb 2012 16:15:17 +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>Comprendre son fichier de configuration &#8211; 3è partie</title>
		<link>http://www.dbnewz.com/2012/02/17/comprendre-son-fichier-de-configuration-3e-partie/</link>
		<comments>http://www.dbnewz.com/2012/02/17/comprendre-son-fichier-de-configuration-3e-partie/#comments</comments>
		<pubDate>Fri, 17 Feb 2012 16:15:17 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=1014</guid>
		<description><![CDATA[Dernier volet dans notre série sur la configuration de MySQL, cet article va vous donner les clés pour paramétrer correctement et simplement InnoDB.
InnoDB est un moteur extrêmement complexe qui mériterait un livre complet pour expliquer son fonctionnement. Selon les versions, il peut exister plus de 80 paramètres pour contrôler son fonctionnement : pas question de [...]]]></description>
			<content:encoded><![CDATA[<p>Dernier volet dans notre série sur la configuration de MySQL, cet article va vous donner les clés pour paramétrer correctement et simplement InnoDB.<span id="more-1014"></span></p>
<p>InnoDB est un moteur extrêmement complexe qui mériterait un livre complet pour expliquer son fonctionnement. Selon les versions, il peut exister plus de 80 paramètres pour contrôler son fonctionnement : pas question de les examiner tous ! Je vais me concentrer sur les 2 principaux, qui devraient suffir dans la majorité des cas.</p>
<p>Le 1er, <code>innodb_buffer_pool_size</code>, contrôle la taille du cache mémoire pour les données et les index. Il s&#8217;agit assurément du paramètre de base pour obtenir de bonnes performances InnoDB puisqu&#8217;il vous permet de remplacer de nombreuses lectures ou écritures sur disque par des lectures ou écritures en mémoire. Pour le configurer, c&#8217;est simple, essayez de mettre la valeur la plus élevée possible&#8230; Evidemment, il faut penser à garder un peu de mémoire pour les connexions, les tables temporaires, les éventuelles tables MyISAM&#8230; et l&#8217;OS !</p>
<p>Le 2nd, <code>innodb_log_file_size</code>, contrôle la taille des redo logs. A quoi servent ces redo logs en quelques mots ? Pour des raisons de performance, quand un client fait une modification dans une table, InnoDB exécute la modification en mémoire instantanément (dans le fameux buffer pool que nous avons appris à configurer ci-dessus) mais garde pour plus tard l&#8217;exécution de la modification sur le disque.<br />
Que se passe-t-il si le serveur subit un crash entre le moment où la donnée est écrite en mémoire et le moment où la donnée est écrite sur le disque ? La donnée est perdue tout simplement. Il faut donc un mécanisme supplémentaire pour assurer la persistence des données en cas de crash : ce sont les fameux redo logs. En réalité, au moment où on exécute une modification, InnoDB l&#8217;exécute en mémoire et la note également dans ses redo logs avant de signaler au client que la modification est bien enregistrée. En cas de crash, la lecture des redo logs va permettre de reconstituer les données qui étaient en mémoire mais pas encore sauvegardées sur le disque.</p>
<p>Quel est l&#8217;intérêt des redo logs par rapport à une écriture directe dans le fichier de données ? En général, les modifications de données successives ont lieu à des endroits radicalement différents, ce qui se traduit pour le disque par de nombreuses écritures aléatoires, extrêmement lentes. Au contraire, écrire des modifications successives dans les redo logs consiste à écrire chaque modification à la fin du fichier : il s&#8217;agit là d&#8217;écritures séquentielles, nettement plus rapides. Au moment de retranscrire le contenu des redo logs vers les tables, InnoDB pourra en profiter pour regrouper les écritures pour les rendre les plus séquentielles possibles. Ce mécanisme permet donc d&#8217;éviter de nombreuses et coûteuses écritures aléatoires.</p>
<p>Et que se passe-t-il quand les redo logs sont pleins ? Il n&#8217;est tout simplement plus possible de faire de modifications ! InnoDB tente donc de nettoyer régulièrement ces logs pour qu&#8217;il reste toujours de la place.</p>
<p>Bref, si vous m&#8217;avez suivi, les redo logs permettent de gagner en performance en écriture, on a donc tout intérêt à configurer une valeur aussi grande que possible. Cependant, il faut également garder à l&#8217;esprit qu&#8217;en cas de crash, les redo logs vont être utilisés pour restaurer les données, et cette restauration sera d&#8217;autant plus longue que la taille des redo logs sera grande&#8230; Il va donc falloir trouver un compromis entre ces 2 exigences contradictoires.</p>
<p>Baron de Percona a écrit un <a href="http://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodb-log-file-size/">très bon article</a> sur le sujet. Sans reprendre dans le détail ce qu&#8217;il explique, le principe de son calcul est de déterminer le volume d&#8217;écriture en période de pleine charge et de faire en sorte que les redo logs puisse contenir 1 heure d&#8217;écriture à pleine charge.</p>
<p>J&#8217;en profite au passage pour vous inciter à la méfiance si vous lisez des articles vous parlant de fixer cette variable à 25% de la taille du buffer pool par exemple. C&#8217;est faux et d&#8217;ailleurs bien souvent irréalisable : à part en MySQL 5.6 et sur les versions récentes de Percona Server, la taille des redo logs est limitée à 4 Go. Avec un buffer pool de 32 Go ou plus, difficile de respecter cette &laquo;&nbsp;règle&nbsp;&raquo; des 25 %&#8230;</p>
<p>Je fais une petite parenthèse sur le paramètre <code>innodb_file_per_table</code>. Ce paramètre fait en sorte que les données de chaque table soient stockées dans un tablespace spécifique au lieu du tablespace principal. Il ne faut pas attendre de cette option des gains en performance, mais plutôt en flexibilité : si vous avez 200 Go de données, au lieu d&#8217;avoir un énorme fichier ibdata de 200 Go, vous aurez autant de fichiers que de tables. De plus, si jamais vous supprimez une table, le tablespace spécifique à la table sera supprimé et vous récupérerez l&#8217;espace disque correspondant, ce qui ne sera jamais le cas si vous stockez toutes vos données dans le tablespace principal.</p>
<p>A noter que Percona a mis en ligne <a href="http://tools.percona.com/wizard">un outil</a> vous permettant d&#8217;obtenir un fichier de configuration basique (c&#8217;est-à-dire sans doute pas optimisé au micro-poil près, mais en tout cas sans grossière erreur pouvant coûter très cher).</p>
<p>Et voilà ! En configurant correctement ces 2 paramètres, vous aurez réalisé l&#8217;essentiel du paramétrage d&#8217;InnoDB ! Evidemment, ensuite, il est possible d&#8217;aller un peu plus loin en jouant sur d&#8217;autres paramètres comme <code>innodb_flush_log_at_trx_commit</code> ou <code>innodb_flush_method</code>. Mais ce sera peut-être pour un prochain article !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2012/02/17/comprendre-son-fichier-de-configuration-3e-partie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Meetup MySQL le 1er février à Paris</title>
		<link>http://www.dbnewz.com/2012/01/31/meetup-mysql-le-1er-fevrier-a-paris/</link>
		<comments>http://www.dbnewz.com/2012/01/31/meetup-mysql-le-1er-fevrier-a-paris/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 16:03:38 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[meetings]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=1011</guid>
		<description><![CDATA[A l&#8217;initiative de SkySQL et LeMug.fr, Colin Charles de Monty Program viendra nous parler de MariaDB demain mercredi 1er février au Patricks Irish Pub à Paris à partir de 18h. Si vous souhaitez en connaître un peu plus sur ce fork de MySQL, n&#8217;hésitez pas !
Plus d&#8217;infos sur la page officiellle
]]></description>
			<content:encoded><![CDATA[<p>A l&#8217;initiative de SkySQL et LeMug.fr, Colin Charles de Monty Program viendra nous parler de MariaDB demain mercredi 1er février au Patricks Irish Pub à Paris à partir de 18h. Si vous souhaitez en connaître un peu plus sur ce fork de MySQL, n&#8217;hésitez pas !<br />
Plus d&#8217;infos sur <a href="http://www.lemug.fr/2012/01-fevrier-2012-meetup-mariadb/">la page officiellle</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2012/01/31/meetup-mysql-le-1er-fevrier-a-paris/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Comprendre son fichier de configuration &#8211; 2ème partie</title>
		<link>http://www.dbnewz.com/2011/12/22/comprendre-son-fichier-de-configuration-2eme-partie/</link>
		<comments>http://www.dbnewz.com/2011/12/22/comprendre-son-fichier-de-configuration-2eme-partie/#comments</comments>
		<pubDate>Thu, 22 Dec 2011 15:33:54 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=1005</guid>
		<description><![CDATA[Pour notre deuxième volet sur les points les plus importants à regarder lors de la configuration d&#8217;un serveur MySQL, nous allons nous occuper du cache de requêtes, de la réplication et de la journalisation. En route !
Cache de requêtes
Le cache de requêtes est une excellente idée des années 90 pour accélérer les performances des SELECT. [...]]]></description>
			<content:encoded><![CDATA[<p>Pour notre deuxième volet sur les points les plus importants à regarder lors de la configuration d&#8217;un serveur MySQL, nous allons nous occuper du cache de requêtes, de la réplication et de la journalisation. En route !<span id="more-1005"></span></p>
<h3>Cache de requêtes</h3>
<p>Le cache de requêtes est une excellente idée des années 90 pour accélérer les performances des SELECT. Malheureusement ce cache n&#8217;est absolument pas scalable en terme de connexions simultanées, ce qui signifie que dans 99,9% des cas, les performances seront meilleures quand le cache est désactivée.</p>
<p>Par conséquent, le tuning du cache de requêtes est très simple, il vous suffit d&#8217;indiquer dans votre fichier de configuration :<br />
<code>query_cache = 0</code></p>
<p>Pour les plus curieux, sachez que la désactivation du cache de requêtes n&#8217;empêche pas le serveur d&#8217;utiliser la mutex d&#8217;accès au cache de requêtes : même désactivé, le cache de requêtes peut diminuer vos performances !! Dans Percona Server et MySQL 5.5, <code>query_cache = 0</code> désactive également cette mutex, mais pour les versions antérieures, il vous faudra recompiler le serveur sans support du cache de requêtes pour vous débarasser de cette mutex.</p>
<h3>Réplication</h3>
<p>La réplication est tellement utile qu&#8217;il est difficile de s&#8217;en passer : scale-out, haute disponibilité, aide aux backups, slaves dédiés pour les statistiques&#8230; la liste des applications est longue !</p>
<p>Sur toutes les instances impliquées dans un schéma de réplication, il faut absolument que chaque instance ait un <code>server-id</code> unique. Si vous utilisez un système de déploiement de configuration, pensez à faire en sorte que le système de déploiement génère lui-même un id qui soit garanti comme étant unique.</p>
<p>Sur le master, il faut également activer le paramètre <code>log_bin</code> pour mettre en route les logs binaires nécessaires à la réplication (et qui sont aussi utiles pour le Point In Time Recovery), par exemple :<br />
<code>log_bin = /data/mysql-bin</code></p>
<p>Le serveur s&#8217;occupe tout seul de numéroter les fichiers et d&#8217;en faire la rotation. A ce propos, il peut être pratique de forcer la rotation quand le fichier atteint une taille plus faible que celle par défaut (1 Go) :<br />
<code>max_binlog_size = 100000000</code><br />
et de purger automatiquement les fichiers au bout d&#8217;un certain temps :<br />
<code>expire_logs_days        = 7</code></p>
<p>Est également recommandé l&#8217;option <code>sync_binlog = 1</code> qui assure le flush du log binaire à chaque commit (meilleure assurance contre les pertes de données en cas de crash).</p>
<p>Sur les slaves, il n&#8217;est en général pas nécessaire d&#8217;activer les logs binaires, sauf si le slave est lui-même un master. Dans ce cas, il faut penser à ajouter le paramètre <code>log_slave_updates</code>, qui indique au serveur d&#8217;écrire dans les logs binaires les écritures reçues par la réplication.</p>
<p>La seule autre option vraiment intéressante est <code>read_only = 1</code>, qui empêche tous les utilisateurs n&#8217;ayant pas le droit <code>SUPER</code> d&#8217;écrire sur le serveur. Cette option peut éviter des gros problèmes de désynchronisations si accidentellement une application tente d&#8217;écrire directement sur un slave au lieu d&#8217;écrire sur le master.</p>
<p>Ne pas oublier également de configurer un compte pour la réplication avec les droits <code>REPLICATION SLAVE</code> et <code>REPLICATION CLIENT</code>.</p>
<h3>Journalisation</h3>
<p>Avoir des traces de ce qui se passe dans le serveur est évidemment une très bonne idée, aussi bien pour résoudre les problèmes que pour prendre des mesures préventives. Il existe avec MySQL 3 types de logs : general log pour l&#8217;ensemble des connexions et requêtes, slow query log pour les requêtes lentes et error log pour les erreurs et warnings.</p>
<p>Il est préférable d&#8217;éviter d&#8217;activer le general log en production, hormis pour des périodes courtes pour investiguer sur un problème précis, car les performances s&#8217;en ressentent lourdement (et que vous risquez de vous retrouver très vite à court d&#8217;espace disque). Cela se traduit par :<br />
<code>general_log = OFF</code></p>
<p>Le slow query log est particulièrement utile pour repérer les requêtes les plus coûteuses de votre application. Toutes les requêtes au-delà d&#8217;un seuil que vous définissez sont écrites et peuvent servir à générer un rapport des requêtes les plus lentes. Une configuration typique est la suivante :<br />
<code>slow_query_log = 1<br />
slow_query_log_file = /var/log/mysql/slow.log<br />
long_query_time = 0.5</code></p>
<p>Enfin le log d&#8217;erreur devrait être activé, soit par le paramètre <code>log_error</code> soit en écrivant dans syslog, avec le paramètre <code>syslog</code> que vous trouverez généralement dans la section <code>[mysqld_safe]</code>. Sachez que les 2 solutions ont des avantages et des inconvénients, l&#8217;inconvénient étant toujours que vous risquez de perdre la trace d&#8217;erreurs à cause de la rotation de votre fichier de log. Vous trouverez plus d&#8217;informations dans <a href="http://www.dbnewz.com/2010/04/16/le-journal-d-erreurs-de-mysql/">ce billet</a>.</p>
<p>Nous verrons dans le dernier article de cet série les paramètres les plus importants pour InnoDB. D&#8217;ici là, bonnes fêtes de fin d&#8217;année !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/12/22/comprendre-son-fichier-de-configuration-2eme-partie/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Comprendre son fichier de configuration &#8211; 1ère partie</title>
		<link>http://www.dbnewz.com/2011/12/16/comprendre-son-fichier-de-configuration-1ere-partie/</link>
		<comments>http://www.dbnewz.com/2011/12/16/comprendre-son-fichier-de-configuration-1ere-partie/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 15:26:31 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=993</guid>
		<description><![CDATA[Des outils tels que mysql tuning primer ou mysqltuner proposent de vous aider à configurer correctement votre serveur MySQL. Il est vrai qu&#8217;il est facile de se perdre dans la profusion d&#8217;options disponibles. Pourtant les recommendations de ces outils sont bien souvent complètement absurdes ! Il est bien plus fiable de connaître les grandes lignes [...]]]></description>
			<content:encoded><![CDATA[<p>Des outils tels que mysql tuning primer ou mysqltuner proposent de vous aider à configurer correctement votre serveur MySQL. Il est vrai qu&#8217;il est facile de se perdre dans la profusion d&#8217;options disponibles. Pourtant les recommendations de ces outils sont bien souvent complètement absurdes ! Il est bien plus fiable de connaître les grandes lignes de la configuration du serveur pour obtenir rapidement un paramétrage correct. Je vous propose dans cette série d&#8217;articles de faire le tour des principales variables à regarder.<span id="more-993"></span></p>
<p><code>bind-address</code><br />
Cette variable permet les connexions TCP à l&#8217;adresse IP indiquée. C&#8217;est la 1ère variable à regarder si vous constatez un problème d&#8217;accès et que les droits de l&#8217;utilisateur sont corrects.<br />
En général, on utilise cette variable de 2 manières :<br />
- <code>bind-address = 127.0.0.1</code> pour n&#8217;autoriser que les connexions TCP en local<br />
- en commentant la variable pour n&#8217;avoir aucune restriction</p>
<p>A noter qu&#8217;il n&#8217;est (malheureusement) pas possible d&#8217;indiquer une liste d&#8217;adresses IP pour restreindre les connexions TCP à plusieurs hôtes, dans ce cas, il faut passer par des règles de firewalls.</p>
<p><code>skip-name-resolve</code><br />
Lorsqu&#8217;un client se connecte au serveur, celui-ci fait 2 requêtes DNS. Le DNS pouvant être lent, c&#8217;est souvent une bonne idée d&#8217;utiliser cette variable pour désactiver complètement les requêtes DNS. Seule conséquence contraignante : le champ host des utilisateurs ne peut plus être un nom de domaine, mais uniquement une adresse IP (vous pouvez continuer à utiliser les wildcards, par exemple 192.168.%).</p>
<p><code>max_connections</code><br />
C&#8217;est la variable qui contrôle le nombre de connexions simultanées au serveur. Si vous voyez régulièrement des erreurs &#8216;<code>Too many connections</code>&#8216; lorsque des clients essaient de se connecter, il s&#8217;agit de la variable à regarder, la valeur par défaut (100 ou 150 selon les versions) pouvant être trop faible selon le type d&#8217;applications.</p>
<p>A noter qu&#8217;il n&#8217;est pas conseillé de mettre systématiquement cette variable à un nombre très élevé, puisque plus le nombre de connexions simultanées est élevé, plus le serveur risque d&#8217;être sujet à des problèmes de contentions internes et risque d&#8217;être peu performant. Parfois un trop grand nombre de connexions simultanées cache un problème de conception dans l&#8217;application.</p>
<p>A noter également que même quand le nombre de connexions ouvertes a atteint max_connections, il est toujours possible de se connecter au serveur avec un utilisateur qui dispose du droit <code>SUPER</code>. Le but est de pouvoir faire des <code>SHOW PROCESSLIST</code> pour comprendre ce qu&#8217;il se passe (et éventuellement quelques <code>KILL</code> bien sentis&#8230;).</p>
<p><code>datadir</code><br />
Il s&#8217;agit de l&#8217;emplacement du répertoire de données. Vous n&#8217;avez pas besoin de le changer si votre volumétrie de données reste dans la gamme des centaines de Mo ou moins, mais dès que vous prévoyez d&#8217;atteindre des tailles conséquentes, il est plus que conseillé d&#8217;indiquer un répertoire se trouvant dans une partition où l&#8217;espace disque est suffisant.</p>
<p><code>old_passwords</code><br />
Avant la version 4.1, MySQL utilisait une méthode de cryptage des mots de passe très faible, toujours disponible sur les versions actuelles quand on active ce fameux paramètre (<code>old_passwords = 1</code>).</p>
<p>En général, on trouve ce fameux paramètre sur de vielles applications &laquo;&nbsp;parce qu&#8217;on ne va pas s&#8217;amuser à changer tous les mauvais de passe même si on sait que ce serait mieux&nbsp;&raquo;.</p>
<p>En réalité, même quand on désactive ce paramètre, un client est toujours capable de se connecter lorsque son mot de passe est crypté avec l&#8217;ancienne méthode. Il n&#8217;y a donc que des avantages à désactiver <code>old_passwords</code>, sauf si bien sûr vous utilisez encore des librairies ne connaissant que l&#8217;ancien cryptage (ce qui devrait être hautement improbable, le changement dans MySQL datant de 2004 !).</p>
<p>Quelques autres variables intéressantes<br />
Selon les cas, il peut aussi être pertinent de regarder du côté des variables suivantes : <code>thread_cache_size</code>, <code>tmp_table_size</code>, <code>max_heap_table_size</code>, <code>table_definition_cache</code>, <code>table_open_cache</code>.</p>
<p>Je signale également un cas particulier : <code>sort_buffer_size</code>. Vous avez tout intérêt à la laisser à sa valeur par défaut et donc à la commenter si vous la trouvez dans un fichier de configuration. Pour la petite histoire, j&#8217;ai mis fin aux crashes aléatoires d&#8217;un serveur en commentant simplement cette petite variable. Le scénario était le suivant avant cette modification : sort_buffer_size = 512M + plusieurs requêtes simultanées avec des gros tris =&gt; la RAM vient à manquer =&gt; OOM killer se met en route et tue mysqld&#8230;</p>
<p>Dans le prochain épisode, nous verrons les variables les plus importantes concernant le cache de requêtes, la réplication et les journaux.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/12/16/comprendre-son-fichier-de-configuration-1ere-partie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Meetup Viadeo/LeMug.fr le 16 novembre</title>
		<link>http://www.dbnewz.com/2011/11/03/meetup-viadeolemug-fr-le-16-novembre/</link>
		<comments>http://www.dbnewz.com/2011/11/03/meetup-viadeolemug-fr-le-16-novembre/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 15:59:42 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[lemug.fr]]></category>
		<category><![CDATA[meetings]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=989</guid>
		<description><![CDATA[Notre ami Olivier organise le mercredi 16 novembre 2011 une rencontre autour de MySQL dans les locaux de Viadeo à Paris. Si vous êtes disponible ce jour-là, n&#8217;hésitez pas à vous inscrire et à venir nous dire bonjour !
Le programme de la rencontre est déjà disponible, n&#8217;oubliez pas de vous inscrire si vous comptez venir, [...]]]></description>
			<content:encoded><![CDATA[<p>Notre ami <a href="http://dasini.net/blog">Olivier</a> organise le mercredi 16 novembre 2011 une rencontre autour de MySQL dans les locaux de Viadeo à Paris. Si vous êtes disponible ce jour-là, n&#8217;hésitez pas à vous inscrire et à venir nous dire bonjour !<span id="more-989"></span></p>
<p>Le programme de la rencontre est déjà <a href="http://www.viadeo.com/fr/event/006201xckh927216/meetup-mysql-viadeo-lemug.fr">disponible</a>, n&#8217;oubliez pas de vous inscrire si vous comptez venir, le nombre de places étant limité.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/11/03/meetup-viadeolemug-fr-le-16-novembre/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Discount code pour Percona Live London</title>
		<link>http://www.dbnewz.com/2011/10/07/discount-code-pour-percona-live-london/</link>
		<comments>http://www.dbnewz.com/2011/10/07/discount-code-pour-percona-live-london/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 13:01:25 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[actu]]></category>
		<category><![CDATA[percona]]></category>
		<category><![CDATA[Percona Live]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=983</guid>
		<description><![CDATA[Percona Live est une série de conférences 100% techniques autour de MySQL, organisées par Percona. La prochaine conférence aura lieu à Londres les 24 et 25 octobre et pour tous ceux qui souhaiteraient y assister, dbnewz vous propose un tarif réduit !
Voici le code magique qui vous donnera droit à une réduction de 40£ : [...]]]></description>
			<content:encoded><![CDATA[<p>Percona Live est une série de conférences 100% techniques autour de MySQL, organisées par Percona. La prochaine conférence aura lieu à Londres les 24 et 25 octobre et pour tous ceux qui souhaiteraient y assister, dbnewz vous propose un tarif réduit !<span id="more-983"></span></p>
<p>Voici le code magique qui vous donnera droit à une réduction de 40£ : <strong>come-c-talk</strong>, que vous pourrez saisir en vous <a href="http://www.eventbrite.com/event/1909670877">enregistrant</a>.</p>
<p>Pour plus d&#8217;informations sur Percona Live, vous pouvez aller voir le <a href="http://www.percona.com/live/london-2011">site</a>, et surtout le <a href="http://www.percona.com/live/london-2011/schedule-conference">programme</a>.<br />
Enfin, vous pouvez vous inscrire au groupe <a href="http://www.linkedin.com/groups/Percona-Live-London-2011-For-4094253">LinkedIn</a> créé pour l&#8217;occasion par notre ami <a href="http://www.mysqlplus.fr">Cédric</a>.</p>
<p>See you there!</p>
<p>PS : promis, dans le prochain post, vous trouverez plus de technique et moins de liens !!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/10/07/discount-code-pour-percona-live-london/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL et ses messages d&#8217;erreur</title>
		<link>http://www.dbnewz.com/2011/09/13/mysql-et-ses-messages-derreur/</link>
		<comments>http://www.dbnewz.com/2011/09/13/mysql-et-ses-messages-derreur/#comments</comments>
		<pubDate>Tue, 13 Sep 2011 14:16:04 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[erreur]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=972</guid>
		<description><![CDATA[Je suis en généralement plutôt content de MySQL : c&#8217;est simple et stable, ça fonctionne bien. Mais il reste encore du travail pour que les messages d&#8217;erreur soient explicites. Petit résumé d&#8217;une frayeur causée par un message d&#8217;erreur approximatif.

Une de mes tables a une tendance marquée à la fragmentation, ce qui a pour conséquence de [...]]]></description>
			<content:encoded><![CDATA[<p>Je suis en généralement plutôt content de MySQL : c&#8217;est simple et stable, ça fonctionne bien. Mais il reste encore du travail pour que les messages d&#8217;erreur soient explicites. Petit résumé d&#8217;une frayeur causée par un message d&#8217;erreur approximatif.<br />
<span id="more-972"></span></p>
<p>Une de mes tables a une tendance marquée à la fragmentation, ce qui a pour conséquence de faire gonfler artificiellement sa taille et de faire baisser les performances de certaines requêtes. Je dois donc de temps à autre la défragmenter. Pas de problème : je commence par regarder la taille sur le disque du fichier .ibd (il s&#8217;agit d&#8217;une table InnoDB sur un serveur pour lequel innodb_file_per_table est activé). Quand la table est défragmentée, elle fait environ 8 Go :<br />
<code><br />
# cd /data/mysql/main_revshare<br />
# du -sh bris_statistic_video.ibd<br />
11G	bris_statistic_video.ibd<br />
</code><br />
Pas de doute, une séance de défragmentation s&#8217;impose. Je me connecte donc au serveur MySQL pour exécuter un OPTIMIZE TABLE :<br />
<code><br />
mysql&gt; OPTIMIZE TABLE bris_statistic_video\G<br />
******** 1. row ********<br />
   Table: dailymotion.bris_statistic_video<br />
      Op: optimize<br />
Msg_type: Error<br />
Msg_text: Table 'dailymotion.bris_statistic_video' doesn't exist<br />
******** 2. row ********<br />
   Table: dailymotion.bris_statistic_video<br />
      Op: optimize<br />
Msg_type: error<br />
Msg_text: Corrupt<br />
</code><br />
Et là, c&#8217;est le drame ! Quoi, ma table est corrompue ? Impossible, l&#8217;application fonctionne toujours et la table est utilisée partout !</p>
<p>Comment sortir de ce problème : redémarrer le serveur en espérant qu&#8217;InnoDB répare la table, récupérer un backup ? En fait non, il faut simplement rester calme et bien regarder le message : si à la 2ème ligne, MySQL nous annonce que la table est corrompue, à la 1ère MySQL nous annonce que la table n&#8217;existe pas &#8230; alors que nous avons vu que le fichier .ibd existe bien. Louche cette histoire !</p>
<p>Un tour dans le journal d&#8217;erreurs confirme bien qu&#8217;il n&#8217;existe aucune erreur : il doit donc y avoir une erreur avec la commande OPTIMIZE. Réfléchissons, réfléchissons &#8230; ça y est : j&#8217;ai exécuté la commande OPTIMIZE dans la base main alors que ma table se trouve dans la base main_revshare. Il suffit donc de se positionner dans main_revshare et de relancer l&#8217;OPTIMIZE, avec succès cette fois.</p>
<p>Moralité : Mieux vaut se méfier des messages d&#8217;erreurs qui ne vous sont pas familiers et bien réfléchir à la cause de l&#8217;erreur avant de se lancer dans la correction d&#8217;un problème qui n&#8217;existe peut-être pas !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/09/13/mysql-et-ses-messages-derreur/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Méthodes de suppression des index inutiles</title>
		<link>http://www.dbnewz.com/2011/09/05/methodes-de-suppression-des-index-inutiles/</link>
		<comments>http://www.dbnewz.com/2011/09/05/methodes-de-suppression-des-index-inutiles/#comments</comments>
		<pubDate>Mon, 05 Sep 2011 15:59:32 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[maatkit]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=916</guid>
		<description><![CDATA[Les vacances étant terminées, nous allons boucler notre tour de vue des index inutiles en voyant quels outils vont nous aider à découvrir les index qui peuvent être supprimés. Le dernier article présentait en effet des indications qui fonctionnent généralement bien mais qui ont l&#8217;inconvénient de demander beaucoup de travail manuel et de laisser de [...]]]></description>
			<content:encoded><![CDATA[<p>Les vacances étant terminées, nous allons boucler notre tour de vue des index inutiles en voyant quels outils vont nous aider à découvrir les index qui peuvent être supprimés. Le dernier article présentait en effet des indications qui fonctionnent généralement bien mais qui ont l&#8217;inconvénient de demander beaucoup de travail manuel et de laisser de côté tout un pan d&#8217;index qui peuvent être inutiles : ceux qui ne sont pas en doublon ni redondants, qui n&#8217;ont pas une cardinalité faible mais qui ne sont tout simplement pas utilisés par l&#8217;application.<br />
<span id="more-916"></span></p>
<h3>Idée générale</h3>
<p>Si vous avez bien lu l&#8217;article précédent, vous avez probablement remarqué que la principale difficulté est qu&#8217;il n&#8217;existe quasiment jamais de règle absolue permettant de savoir à coup sûr qu&#8217;un index est inutile (exception notable : les index en doublon repérés par mk-duplicate-key-checker et qui peuvent être supprimés dans 99% des cas sans problème). Finalement, la seule méthode qui semble marcher est la suivante :<br />
- Récupérer les requêtes exécutées sur le serveur<br />
- Regarder le plan d&#8217;exécution de ces requêtes pour voir les index utilisés<br />
- Comparer avec le schéma des tables pour en déduire les index non utilisés<br />
- Supprimer les index repérés<br />
La difficulté se situe surtout dans la 2ème étape (construire la liste des index utilisés à partir du plan d&#8217;exécution). Heureusement il existe au moins 2 manières, en fonction de votre version de MySQL, pour réussir cette étape.</p>
<h3>Index_statistics</h3>
<p>Les heureux utilisateurs de MariaDB ou Percona Server ont la chance d&#8217;avoir userstats v2, un patch exceptionnel développé à l&#8217;origine par Google. Ce patch ajoute un nombre incalculable de statistiques sur les utilisateurs, les tables mais aussi, ce qui nous intéresse ici, les index. Pour activer la fonctionnalité, il suffit de changer une variable :<br />
<code>mysql&gt; SET GLOBAL userstat_running = 1;</code></p>
<p>A partir de ce moment, la table <code>INDEX_STATISTICS</code> de la base <code>INFORMATION_SCHEMA</code> va se remplir. Les colonnes sont très simples à comprendre : <code>TABLE_SCHEMA</code>, <code>TABLE_NAME</code> et <code>INDEX_NAME</code> localisent l&#8217;index et <code>ROWS_READ</code> donne le nombre de lignes lues dans l&#8217;index. Evidemment, pour obtenir des statistiques significatives et fiables, il faut attendre suffisamment longtemps avant de consulter le contenu de la table. Une bonne question est de savoir ce que signifie &laquo;&nbsp;suffisamment longtemps&nbsp;&raquo; : disons qu&#8217;il faut que chaque requête potentielle de l&#8217;application ait pu être exécutée, en pensant bien aux requêtes rares telles que les requêtes faites dans des cronjobs par exemple.</p>
<p>Un petit exemple sur des données réelles ?<br />
<code><br />
mysql&gt; SELECT * FROM INFORMATION_SCHEMA.INDEX_STATISTICS WHERE TABLE_NAME='main';<br />
+--------------+------------+------------+-----------+<br />
| TABLE_SCHEMA | TABLE_NAME | INDEX_NAME               | ROWS_READ  |<br />
+--------------+------------+------------+-----------+<br />
| biz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  | main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      | user_id&nbsp;&nbsp;&nbsp;        |  231751890 |<br />
| biz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  | main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      | created&nbsp;&nbsp;&nbsp;                  |    4456658&nbsp;&nbsp; |<br />
| biz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| PRIMARY&nbsp;&nbsp;&nbsp;&nbsp;| 1748023896&nbsp;|<br />
| biz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      | modified_idx&nbsp;&nbsp;&nbsp; |  266636148 |<br />
| biz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  | main&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      | mobile_idx&nbsp;&nbsp;&nbsp; |        550 |<br />
+--------------+------------+---------------+------------+<br />
</code><br />
et voyons en parallèle le schéma de la table :<br />
<code><br />
mysql&gt; SHOW CREATE TABLE main\G<br />
********* 1. row *********<br />
Table: main<br />
Create Table: CREATE TABLE main (<br />
&nbsp;&nbsp;...<br />
&nbsp;&nbsp;PRIMARY KEY (main_id),<br />
&nbsp;&nbsp;KEY user_id (user_id),<br />
&nbsp;&nbsp;KEY created (created),<br />
&nbsp;&nbsp;KEY language_idx (language),<br />
&nbsp;&nbsp;KEY mobile_idx (mobile,deleted,web,published),<br />
&nbsp;&nbsp;KEY country_mobile_idx (country,mobile),<br />
&nbsp;&nbsp;KEY modified_idx (modified)<br />
) ENGINE=InnoDB;<br />
</code><br />
Qu&#8217;en déduit-on ? En premier lieu, que les index language_idx et country_mobile_idx ne sont jamais utilisés, ils ne servent donc à rien et peuvent être supprimés. Ensuite, en comparant les chiffres, on voit que le serveur a lu 1,7 milliards de lignes dans la clé primaire, 550 dans l&#8217;index mobile_idx : aucun doute mobile_idx ne sert à rien lui non plus !</p>
<p>Et voilà, de manière très simple, nous avons trouvé des index qui peuvent être supprimés.<br />
On peut même automatiser la découverte des index non utilisée <a href="http://www.mysqlperformanceblog.com/2008/09/12/unused-indexes-by-single-query/">avec une petite requête qui va bien</a>.</p>
<p>Quelles sont les limitations de cette méthode ? Très simple, autant les index qui ne sont jamais utilisés sont visibles immédiatement, autant on peut se poser la question de la pertinence de certains index. Et malheureusement, les chiffres donnés par la table INDEX_STATISTICS ne sont pas suffisants. Exemple : l&#8217;index created affiche un nombre de lignes lues environ 500 fois plus faibles que la clé primaire : cela signifie-t-il qu&#8217;il n&#8217;est pas très utile ou qu&#8217;au contraire il est très utile mais seulement sur certaines requêtes ?</p>
<h3>mk-index-usage</h3>
<p>Quand on ne dispose de userstats, la seule solution consiste à collecter toutes les requêtes pendant une période suffisamment longue, par exemple en mettant <code>long_query_time</code> à 0 pour que toutes les requêtes aboutissent dans le slow query log, et à se tourner vers Maatkit en espérant y trouver notre bonheur. Ca tombe bien, <code>mk-index-usage</code> est justement un outil lisant un fichier de log et délivrant de nombreuses informations intéressantes (en déterminant en particulier le plan d&#8217;exécution des requêtes).</p>
<p>Par défaut, <code>mk-index-usage</code> prend en entrée un fichier de log au format slow query log et affiche une liste de requêtes SQL pour supprimer les index inutiles :<br />
<code><br />
$ mk-index-usage slow.log<br />
slow.log:   5% 08:28 remain<br />
slow.log:  11% 07:20 remain<br />
slow.log:  14% 08:36 remain<br />
...<br />
slow.log:  93% 00:37 remain<br />
slow.log:  97% 00:12 remain<br />
</code><br />
ALTER TABLE `main`.`adfit` DROP KEY `status`; &#8212; type:non-unique<br />
&#8230;<br />
ALTER TABLE `main`.`biz` DROP KEY `created`, DROP KEY `modified_idx`; &#8212; type:non-unique<br />
</code><br />
Surprise ! Le script a bien identifié modified_idx comme inutile, mais pas mobile_idx. Et il a décidé que created pouvait être supprimé alors que nous avons vu que ce n'était pas forcément évident.</p>
<p>Comme d'habitude avec Maatkit, si vous êtes curieux et que vous lisez la documentation, vous verrez qu'il existe une multitude d'options permettant d'obtenir bien plus d'informations sur l'usage des index sur votre plate-forme. De plus, cette méthode est compatible avec toutes les versions de MySQL</p>
<h3>Conclusion</h3>
<p>Les deux méthodes présentées ici peuvent vous faire gagner beaucoup de temps en vous aidant à identifier les index candidats à la suppression. Bien entendu, comme souvent, il ne serait pas très prudent de faire confiance aveuglément à un script pour gérer votre plate-forme : votre travail consistera à vérifier s'il faut effectivement enlever les index qui sont susceptibles d'être supprimés. Normalement, ce type d'index ne devrait représenter qu'une petite portion des index de votre application, le gain de temps sera donc appréciable.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/09/05/methodes-de-suppression-des-index-inutiles/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Index candidats à la suppression</title>
		<link>http://www.dbnewz.com/2011/07/06/index-candidats-a-la-suppression/</link>
		<comments>http://www.dbnewz.com/2011/07/06/index-candidats-a-la-suppression/#comments</comments>
		<pubDate>Wed, 06 Jul 2011 13:10:14 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[maatkit]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=895</guid>
		<description><![CDATA[Après avoir constaté dans les articles précédents que les index inutiles causent des baisses de performances non négligeables, nous allons voir dans cet article qu&#8217;il n&#8217;est pas aussi simple qu&#8217;il y paraît de déterminer si un index est utile ou non, même si dans certains cas la réponse semble évidente.

A première vue, trois catégories d&#8217;index [...]]]></description>
			<content:encoded><![CDATA[<p>Après avoir constaté dans les articles précédents que les index inutiles causent des baisses de performances non négligeables, nous allons voir dans cet article qu&#8217;il n&#8217;est pas aussi simple qu&#8217;il y paraît de déterminer si un index est utile ou non, même si dans certains cas la réponse semble évidente.<br />
<span id="more-895"></span><br />
A première vue, trois catégories d&#8217;index sont bien placés pour être qualifiés d&#8217;inutiles : les index en doublon, les index redondants et les index à faible cardinalité. Regardons chaque catégorie en détail.</p>
<h3>Index en doublon</h3>
<p>Les index en doublon sont simplement ceux qui sont définis plusieurs fois. Un exemple simple pour commencer :<br />
<code>CREATE TABLE t (<br />
&nbsp;&nbsp;id int(11) DEFAULT NULL,<br />
&nbsp;&nbsp;KEY a (id),<br />
&nbsp;&nbsp;KEY b (id)<br />
);</code></p>
<p>Notons que MySQL n&#8217;empêche en aucun cas ce genre de définition erronée.</p>
<p>Un autre exemple, un peu plus compliqué :<br />
<code>CREATE TABLE u (<br />
&nbsp;&nbsp;id int(11) NOT NULL DEFAULT '0',<br />
&nbsp;&nbsp;UNIQUE KEY (id),<br />
&nbsp;&nbsp;KEY b (id)<br />
);</code></p>
<p>La logique était : je crée une contrainte d&#8217;unicité sur la colonne id, puis un index pour accélérer les requêtes. Sauf qu&#8217;avec MySQL, toutes les contraintes sont vérifiées avec des index, ce qui signifie que la création de la contrainte d&#8217;unicité crée implicitement un index (idem pour <code>PRIMARY KEY</code> et <code>FOREIGN KEY</code>).</p>
<p>Un dernier exemple :<br />
<code>CREATE TABLE v (<br />
&nbsp;&nbsp;id int(11) NOT NULL DEFAULT '0',<br />
&nbsp;&nbsp;col varchar(10) DEFAULT NULL,<br />
&nbsp;&nbsp;PRIMARY KEY (id),<br />
&nbsp;&nbsp;KEY a (col),<br />
&nbsp;&nbsp;FULLTEXT KEY b (col)<br />
) ENGINE=MyISAM;</code></p>
<p>Ici, nous avons bien deux index sur col, mais ce ne sont pas des index de même type, ils ne sont donc pas en doublon. Attention aux excès de zèle !</p>
<p>Les index en doublon n&#8217;apportent rien, il est donc toujours utile de pouvoir les repérer pour ensuite les supprimer. Un bon outil pour ce travail est <code>mk-duplicate-key-checker</code> de Maatkit. L&#8217;outil est très simple d&#8217;usage et la documentation complète.</p>
<p>Par exemple, pour vérifier si notre table u (dans la base test) contient des index en doublon, on peut utiliser la ligne suivante :<br />
<code>$ mk-duplicate-key-checker h=localhost,u=root,D=test -t u<br />
# ##################################<br />
# test.u<br />
# ##################################</code></p>
<p><code># b is a duplicate of PRIMARY<br />
# Key definitions:<br />
#   KEY `b` (`id`)<br />
#   PRIMARY KEY (`id`),<br />
# Column types:<br />
#	  `id` int(11) not null default '0'<br />
# To remove this duplicate index, execute:<br />
ALTER TABLE `test`.`u` DROP INDEX `b`;</code></p>
<p><code># ##################################<br />
# Summary of indexes<br />
# ##################################</code></p>
<p><code># Size Duplicate Indexes   0<br />
# Total Duplicate Indexes  1<br />
# Total Indexes            2</code></p>
<p>L&#8217;outil suggère avec raison que l&#8217;index b est en doublon de la clé primaire et propose sous la forme d&#8217;un <code>ALTER TABLE</code> une requête pour supprimer cet index.</p>
<p>La même ligne de commande, en version longue :<br />
<code>$ mk-duplicate-key-checker --host localhost --user root --databases test --tables t</code></p>
<p>Il est bien sûr possible d&#8217;examiner en une seule passe toutes les tables de la base test :<br />
<code>$ mk-duplicate-key-checker --host localhost --user root --databases test<br />
# ##################################<br />
# test.t<br />
# ##################################</code></p>
<p><code># a is a duplicate of b<br />
# Key definitions:<br />
#   KEY `a` (`id`),<br />
#   KEY `b` (`id`)<br />
# Column types:<br />
#	  `id` int(11) default null<br />
# To remove this duplicate index, execute:<br />
ALTER TABLE `test`.`t` DROP INDEX `a`;</code></p>
<p><code># ##################################<br />
# test.u<br />
# ##################################</code></p>
<p><code># b is a duplicate of id<br />
# Key definitions:<br />
#   KEY `b` (`id`)<br />
#   UNIQUE KEY `id` (`id`),<br />
# Column types:<br />
#	  `id` int(11) not null default '0'<br />
# To remove this duplicate index, execute:<br />
ALTER TABLE `test`.`u` DROP INDEX `b`;</code></p>
<p><code># ##################################<br />
# Summary of indexes<br />
# ##################################</code></p>
<p><code># Size Duplicate Indexes   0<br />
# Total Duplicate Indexes  2<br />
# Total Indexes            7</code></p>
<p>Parfait, <code>mk-duplicate-key-checker</code> a bien identifié les doublons et ne s&#8217;est pas fait piéger par l&#8217;index FULLTEXT de la table v.</p>
<h3>Index redondants</h3>
<p>Pour les index de type BTREE (c&#8217;est-à-dire tous les index sauf les index HASH de Memory et les index FULLTEXT de MyISAM), l&#8217;optimiseur de requêtes de MySQL est capable d&#8217;utiliser un préfixe gauche d&#8217;un index composite. Tous les index portant sur des préfixes gauche d&#8217;un index composite ne sont donc théoriquement pas utiles.</p>
<p>Pas clair ? Regardez la table suivante :<br />
<code>CREATE TABLE w (<br />
&nbsp;&nbsp;id int(11) NOT NULL DEFAULT '0',<br />
&nbsp;&nbsp;col varchar(10) DEFAULT NULL,<br />
&nbsp;&nbsp;col2 varchar(10) DEFAULT NULL,<br />
&nbsp;&nbsp;KEY id (id),<br />
&nbsp;&nbsp;KEY id_col (id,col)<br />
);</code></p>
<p>Et considérez la requête suivante :<br />
<code>SELECT * FROM w WHERE id = 1</code></p>
<p><code>EXPLAIN</code> nous indique que l&#8217;index sur id est utilisé :<br />
<code>mysql&gt; EXPLAIN SELECT * FROM w WHERE id = 1\G<br />
********* 1. row ********<br />
&nbsp;&nbsp;&nbsp;id: 1<br />
&nbsp;&nbsp;&nbsp;select_type: SIMPLE<br />
&nbsp;&nbsp;&nbsp;table: w<br />
&nbsp;&nbsp;&nbsp;type: ref<br />
&nbsp;&nbsp;&nbsp;possible_keys: id,id_col<br />
&nbsp;&nbsp;&nbsp;key: id<br />
&nbsp;&nbsp;&nbsp;key_len: 4<br />
&nbsp;&nbsp;&nbsp;ref: const<br />
&nbsp;&nbsp;&nbsp;rows: 1<br />
&nbsp;&nbsp;&nbsp;Extra:<br />
</code><br />
Mais si on demande à l&#8217;optimiseur de ne pas considérer l&#8217;index sur id, on voit que la requête va s&#8217;exécuter de manière aussi efficace en utilisant l&#8217;index sur (id,col) :</p>
<p><code>mysql&gt; EXPLAIN SELECT * FROM w IGNORE INDEX(id) WHERE id = 1\G<br />
******** 1. row ********<br />
&nbsp;&nbsp;&nbsp;id: 1<br />
&nbsp;&nbsp;&nbsp;select_type: SIMPLE<br />
&nbsp;&nbsp;&nbsp;table: w<br />
&nbsp;&nbsp;&nbsp;type: ref<br />
&nbsp;&nbsp;&nbsp;possible_keys: id_col<br />
&nbsp;&nbsp;&nbsp;key: id_col<br />
&nbsp;&nbsp;&nbsp;key_len: 4<br />
&nbsp;&nbsp;&nbsp;ref: const<br />
&nbsp;&nbsp;&nbsp;rows: 1<br />
&nbsp;&nbsp;&nbsp;Extra:<br />
</code><br />
On peut donc éliminer sans problème l&#8217;index sur id.</p>
<p>Faut-il donc systématiquement supprimer les index redondants ? Eh bien non, il convient d&#8217;apporter quelques nuances. Disons que la majeure partie du temps, un index redondant est inutile et peut être supprimé, mais qu&#8217;il existe des situations où il faudra conserver tous les index.</p>
<p>Par exemple, ce sera souvent le cas quand les colonnes indexées sont de grande taille (que ceux qui n&#8217;utilisent jamais de <code>VARCHAR(255)</code> lèvent la main <img src='http://www.dbnewz.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Pour avoir un cas concret de ce type de situation, vous pouvez lire <a href="http://www.mysqlperformanceblog.com/2007/08/28/redundant-index-is-not-always-bad/">cet article</a>.</p>
<p>Comment savoir si un index redondant est utile ou non ? La seule solution utilisable est bien souvent de faire des tests.</p>
<p>Que dit <code>mk-duplicate-key-checker</code> sur les index redondants ?</p>
<p><code>$ mk-duplicate-key-checker h=localhost,u=root,D=test -t w<br />
# ##################################<br />
# test.w<br />
# ##################################</code></p>
<p><code># id is a left-prefix of id_col<br />
# Key definitions:<br />
#   KEY `id` (`id`),<br />
#   KEY `id_col` (`id`,`col`)<br />
# Column types:<br />
#	  `id` int(11) not null default '0'<br />
#	  `col` varchar(10) default null<br />
# To remove this duplicate index, execute:<br />
ALTER TABLE `test`.`w` DROP INDEX `id`;</code></p>
<p><code># ##################################<br />
# Summary of indexes<br />
# ##################################</code></p>
<p><code># Size Duplicate Indexes   16<br />
# Total Duplicate Indexes  1<br />
# Total Indexes            2</code></p>
<p>L&#8217;outil considère toujours les index redondants comme candidats à la suppression. A vous de voir s&#8217;il vaux mieux le conserver ou non.</p>
<h3>Index à faible cardinalité</h3>
<p>Arnaud a déjà parlé en détail de la notion de<a href="http://www.dbnewz.com/2008/09/05/cardinalite-selectivite-et-distributivite-dun-index-mysql-quel-impact-sur-le-plan-dexecution/"> cardinalité d&#8217;un index</a>. En résumé, la cardinalité est le nombre de valeurs uniques de l&#8217;index. Plus elle est élevée, plus l&#8217;index sera efficace dans son filtrage.</p>
<p>Si la cardinalité est faible, l&#8217;index perd de son intérêt. D&#8217;ailleurs, l&#8217;optimiseur de requêtes n&#8217;utilisera pas l&#8217;index s&#8217;il estime qu&#8217;il n&#8217;est pas utile. Voici donc de bons candidats à la suppression ! Cependant là encore, il convient de nuancer.</p>
<p>Imaginez un site de e-commerce avec une table contenant les commandes. Cette table va contenir un champ indiquant le statut des commandes, avec par exemple les valeurs &#8216;archivée&#8217; et &#8216;en cours&#8217;. Au bout de quelque temps, il est certain que presque toutes les lignes de la table auront le statut &#8216;archivée&#8217;. Cependant, si vous écrivez une requête qui porte sur les commandes &#8216;en cours&#8217;, un index sur la colonne statut sera sûrement utile, même si sa cardinalité sera ridicule (2 en l&#8217;occurence).</p>
<p>Ce qui joue ici n&#8217;est pas seulement la cardinalité de l&#8217;index, mais aussi la distribution des valeurs et surtout les requêtes que vous faites. Si avec la même table des commandes, votre voisin ne s&#8217;intéresse qu&#8217;aux commandes archivées, l&#8217;index n&#8217;a plus aucun intérêt. C&#8217;est d&#8217;ailleurs une situation que l&#8217;on rencontre fréquemment : les requêtes que l&#8217;on peut faire en mode transactionnel ou en mode décisionnel sont radicalement différentes, exigeant souvent d&#8217;avoir des installations dédiées avec une indexation différente.</p>
<h3>Conclusion</h3>
<p>Cet article a montré que le repérage des index inutiles est loin d&#8217;être évident. Mis à part les index en doublon qu&#8217;on peut repérer et éliminer facilement, il est nécessaire d&#8217;avoir une bonne connaissance des requêtes qui sont exécutées afin de déterminer si un index est inutile. Le prochain article va se concentrer sur des méthodes plus systématiques d&#8217;identification des index inutiles.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/07/06/index-candidats-a-la-suppression/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Le coût des index inutiles &#8211; 2nde partie</title>
		<link>http://www.dbnewz.com/2011/05/27/le-cout-des-index-inutiles-2nde-partie/</link>
		<comments>http://www.dbnewz.com/2011/05/27/le-cout-des-index-inutiles-2nde-partie/#comments</comments>
		<pubDate>Fri, 27 May 2011 15:50:34 +0000</pubDate>
		<dc:creator>stephane</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[pratique]]></category>

		<guid isPermaLink="false">http://www.dbnewz.com/?p=885</guid>
		<description><![CDATA[Dans l&#8217;article précédent, nous nous étions demandés quelle était la dégradation des performances en écriture quand on ajoute des index. On peut élargir la réflexion en se penchant sur les conditions qui améliorent ou diminuent la vitesse d&#8217;écriture dans une table.

Avant de commencer de nouvelles expérimentations, rappelons les conditions du test. La table utilisée a [...]]]></description>
			<content:encoded><![CDATA[<p>Dans l&#8217;article précédent, nous nous étions demandés quelle était la dégradation des performances en écriture quand on ajoute des index. On peut élargir la réflexion en se penchant sur les conditions qui améliorent ou diminuent la vitesse d&#8217;écriture dans une table.<br />
<span id="more-885"></span><br />
Avant de commencer de nouvelles expérimentations, rappelons les conditions du test. La table utilisée a la structure suivante :<br />
<code><br />
CREATE TABLE (<br />
&nbsp;&nbsp;id int(11) NOT NULL AUTO_INCREMENT,<br />
&nbsp;&nbsp;col_a varchar(30) NOT NULL DEFAULT '',<br />
&nbsp;&nbsp;col_b varchar(30) NOT NULL DEFAULT '',<br />
&nbsp;&nbsp;col_c varchar(30) NOT NULL DEFAULT '',<br />
PRIMARY KEY (id)<br />
);<br />
</code><br />
et nous cherchons à insérer un millions de lignes à l&#8217;aide de la commande <code>LOAD DATA INFILE</code>.</p>
<p>Rappelons aussi que pour améliorer les performances en écriture dans l&#8217;absolu, il existe d&#8217;autres pistes que nous n&#8217;explorerons pas dans cet article :</p>
<ul>
<li>si la table utilise InnoDB ou XtraDB, modifier certaines variables de configuration (<code>innodb_log_file_size</code>,<code>innodb_flush_log_at_trx_commit</code>,<br />
<code>innodb_adaptive_flushing</code>, etc)</li>
<li>si la table utilise MyISAM, modifier d&#8217;autres variables de configuration (<code>myisam_sort_buffer_size</code>, <code>bulk_insert_buffer_size</code>)</li>
<li>faire des insertions multithreadées</li>
<li>désactiver les vérifications de clés étrangères ou de clés uniques</li>
<li>&#8230;</li>
</ul>
<p>Pour commencer nos test, examinons le comportement de MyISAM. Le paramètre clé est la taille du cache d&#8217;index (<code>key_buffer_size</code>), nous faisons donc un essai avec un cache d&#8217;index suffisamment grand pour pouvoir contenir tous les index et un essai avec un cache trop petit.</p>
<p>MyISAM, key_buffer_size grand :<br />
Seulement la clé primaire : 4s<br />
Clé primaire + index sur col_a : 6,6s<br />
Clé primaire + index sur col_b : 9,3s<br />
Clé primaire + index sur (col_a,col_b) : 6,8s</p>
<p>MyISAM, key_buffer_size petit :<br />
Seulement la clé primaire : 4s<br />
Clé primaire + index sur col_a : 6,8s<br />
Clé primaire + index sur col_b : 9,3s<br />
Clé primaire + index sur (col_a,col_b) : 6,8s</p>
<p>A comparer avec les résultats obtenus la dernière fois avec InnoDB :<br />
Seulement la clé primaire : 15s<br />
Clé primaire + index sur col_a : 28,5s<br />
Clé primaire + index sur col_a + index sur col_b : 44,5s<br />
Clé primaire + index sur (col_a,col_b) : 36,3s</p>
<p>Très intéressant ! MyISAM se révèle plus performant qu&#8217;InnoDB, et de loin.</p>
<p>Attention, ne faisons pas de généralités sur la vitesse de MyISAM ! Ce test se déroule sur un serveur hors production qui n&#8217;exécute que les <code>LOAD DATA INFILE</code>. Il est certain que l&#8217;écart entre InnoDB et MyISAM ne serait pas aussi important si les insertions étaient multithreadées (verrouillage de toute la table pour MyISAM) et si le serveur recevait également des requêtes en lecture (les écritures seraient peut-être rapides mais les lectures souffriraient sans doute).</p>
<p>De plus, on voit que la taille du cache d&#8217;index n&#8217;influence quasiment pas le temps de chargement, ce qui est plutôt inattendu. Une explication possible serait que les données et index ne soient pas réellement écrits sur le disque, mais restent dans le cache de l&#8217;OS. L&#8217;hypothèse semble raisonnable puisqu&#8217;un coup d&#8217;oeil sur iostat pendant les <code>LOAD DATA INFILE</code> montre que les écritures sur le disque sont nettement plus nombreuses quand la table est en InnoDB.</p>
<p>Faisons donc un autre test avec MyISAM, pour lequel le cache d&#8217;index est petit et la quantité de mémoire disponible pour l&#8217;OS est trop faible pour pouvoir contenir les données et les index :<br />
Seulement la clé primaire : 6,8s<br />
Clé primaire + index sur col_a : 14s<br />
Clé primaire + index sur col_b : 24s<br />
Clé primaire + index sur (col_a,col_b) : 18s</p>
<p>Les performances sont bien moins bonnes qu&#8217;avant, mais restent encore supérieures à celles d&#8217;InnoDB !</p>
<p>Concentrons nous maintenant sur InnoDB et voyons l&#8217;influence du type de clé primaire sur le temps de chargement. Pour simplifier, mettons-nous toujours dans le cas où le buffer_pool est suffisamment grand pour contenir données et index.</p>
<p>Dans un premier temps, optons pour une clé primaire composite (id,col_c) :<br />
Seulement la clé primaire : 17,8s<br />
Clé primaire + index sur col_a : 40s<br />
Clé primaire + index sur col_b : 72s<br />
Clé primaire + index sur (col_a,col_b) : 51,7s</p>
<p>On voit qu&#8217;avoir une clé primaire composite a un impact non négligeable lorsqu&#8217;il n&#8217;existe pas d&#8217;autres index (20% de perte de performance) et que cet impact négatif est encore plus important quand on ajoute des index. Ce résultat était attendu, puisqu&#8217;avec InnoDB, chaque index secondaire contient la clé primaire : plus la clé primaire est volumineuse, plus les index secondaires sont volumineux et longs à mettre à jour.</p>
<p>Dans un second temps, supprimons la clé primaire :<br />
Seulement la clé primaire : 14,6s<br />
Clé primaire + index sur col_a : 29,2s<br />
Clé primaire + index sur col_b : 45,5s<br />
Clé primaire + index sur (col_a,col_b) : 38,1s</p>
<p>Les temps obtenus sont proches du cas initial où id est la clé primaire. Comment expliquer ce résultat ? InnoDB a besoin d&#8217;une clé primaire pour toutes ses tables. Si vous ne la définissez pas vous-même et qu&#8217;il n&#8217;existe pas d&#8217;index unique n&#8217;ayant aucune valeur NULL, InnoDB créé une clé primaire cachée sous la forme d&#8217;un entier. On se retrouve donc avec une structure du type suivant :<br />
<code><br />
CREATE TABLE (<br />
&nbsp;&nbsp;hidden_id int NOT NULL AUTO_INCREMENT,<br />
&nbsp;&nbsp;id int(11) NOT NULL,<br />
&nbsp;&nbsp;col_a varchar(30) NOT NULL DEFAULT '',<br />
&nbsp;&nbsp;col_b varchar(30) NOT NULL DEFAULT '',<br />
&nbsp;&nbsp;col_c varchar(30) NOT NULL DEFAULT '',<br />
&nbsp;&nbsp;PRIMARY KEY (hidden_id)<br />
);<br />
</code><br />
On se retrouve avec la même structure qu&#8217;initialement, mais avec une colonne de plus, donc avec une volumétrie un peu plus importante, ce qui explique les résultats similaires mais légèrement inférieurs.</p>
<p>Que pouvons-nous conclure de tous ces test ? Tout d&#8217;abord, que le moteur de stockage a une grande influence sur les résultats. Si vous avez le choix du moteur, des tests s&#8217;imposent pour déterminer lequel est le meilleur, en ayant soin de prendre en compte la charge réelle de l&#8217;application (connexions concurrentes, lectures et écritures). Ensuite, que pour InnoDB, le choix de la clé primaire est critique : la clé primaire doit être la plus compacte possible, sous peine de performances très dégradées. Enfin, que pour MyISAM comme pour InnoDB, il vaut mieux limiter le nombre d&#8217;index qui ralentissent considérablement toutes les écritures.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dbnewz.com/2011/05/27/le-cout-des-index-inutiles-2nde-partie/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

