
{"id":788,"date":"2009-10-12T16:21:50","date_gmt":"2009-10-12T15:21:50","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=788"},"modified":"2009-10-12T16:21:50","modified_gmt":"2009-10-12T15:21:50","slug":"mysql-query-cache","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2009\/10\/12\/mysql-query-cache\/","title":{"rendered":"MySQL Query cache"},"content":{"rendered":"<p><!-- \t\t@page { margin: 2cm } \t\tP { margin-bottom: 0.21cm } \t\tA:link { so-language: zxx } --><\/p>\n<p style=\"margin-bottom: 0cm;\">En tant que boulimique  de  MySQL, je me prom\u00e8ne souvent sur la toile \u00e0 la recherche d&rsquo;informations, de bonnes et de moins bonnes&#8230;<\/p>\n<p style=\"margin-bottom: 0cm;\">Je suis tomb\u00e9 sur un article traitant du cache de requ\u00eates de MySQL (<a title=\"noidea.ca - Patrick Lafontaine\" href=\"http:\/\/www.noidea.ca\/2009\/10\/10\/mysql-query-cache\/\" target=\"_blank\">MySQL Query Cache<\/a>) sur le blogue de<a title=\"noidea.ca - Patrick Lafontaine\" href=\"http:\/\/www.noidea.ca\/2009\/10\/10\/mysql-query-cache\/\" target=\"_blank\"> Patrick Lafontaine<\/a> (http:\/\/www.noidea.ca\/)<strong><br \/>\n<\/strong><\/p>\n<p style=\"margin-bottom: 0cm;\">Je me permet de faire quelques commentaires ici.<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">En pr\u00e9ambule, quelques informations n\u00e9cessaires sur le cache de requ\u00eates:<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Syst\u00e8me de cache interne \u00e0 MySQL qui ne stocke que les requ\u00eates <strong>SELECT<\/strong> et leur r\u00e9sultat ie pas d&rsquo;INSERT, UPDATE, DELETE&#8230;<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Les requ\u00eates ( SELECT donc)  doivent \u00eatre <strong>strictement identiques<\/strong> ie m\u00eame casse, m\u00eames espaces entre les mots,&#8230;<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Ex 3 requ\u00eates diff\u00e9rentes pour le cache<\/strong> :<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<address style=\"margin-bottom: 0cm;\">SELECT nom, prenom FROM client WHERE client_id=123;<\/address>\n<address style=\"margin-bottom: 0cm;\">\n<\/address>\n<address style=\"margin-bottom: 0cm;\">select nom, prenom FROM client WHERE client_id=123; \/* la casse du select*\/<\/address>\n<address style=\"margin-bottom: 0cm;\">\n<\/address>\n<address style=\"margin-bottom: 0cm;\">SELECT nom, prenom FROM client \u00a0 \u00a0 \u00a0\u00a0          WHERE client_id=123; \/*plusieurs espaces entre client et WHERE*\/<\/address>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Le cache est <strong>toujours \u00e0 jour<\/strong> car en cas de modification d&rsquo;une table, toutes les requ\u00eates en relations avec cette table sont invalid\u00e9es.<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Le cache de requ\u00eates est  en g\u00e9n\u00e9ral utile lorsque:<\/p>\n<ul>\n<li>Les modifications sur les tables \tne sont pas tr\u00e8s fr\u00e9quentes<\/li>\n<li>Beaucoup de requ\u00eates de lectures \tidentiques<\/li>\n<li>Utilisation de tables MyISAM. \tMoins int\u00e9ressant pour InnoDB<\/li>\n<\/ul>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Pour rebondir sur l&rsquo;article de Patrick Lafontaine<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><em>\u00ab\u00a0puisqu\u2019elle est activ\u00e9e par d\u00e9faut.<\/em> \u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\">Le cache de requ\u00eates n&rsquo;est pas activ\u00e9 par d\u00e9faut, car la variable <strong>query_cache_size<\/strong> vaut <strong>0<\/strong>. Si vous voulez vous en servir, il faut lui donner une taille en octet. Mettez le tout dans votre fichier de configuration. Assurez vous \u00e9galement que la variable  <strong>query_cache_type<\/strong> est diff\u00e9rente de <strong>OFF<\/strong><\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><em>\u00ab\u00a0c\u2019est-\u00e0-dire que la ou les applications qui s\u2019en servent n\u2019ont pas besoin d\u2019\u00eatre modifi\u00e9es\u00a0\u00bb<\/em><\/p>\n<p style=\"margin-bottom: 0cm;\">Pour une optimisation optimale, il est parfois n\u00e9cessaire de modifier les requ\u00eates SELECT avec <strong>SQL_CACHE<\/strong> ou <strong>SQL_NO_CACHE<\/strong>. On choisit alors quelles requ\u00eates seront mis en cache.<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\u00ab<em> Si quelqu\u2019un modifie la valeur directement dans MySQL, la cache poss\u00e8dera la vieille valeur jusqu\u2019\u00e0 ce qu\u2019un processus l\u2019invalide<\/em>.\u00a0\u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\">Sur une machine de production, ce type de manipulation reste quand m\u00eame exceptionnel, sinon c&rsquo;est qu&rsquo;il y a des choses \u00e0 revoir dans les process.<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\u00ab\u00a0<em>Puisque les donn\u00e9es ne changent pratiquement jamais, je ne me casserais pas la t\u00eate \u00e0 r\u00e9inventer la roue. MySQL fait d\u00e9j\u00e0 pour vous ce que APC ferait, sans le moindre effort.<\/em> \u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\">Si le contenu ne change JAMAIS, il n&rsquo;a \u00e0 priori rien \u00e0 faire en base ! Il vaut mieux utiliser un menu statique et laisser la base faire son boulot avec du contenu dynamique. Dans le m\u00eame ordre d&rsquo;id\u00e9e, plus le cache est \u00e9loign\u00e9 du disque plus il est performant. En d&rsquo;autres termes, le goulet d&rsquo;\u00e9tranglement est souvent <em>(parfois)<\/em> la base de donn\u00e9es, de plus elle est souvent <em>(parfois)<\/em> plus difficilement scalable que le reste. L&rsquo;utilisation d&rsquo;un cache applicatif est rarement une mauvaise id\u00e9e (il suffit de conna\u00eetre l&rsquo;identit\u00e9 du plus gros consommateur de memcached au monde <a title=\"FaceBook - memcached\" href=\"http:\/\/www.facebook.com\/note.php?note_id=39391378919\" target=\"_blank\">http:\/\/www.facebook.com\/note.php?note_id=39391378919<\/a>)<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\u00ab\u00a0<em><span style=\"font-weight: normal;\">Il est donc plus avantageux de cacher les processus lourds que les l\u00e9gers<\/span><\/em>.\u00a0\u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\">Malheureusement, avec le cache de requ\u00eates ce n&rsquo;est pas aussi simple. Admettons qu&rsquo;une requ\u00eate renvoyant un gros r\u00e9sultat prenne plus de temps qu&rsquo;une renvoyant  un plus petit. Si cette grosse requ\u00eate vire toutes les autres requ\u00eates du cache, l&rsquo;apport du cache pour les autres requ\u00eates est perdu, elle devront \u00eatre misent \u00e0 nouveau dans le cache ca qui implique des recherches inutiles dans le caches et de nouveaux acc\u00e8s disque&#8230;<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\u00ab\u00a0<em>Lorsque la Query Cache de MySQL est activ\u00e9e, le processus de cacher les r\u00e9sultats et de les invalider s\u2019effectue tout seul de mani\u00e8re invisible. Ainsi, d\u2019autres requ\u00eates que vous ne soup\u00e7onnez m\u00eame pas b\u00e9n\u00e9ficient de la cache <\/em>\u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\">L&rsquo;efficacit\u00e9 du cache de requ\u00eates est vraiment li\u00e9 \u00e0 l&rsquo;application. Il d\u00e9pend du type de requ\u00eates SELECT, de leur fr\u00e9quence et de la fr\u00e9quence des \u00e9critures dans les tables. Le gain n&rsquo;est pas \u00e9vident et est loin d&rsquo;\u00eatre imm\u00e9diat, car pour chaque requ\u00eate \u00ab\u00a0cachable\u00a0\u00bb MySQL devra l&rsquo;analyser, devra la hacher, devra  v\u00e9rifier s&rsquo;il elle est dans le cache ou non. Et ceci \u00e0 un co\u00fbt&#8230;<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Vous pouvez calculer le taux d&rsquo;efficacit\u00e9 du cache de requ\u00eates avec la formule suivant:<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><span style=\"color: #800000;\"><strong>Qcache_hits \/ (Qcache_hits + Com_select )<\/strong><\/span><\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">Pour finir, quelques param\u00e8tres et commandes<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><span style=\"text-decoration: underline;\"><strong>Param\u00e8tres principaux<\/strong><\/span>:<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><strong>query_cache_size<\/strong>: Doit \u00eatre diff\u00e9rent de z\u00e9ro pour activer le cache<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>query_cache_type<\/strong>:<\/p>\n<ul>\n<li>\n<p style=\"margin-bottom: 0cm;\"><strong>ON<\/strong>: les requ\u00eates select  \tsont misent en cache<\/p>\n<ul>\n<li>\n<p style=\"margin-bottom: 0cm;\">Sauf (<strong>SQL_NO_CACHE<\/strong>, result set \t\ttrop grand, fonction non d\u00e9terministe..)<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p style=\"margin-bottom: 0cm;\"><strong>DEMAND<\/strong>: SELECT <strong>SQL_CACHE<\/strong>&#8230;<\/p>\n<\/li>\n<li>\n<p style=\"margin-bottom: 0cm;\"><strong>OFF<\/strong><\/p>\n<\/li>\n<\/ul>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><span style=\"text-decoration: underline;\"><strong>Commandes principales<\/strong>:<\/span><\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><strong>FLUSH QUERY CACHE<\/strong><\/p>\n<ul>\n<li>\n<p style=\"margin-bottom: 0cm;\">D\u00e9fragmente le cache de requ\u00eates<\/p>\n<\/li>\n<li>\n<p style=\"margin-bottom: 0cm;\">Ne vide pas le cache<\/p>\n<\/li>\n<\/ul>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><span style=\"text-decoration: underline;\"><strong>Vider le cache de requ\u00eates<\/strong><\/span>:<\/p>\n<ul>\n<li>\n<p style=\"margin-bottom: 0cm;\"><strong>RESET QUERY CACHE<\/strong><\/p>\n<\/li>\n<li>\n<p style=\"margin-bottom: 0cm;\"><strong>FLUSH TABLES<\/strong><\/p>\n<\/li>\n<li>\n<p style=\"margin-bottom: 0cm;\">Red\u00e9marrer le serveur<\/p>\n<\/li>\n<\/ul>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><span style=\"text-decoration: underline;\"><strong>Variables d&rsquo;\u00e9tat<\/strong><\/span>: <strong>SHOW STATUS LIKE &lsquo;Qcache%&rsquo; <\/strong>;<\/p>\n<p style=\"margin-bottom: 0cm;\">\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_free_blocks<\/strong> : nombre de blocs libres<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_free_memory<\/strong> : m\u00e9moire libre<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_hits<\/strong> : nombre de fois qu&rsquo;il a servi<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_inserts<\/strong> : nombre de requ\u00eates ins\u00e9r\u00e9es<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_lowmem_prunes<\/strong> : nombre de requ\u00eates supprim\u00e9es car plus de place<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_not_cached <\/strong>: nombre de requ\u00eates non \u00ab\u00a0cachables\u00a0\u00bb<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_queries_in_cache <\/strong>: nombre de requ\u00eates dans le cache<\/p>\n<p style=\"margin-bottom: 0cm;\"><strong>Qcache_total_blocks<\/strong> : nombre de blocs de m\u00e9moire<\/p>\n<p style=\"margin-bottom: 0cm;\">\n","protected":false},"excerpt":{"rendered":"<p>Le cache est toujours \u00e0 jour car en cas de modification d&rsquo;une table, toutes les requ\u00eates en relations avec cette table sont invalid\u00e9es.<\/p>\n<p>Le cache de requ\u00eates est  en g\u00e9n\u00e9ral utile lorsque:<br \/>\nLes modifications sur les tables ne sont pas tr\u00e8s fr\u00e9quentes<br \/>\nBeaucoup de requ\u00eates de lectures identiques<br \/>\nUtilisation de tables MyISAM. Moins int\u00e9ressant pour InnoDB<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":""},"categories":[8,34],"tags":[153],"class_list":["post-788","post","type-post","status-publish","format-standard","hentry","category-mysql","category-optimisation","tag-query-cache"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-cI","jetpack-related-posts":[{"id":1196,"url":"https:\/\/dasini.net\/blog\/2012\/02\/28\/attention-au-query-cache\/","url_meta":{"origin":788,"position":0},"title":"Attention au query cache","author":"Olivier DASINI","date":"28 f\u00e9vrier 2012","format":false,"excerpt":"Selon le livre \u00abAudit & optimisation, MySQL 5 - \u00e9ditions Eyrolles\u00bb, le cache de requ\u00eates ou query cache est un syst\u00e8me de cache m\u00e9moire interne \u00e0 MySQL, transparent pour l'application, qui ne stocke que les requ\u00eates SELECT et leurs r\u00e9sultats. L'apport de ce cache est particuli\u00e8rement d\u00e9pendant de votre application.\u2026","rel":"","context":"Dans &quot;bench&quot;","block_context":{"text":"bench","link":"https:\/\/dasini.net\/blog\/category\/bench\/"},"img":{"alt_text":"dasini.net - MySQL query cache","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-includes\/images\/query_cache_on_VS_query_cache_off_im_tiny.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":192,"url":"https:\/\/dasini.net\/blog\/2008\/11\/23\/mysql-5-les-vues-part-17\/","url_meta":{"origin":788,"position":1},"title":"MySQL 5 : Les vues &#8212; (part 1\/7)","author":"Olivier DASINI","date":"23 novembre 2008","format":false,"excerpt":"Le langage SQL acronyme de Structured Query Language (Langage Structur\u00e9 de Requ\u00eates), a \u00e9t\u00e9 con\u00e7u pour g\u00e9rer les donn\u00e9es dans un SGBDR. A l'aide des DML (Data Manipulation Language ie les requ\u00eates SELECT, INSERT, UPDATE, DELETE) il est possible de manipuler ces donn\u00e9es qui sont stock\u00e9es dans des tables. SQL\u2026","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1593,"url":"https:\/\/dasini.net\/blog\/2016\/03\/02\/30-mins-avec-mysql-query-rewriter\/","url_meta":{"origin":788,"position":2},"title":"30 mins avec MySQL Query Rewriter","author":"Olivier DASINI","date":"2 mars 2016","format":false,"excerpt":"Parfois des requ\u00eates probl\u00e9matiques tournent sur le serveur, mais il n'est pas possible de r\u00e9gler le probl\u00e8me \u00e0 la source (Requ\u00eates venant d'un ORM par example) MySQL 5.7 fournit une API pre et post parse query rewrite. Les utilisateurs peuvent \u00e9crire leurs propre plugins ce qui permet d'\u00e9liminer le besoin\u2026","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":248,"url":"https:\/\/dasini.net\/blog\/2008\/11\/30\/mysql-5-les-vues-part-37\/","url_meta":{"origin":788,"position":3},"title":"MySQL 5 : Les vues &#8212; (part 3\/7)","author":"Olivier DASINI","date":"30 novembre 2008","format":false,"excerpt":"Restrictions Lors de la cr\u00e9ation d'une vue, certaines contraintes doivent \u00eatre prises en compte : * Il n'est pas possible de cr\u00e9er un index sur une vue * La vue ne peut pas contenir de sous-requ\u00eates dans la clause FROM du SELECT. * Il n'est pas possible d'utiliser de variables\u2026","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":275,"url":"https:\/\/dasini.net\/blog\/2008\/12\/03\/les-nouveautes-de-mysql-51-part-25\/","url_meta":{"origin":788,"position":4},"title":"Les nouveaut\u00e9s de MySQL 5.1 &#8212; (part 2\/5)","author":"Olivier DASINI","date":"3 d\u00e9cembre 2008","format":false,"excerpt":"Pouvoir automatiser ses t\u00e2ches de mani\u00e8re fiable et simple est le r\u00eave de tout administrateur de base de donn\u00e9es. Le programmateur d'\u00e9v\u00e8nements (Event Scheduler) est un planificateur de t\u00e2ches (CRON-like) embarqu\u00e9 dans MySQL 5.1.","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1363,"url":"https:\/\/dasini.net\/blog\/2013\/01\/08\/full-table-scan-vs-full-index-scan-part1-2\/","url_meta":{"origin":788,"position":5},"title":"Full table scan vs Full index scan part1-2","author":"Olivier DASINI","date":"8 janvier 2013","format":false,"excerpt":"MySQL utilise un optimiseur \u00e0 base de co\u00fbts. Le plan d\u2019ex\u00e9cution de la requ\u00eate choisit est celui dont le co\u00fbt est le plus faible. Ce dernier peut \u00eatre visualis\u00e9 gr\u00e2ce \u00e0 la variable Last_query_cost. Son unit\u00e9 est le co\u00fbt (encore lui) des acc\u00e8s al\u00e9atoires en lecture de pages de 4ko.\u2026","rel":"","context":"Dans &quot;optimisation&quot;","block_context":{"text":"optimisation","link":"https:\/\/dasini.net\/blog\/category\/optimisation\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/788","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/comments?post=788"}],"version-history":[{"count":4,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/788\/revisions"}],"predecessor-version":[{"id":792,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/788\/revisions\/792"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=788"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=788"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=788"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}