{"id":5480,"date":"2016-05-24T06:52:56","date_gmt":"2016-05-24T05:52:56","guid":{"rendered":"http:\/\/blog.capdata.fr\/?p=5480"},"modified":"2019-09-13T13:06:31","modified_gmt":"2019-09-13T12:06:31","slug":"requetes-consommatrices-sous-postgresql-episode-1","status":"publish","type":"post","link":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/","title":{"rendered":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1)"},"content":{"rendered":"<a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-twitter nolightbox\" data-provider=\"twitter\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Twitter\" href=\"https:\/\/twitter.com\/intent\/tweet?url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480&#038;text=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"twitter\" title=\"Share on Twitter\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/twitter.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-linkedin nolightbox\" data-provider=\"linkedin\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Linkedin\" href=\"https:\/\/www.linkedin.com\/shareArticle?mini=true&#038;url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480&#038;title=Requ%C3%AAtes%20consommatrices%20sous%20PostgreSQL%20%28%C3%A9pisode%201%29\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"linkedin\" title=\"Share on Linkedin\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/linkedin.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox\" data-provider=\"mail\" rel=\"nofollow\" title=\"Share by email\" href=\"mailto:?subject=Requ%C3%AAtes%20consommatrices%20sous%20PostgreSQL%20%28%C3%A9pisode%201%29&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"mail\" title=\"Share by email\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/mail.png\" \/><\/a><p>Lors de l&#8217;\u00e9criture de l&#8217;agent <a href=\"https:\/\/www.alldb.fr\/\">AllDB<\/a> pour PostgreSQL je me suis pas mal frott\u00e9 avec les solutions de remont\u00e9e de requ\u00eates lentes donc je me suis dit qu&#8217;un petit post sur le sujet ne ferait pas de mal.<\/p>\n<p>En gros il y a 2 fa\u00e7ons de r\u00e9cup\u00e9rer les requ\u00eates &#8216;consommatrices&#8217; sous PostgreSQL, via un log ou via une extension.<\/p>\n<h2>Via le log g\u00e9n\u00e9ral:<\/h2>\n<p>C&#8217;est un peu le m\u00eame fonctionnement que sous MySQL o\u00f9 on va indiquer une dur\u00e9e de requ\u00eate minimale et toutes les requ\u00eates au-dessus de ce seuil seront logg\u00e9es dans le fichier de log de l&#8217;instance PG.<\/p>\n<p>On active ce principe via un param\u00e8tre instance dans postgresl.conf:<\/p>\n<p><em>&#8211;\u00a0log_min_duration_statement = &lt;valeur en ms&gt;<\/em><\/p>\n<p>0 pour tout r\u00e9cup\u00e9rer, -1 par d\u00e9faut pour ne rien r\u00e9cup\u00e9rer, ou tout autre valeur en millisecondes. <\/p>\n<pre name=\"code\" class=\"sql\">(postgres@[local]:5432) [postgres] &gt; \\x\r\nExpanded display is on.\r\n(postgres@[local]:5432) [postgres] &gt; select * from pg_settings where name = 'log_min_duration_statement' ;\r\n-[ RECORD 1 ]----------------------------------------------------------------------\r\nname | log_min_duration_statement\r\nsetting | -1\r\nunit | ms\r\ncategory | Reporting and Logging \/ When to Log\r\nshort_desc | Sets the minimum execution time above which statements will be logged.\r\nextra_desc | Zero prints all queries. -1 turns this feature off.\r\ncontext | superuser\r\nvartype | integer\r\nsource | default\r\nmin_val | -1\r\nmax_val | 2147483647\r\nenumvals | NULL\r\nboot_val | -1\r\nreset_val | -1\r\nsourcefile | NULL\r\nsourceline | NULL<\/pre>\n<p>Par exemple:<\/p>\n<pre name=\"code\" class=\"sql\">(postgres@[local]:5432) [FRCITY] &gt; SET log_min_duration_statement = 30 ;\r\nSET\r\nTime: 0.901 ms\r\n(postgres@[local]:5432) [FRCITY] &gt; select * from towns ;\r\n(...)\r\n(36684 rows)\r\nTime: 56.471 ms<\/pre>\n<p>Et dans le fichier de log (sous $PGDATA\/pg_log) par d\u00e9faut:<\/p>\n<pre name=\"code\" class=\"sql\">(...)\r\n2016-05-18 11:15:52 CEST LOG: duration: 56.297 ms statement: select * from towns ;\r\n(...)<\/pre>\n<p>La diff\u00e9rence de temps vient du fait que dans le log n&#8217;est report\u00e9 que le temps pass\u00e9 dans le postmaster, alors que le temps remont\u00e9 par psql est logiquement le temps serveur + client.<\/p>\n<p>On peut continuer de jouer en modifiant log_line_prefix pour enrichir le contenu de la ligne qui est logg\u00e9e. Par contre il faut modifier dans le fichier postgresql.conf et reloader via pg_ctl reload ou pg_reload_conf():<\/p>\n<pre name=\"code\" class=\"sql\">log_line_prefix = '%t [%p]: [%l-1] db=%d,user=%u,app=%a,client=%h '<\/pre>\n<p>R\u00e9sultat dans le fichier:<\/p>\n<pre name=\"code\" class=\"sql\">16-05-18 11:26:49 CEST [22945]: [3-1] db=FRCITY,user=postgres,app=psql.bin,client=[local] LOG: duration: 62.788 ms statement: select * from towns ;<\/pre>\n<p>C&#8217;est un peu mieux mais quand on vient d&#8217;Oracle ou SQL Server, on reste un peu sur sa faim (pas de nombre de blocs logiques \/ physiques lus, de nombre de lignes, de temps CPU <em>vs<\/em> elapsed, etc&#8230;)<\/p>\n<h2>Via pg_stat_statements:<\/h2>\n<p>Pour trouver une alternative, on devra se tourner vers des extensions. <\/p>\n<p><a href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/pgstatstatements.html\">Pg_stat_statements<\/a>\u00a0(<em>pgss<\/em> pour faire court) est une extension native qui est livr\u00e9e sous la forme d&#8217;une librairie dynamique (<em>pg_stat_statements.so<\/em>). Elle logge en fonction de la version diff\u00e9rents aspects op\u00e9rationnels de l&#8217;ex\u00e9cution des requ\u00eates, comme le nombre de lignes, le nombre d&#8217;appels, le nombre de blocs lus par type, etc&#8230; Ces donn\u00e9es sont cumul\u00e9es sur la dur\u00e9e de vie de l&#8217;instance, et peuvent \u00eatre m\u00eame r\u00e9initialis\u00e9es \u00e0 la main via une fonction pg_stat_statements_reset().<\/p>\n<p>Si elle ne se trouve pas dans <em>$(pg_config &#8211;libdir)\/postgresql<\/em> , il faut installer les contribs. Par exemple en 9.4:<\/p>\n<pre name=\"code\" class=\"sql\">$ yum search postgresql | grep contrib\r\nRepo rhel-7-server-rpms forced skip_if_unavailable=True due to: \/etc\/pki\/entitlement\/1652329269877919303-key.pem\r\npostgresql-contrib.x86_64 : Extension modules distributed with PostgreSQL\r\npostgresql94-contrib.x86_64\u00a0: Contributed source and binaries distributed with\r\n\r\n$ yum install\u00a0postgresql94-contrib.x86_64\r\n(...)<\/pre>\n<div>A partir de l\u00e0, on retrouve la librairie :<\/div>\n<div>\n<pre name=\"code\" class=\"sql\">$ ls -lrat \/opt\/PostgreSQL\/9.4\/lib\/postgresql\/pg_stat_statements.so\r\n-rwxr-xr-x. 1 postgres postgres 26298 Oct 6 2015 \/opt\/PostgreSQL\/9.4\/lib\/postgresql\/pg_stat_statements.so<\/pre>\n<\/div>\n<p>Plusieurs lignes \u00e0 ajouter dans postgresql.conf:<\/p>\n<pre name=\"code\" class=\"sql\">#-----------------------------------------------------------\r\nshared_preload_libraries = '$libdir\/pg_stat_statements.so'\r\n(...)\r\n\r\n#-----------------------------------------------------------\r\ncustom_variable_classes = 'pg_stat_statements'\r\n\r\npg_stat_statements.max = 4000\r\npg_stat_statements.track = all\r\npg_stat_statements.track_utility = on\r\npg_stat_statements.save = on<\/pre>\n<p>Sachant que depuis la 9.2 custom_variable_classes <a href=\"http:\/\/www.postgresql.org\/docs\/9.2\/static\/release-9-2.html\">n&#8217;est plus n\u00e9cessaire<\/a>. Shared_preload_libraries impose un restart de l&#8217;instance. Concernant les param\u00e8tres:<\/p>\n<ul>\n<li><em>.max<\/em> : indique le nombre de lignes contenues dans la table. Lorsque le nombre de lignes atteint cette valeur, chaque ex\u00e9cution provoque une expiration des lignes les plus anciennes, on verra que ce n&#8217;est pas sans cons\u00e9quences un peu plus loin.<\/li>\n<li><em>.track<\/em> : \u00a0[ <span style=\"text-decoration: underline;\">top<\/span> | all | none ], top permet de tracer les requ\u00eates ad-hoc, all permet de tout tracer y compris les requ\u00eates contenues dans le corps des fonctions, et none d\u00e9sactive.<\/li>\n<li><em>.utility<\/em> : [ <span style=\"text-decoration: underline;\">on<\/span> | off ] permet de tracer aussi les commandes hors SELECT \/ UPDATE \/ INSERT \/ DELETE.<\/li>\n<li><em>.save<\/em> : permet d&#8217;assurer la persistance du contenu de la pseudo-table pg_stat_statements apr\u00e8s un restart de l&#8217;instance.<\/li>\n<\/ul>\n<p>Une fois l&#8217;instance red\u00e9marr\u00e9e on peut cr\u00e9er l&#8217;extension (dans la base postgres):<\/p>\n<pre name=\"code\" class=\"sql\">(postgres@[local]:5432) [postgres] &gt; create extension pg_stat_statements ;\r\nCREATE EXTENSION\r\nTime: 273.357 ms<\/pre>\n<p>D\u00e9finition en 9.1 :<\/p>\n<pre name=\"code\" class=\"sql\">(postgres@[local]:5432) [postgres] &gt; \\d pg_stat_statements\r\n          View \"public.pg_stat_statements\"\r\n       Column        |       Type       | Modifiers \r\n---------------------+------------------+-----------\r\n userid              | oid              | \r\n dbid                | oid              | \r\n query               | text             | \r\n calls               | bigint           | \r\n total_time          | double precision | \r\n rows                | bigint           | \r\n shared_blks_hit     | bigint           | \r\n shared_blks_read    | bigint           | \r\n shared_blks_written | bigint           | \r\n local_blks_hit      | bigint           | \r\n local_blks_read     | bigint           | \r\n local_blks_written  | bigint           | \r\n temp_blks_read      | bigint           | \r\n temp_blks_written   | bigint           |<\/pre>\n<p>D\u00e9finition en 9.4:<\/p>\n<pre name=\"code\" class=\"sql\">(postgres@[local]:5432) [postgres] &gt; \\d pg_stat_statements\r\n          View \"public.pg_stat_statements\"\r\n       Column        |       Type       | Modifiers \r\n---------------------+------------------+-----------\r\n userid              | oid              |\r\n dbid                | oid              |\r\n queryid             | bigint           |\r\n query               | text             |\r\n calls               | bigint           |\r\n total_time          | double precision |\r\n rows                | bigint           |\r\n shared_blks_hit     | bigint           |\r\n shared_blks_read    | bigint           |\r\n shared_blks_dirtied | bigint           |\r\n shared_blks_written | bigint           |\r\n local_blks_hit      | bigint           |\r\n local_blks_read     | bigint           |\r\n local_blks_dirtied  | bigint           |\r\n local_blks_written  | bigint           |\r\n temp_blks_read      | bigint           |\r\n temp_blks_written   | bigint           |\r\n blk_read_time       | double precision |\r\n blk_write_time      | double precision |<\/pre>\n<p>Je vous renvoie <a href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/pgstatstatements.html\">\u00e0 la doc en ligne<\/a> pour la description de chaque colonne. A noter la <a href=\"http:\/\/www.postgresql.org\/docs\/9.4\/static\/release-9-2.html\">savoureuse modification<\/a> faite en 9.2 o\u00f9 les unit\u00e9s de total_time passent de seconde \u00e0 milliseconde. Quelques colonnes apparaissent en 9.2 comme les colonnes dirtied et les blk_read_time et blk_write_time, qui seront renseign\u00e9es si <a href=\"http:\/\/www.postgresql.org\/docs\/9.2\/static\/runtime-config-statistics.html#GUC-TRACK-IO-TIMING\">track_io_timing<\/a> est \u00e0 on.<\/p>\n<p>En 9.5, 4 nouvelles colonnes font leur apparition, permettant de mieux \u00e9valuer les variations en temps d&#8217;ex\u00e9cution de chaque requ\u00eate:<\/p>\n<pre name=\"code\" class=\"sql\">\r\nmin_time\tdouble precision\t \tMinimum time spent in the statement, in milliseconds\r\nmax_time\tdouble precision\t \tMaximum time spent in the statement, in milliseconds\r\nmean_time\tdouble precision\t \tMean time spent in the statement, in milliseconds\r\nstddev_time\tdouble precision\t \tPopulation standard deviation of time spent in the statement, in milliseconds\r\n<\/pre>\n<p>Quant \u00e0 la 9.6, il ne semble pas y avoir eu de modif de faite, le fichier de contr\u00f4le est toujours en 1.3 entre la <a href=\"http:\/\/git.postgresql.org\/gitweb\/?p=postgresql.git;a=blob;f=contrib\/pg_stat_statements\/pg_stat_statements.control;h=53df9789df48afc3ce42a120fb954e990d9c8be4;hb=ba37ac217791dfdf2b327c4b75e7083b6b03a2f5\">9.5.3<\/a> et la <a href=\"http:\/\/git.postgresql.org\/gitweb\/?p=postgresql.git;a=blob;f=contrib\/pg_stat_statements\/pg_stat_statements.control;h=53df9789df48afc3ce42a120fb954e990d9c8be4;hb=8ee29a19d69ab6c19ec0f7565541b9f96e898200\">9.6b1<\/a>. <\/p>\n<p>Un autre fait int\u00e9ressant, jusqu&#8217;en 9.1 inclue, il n&#8217;y a pas encore de notion de <em>queryid<\/em>. La m\u00eame requ\u00eate avec des litt\u00e9raux diff\u00e9rents sera donc logg\u00e9e plusieurs fois.<\/p>\n<p>En 9.2, Tom Lane <a href=\"https:\/\/www.depesz.com\/2012\/03\/30\/waiting-for-9-2-pg_stat_statements-improvements\/\">int\u00e8gre la notion de requ\u00eate <em>repr\u00e9sentative<\/em><\/a>, o\u00f9 les litt\u00e9raux sont remplac\u00e9s par des &#8216;?&#8217;, ce qui permet d&#8217;agr\u00e9ger sur un m\u00eame identifiant, dans la m\u00eame veine que le sql_id chez Oracle ou le query_hash chez SQL Server. Avant la 9.2, on peut s&#8217;inspirer du\u00a0<a href=\"http:\/\/blog.ioguix.net\/postgresql\/2012\/08\/06\/Normalizing-queries-for-pg_stat_statements.html\">code<\/a> de\u00a0\u00a0Jehan-Guillaume de Rorthais (qui s&#8217;inspire de <a href=\"https:\/\/github.com\/dalibo\/pgbadger\">pgbadger<\/a> au passage) pour normaliser:<\/p>\n<pre name=\"code\" class=\"sql\">CREATE OR REPLACE FUNCTION Qnorm(IN TEXT, OUT TEXT) AS \r\n$body$\r\n  SELECT\r\n    regexp_replace(\r\n\tregexp_replace(\r\n\t\tregexp_replace(\r\n\t\t\tregexp_replace(\r\n\t\t\t\tregexp_replace(\r\n\t\t\t\t\tregexp_replace(\r\n\t\t\t\t\t\tregexp_replace(\r\n\t\t\t\t\t\t\tregexp_replace(\r\n\t\t\t\t\t\t\t\t\/*****************************************************************\r\n\t\t\t\t\t\t\t\t\tOn remplace par un espace simple ' ' :\r\n\t\t\t\t\t\t\t\t\t- les espaces suppl\u00e9mentaires\r\n\t\t\t\t\t\t\t\t\t- les caract\u00e8res new line\r\n\t\t\t\t\t\t\t\t\t- les tabulations\r\n\t\t\t\t\t\t\t\t*\/\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tlower($1),'\\s+',' ','g'   \r\n\t\t\t\t\t\t\t),\r\n\t\t\t\t\t\t\t\/*****************************************************************\r\n\t\t\t\t\t\t\t\tOn remplace ce qui se trouve en quotes par 'XXX'\r\n\t\t\t\t\t\t\t*\/\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t$$\\\\'$$,'','g'\r\n\t\t\t\t\t\t),\r\n\t\t\t\t\t\t$$'[^']*'$$, $$'XXX'$$, 'g'\r\n\t\t\t\t\t),\r\n\t\t\t\t\t$$''('')+$$, $$''$$, 'g'\r\n\t\t\t\t),\r\n\t\t\t\t\/*****************************************************************\r\n\t\t\t\t\tOn remplace la cha\u00eene '=NULL' ou '= NULL' par '=O'\r\n\t\t\t\t*\/\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t'=\\s*NULL', '=0', 'g'\r\n\t\t\t),\r\n\t\t\t\/*****************************************************************\r\n\t\t\t\tOn remplace les nombres par 9999\r\n\t\t\t*\/\t\t\t\t\t\t\t\t\t      \r\n\t\t\t'([^a-z_$-])-?([0-9]+)', '\\1'||'9999', 'g'\r\n\t\t),\r\n\t\t\t\/*****************************************************************\r\n\t\t\t\tOn remplace les nombres en hexa par 0xFFFFFFFF\r\n\t\t\t*\/\t\r\n\t\t'([^a-z_$-])0x[0-9a-f]{1,10}', '\\1'||'0xFFFFFFFF', 'g'\r\n\t),\r\n\t\/*****************************************************************\r\n\t\tOn remplace les lit\u00e9raux pass\u00e9s dans les clauses IN par IN (BLA,BLA,BLA)\r\n\t*\/\t\r\n\t-- Remove IN values                            \r\n\t'in\\s*\\([''0x,\\s]*\\)', 'IN (BLA,BLA,BLA)', 'g'  \r\n   ) ;\r\n$body$\r\nLANGUAGE SQL;<\/pre>\n<p>Et du m\u00eame coup calculer un md5 de la forme normalis\u00e9e pour arriver \u00e0 un queryid:<\/p>\n<pre name=\"code\" class=\"sql\">postgres@[local]:5432) [postgres] &gt; \\x\r\nExpanded display is on.\r\n(postgres@[local]:5432) [postgres] &gt; select query, Qnorm(query), md5(Qnorm(query)) as \"queryId\" from pg_stat_statements limit 1 ;\r\n-[ RECORD 1 ]---+--------------------------------------------------------------------------\r\nquery \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 | UPDATE pgbench_accounts SET abalance = abalance + 4875 WHERE aid = 67347;\r\nnormalize_query | update pgbench_accounts set abalance = abalance + 9999 where aid = 9999;\r\nqueryId \u00a0 \u00a0 \u00a0 \u00a0 | 11ff394897d09d426e19dbb68c586bb2<\/pre>\n<p>A partir de l\u00e0, on peut commencer \u00e0 remonter les requ\u00eates lentes :<\/p>\n<pre name=\"code\" class=\"sql\">SELECT  \r\n\tDB.datname                              as DatabaseId\r\n\t,md5(alldb.normalize_query(ST.query))   as QueryId\r\n\t,alldb.normalize_query(ST.query)        as NQuery\r\n\t,sum(total_time)\t\t\tas totaltime\r\n\t,sum(rows)\t\t\t\tas totalrows\r\n\t,sum(calls)\t\t\t\tas totalcalls\r\n\t,sum(ST.shared_blks_hit)\t\tas totalshhits\r\n\t,sum(ST.shared_blks_read)\t\tas totalshread\r\n\t,sum(ST.shared_blks_written)\t\tas totalshwritten\r\nFROM pg_stat_statements ST                      \r\nJOIN pg_database DB on DB.oid = ST.dbid\r\nGROUP BY 1,2,3\r\nhaving sum(ST.shared_blks_hit) &gt; 1500\r\norder by 4 desc LIMIT 10  ;<\/pre>\n<div><a href=\"http:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-large wp-image-5641\" title=\"top10_pgss\" src=\"http:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss-1024x183.png\" alt=\"\" width=\"1024\" height=\"183\" srcset=\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss-1024x183.png 1024w, https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss-300x53.png 300w, https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss-768x138.png 768w, https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/top10_pgss.png 1519w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<p>Evidemment toute cette cuisine n&#8217;est plus n\u00e9cessaire en version 9.2 et sup\u00e9rieure dans la mesure o\u00f9 un queryId est imm\u00e9diatement disponible et que les donn\u00e9es sont d\u00e9j\u00e0 agr\u00e9g\u00e9es&#8230; Mais bon, c\u00f4t\u00e9 Cap Data l&#8217;agent ALlDB pour PostgreSQL supporte toujours la 9.1, donc on fonctionne en calculant un queryid <i>de facto<\/i>.<\/p>\n<h2>Caveats&#8230;<\/h2>\n<p>Ce qu&#8217;il faut toutefois garder \u00e0 l&#8217;esprit quand on d\u00e9cide de se construire un outil bas\u00e9 sur cette interface, c&#8217;est qu&#8217;il n&#8217;y a pas de collecteur, la collecte est r\u00e9alis\u00e9e \u00e0 chaque appel \u00e0 pg_stat_statements. Il faudra donc \u00e0 intervalle r\u00e9gulier effectuer un sample du contenu de pgss et le copier dans des tables de travail sur lesquelles on pourra ensuite calculer des diff\u00e9rences.<\/p>\n<p>Autre d\u00e9tail qui a son importance, c&#8217;est que tout le code est ex\u00e9cut\u00e9 \u00e0 chaque fois, y compris la routine d&#8217;\u00e9viction lorsque <em>pg_stat_statements.max<\/em> est atteint. Ce qui veut dire que dans le cas soit d&#8217;une valeur trop faible du <em>.max<\/em> soit d&#8217;une activit\u00e9 \u00e9lev\u00e9e sur l&#8217;instance, une ex\u00e9cution manuelle de pgss entre 2 snapshots automatis\u00e9s par exemple peut \u00e9vincer des lignes qui ne seront pas vues dans le second snapshot. La solution de fortune \u00e9tant soit de mettre une valeur suffisamment \u00e9lev\u00e9e pour <em>.max<\/em>, soit de faire des clich\u00e9s rapproch\u00e9s, soit les deux.<\/p>\n<p>Dernier probl\u00e8me rencontr\u00e9 avec cette extension, c&#8217;est qu&#8217;il est possible de r\u00e9initialiser les compteurs via <i>pg_stat_statements_reset()<\/i>. Mais contraitement \u00e0 d&#8217;autres vues de stats comme <a href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/monitoring-stats.html\">pg_stat_database ou pg_stat_bgwriter<\/a> qui int\u00e8grent un timestamp de <em>reset,<\/em> ce n&#8217;est pas le cas ici et c&#8217;est franchement dommage, car entre deux samples il faut tester le signe de la diff\u00e9rence entre (val2 &#8211; val1) pour savoir si une r\u00e9initialisation est survenue. C&#8217;est probablement la raison pour laquelle la plupart des outils type <a href=\"https:\/\/github.com\/dalibo\/pgbadger\">pgbadger<\/a> ne se basent que sur log_min_duration_statement et le log.<\/p>\n<h2>A suivre:<\/h2>\n<div>Dans le prochain \u00e9pisode, on r\u00e9cup\u00e8re les plans d&#8217;ex\u00e9cution avec l&#8217;extension de <a href=\"http:\/\/2ndquadrant.com\/fr\/\">2ndQuadrant<\/a>, <a href=\"https:\/\/github.com\/2ndQuadrant\/pg_stat_plans\">pg_stat_plans<\/a>.<\/div>\n<div>A+<\/div>\n<a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-twitter nolightbox\" data-provider=\"twitter\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Twitter\" href=\"https:\/\/twitter.com\/intent\/tweet?url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480&#038;text=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"twitter\" title=\"Share on Twitter\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/twitter.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-linkedin nolightbox\" data-provider=\"linkedin\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Linkedin\" href=\"https:\/\/www.linkedin.com\/shareArticle?mini=true&#038;url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480&#038;title=Requ%C3%AAtes%20consommatrices%20sous%20PostgreSQL%20%28%C3%A9pisode%201%29\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"linkedin\" title=\"Share on Linkedin\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/linkedin.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox\" data-provider=\"mail\" rel=\"nofollow\" title=\"Share by email\" href=\"mailto:?subject=Requ%C3%AAtes%20consommatrices%20sous%20PostgreSQL%20%28%C3%A9pisode%201%29&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F5480\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"mail\" title=\"Share by email\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/mail.png\" \/><\/a>","protected":false},"excerpt":{"rendered":"<p>Lors de l&#8217;\u00e9criture de l&#8217;agent AllDB pour PostgreSQL je me suis pas mal frott\u00e9 avec les solutions de remont\u00e9e de requ\u00eates lentes donc je me suis dit qu&#8217;un petit post sur le sujet ne ferait pas de mal. En gros&hellip; <a href=\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\" class=\"more-link\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":7821,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[266],"tags":[267],"class_list":["post-5480","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-postgresql","tag-pgstats"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG\" \/>\n<meta property=\"og:description\" content=\"Lors de l&#8217;\u00e9criture de l&#8217;agent AllDB pour PostgreSQL je me suis pas mal frott\u00e9 avec les solutions de remont\u00e9e de requ\u00eates lentes donc je me suis dit qu&#8217;un petit post sur le sujet ne ferait pas de mal. En gros&hellip; Continuer la lecture &rarr;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\" \/>\n<meta property=\"og:site_name\" content=\"Capdata TECH BLOG\" \/>\n<meta property=\"article:published_time\" content=\"2016-05-24T05:52:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-09-13T12:06:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/pgstatstatements.png\" \/>\n\t<meta property=\"og:image:width\" content=\"651\" \/>\n\t<meta property=\"og:image:height\" content=\"384\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"David Baffaleuf\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"David Baffaleuf\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\"},\"author\":{\"name\":\"David Baffaleuf\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf\"},\"headline\":\"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1)\",\"datePublished\":\"2016-05-24T05:52:56+00:00\",\"dateModified\":\"2019-09-13T12:06:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\"},\"wordCount\":1168,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/blog.capdata.fr\/#organization\"},\"keywords\":[\"pgstats\"],\"articleSection\":[\"PostgreSQL\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\",\"url\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\",\"name\":\"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG\",\"isPartOf\":{\"@id\":\"https:\/\/blog.capdata.fr\/#website\"},\"datePublished\":\"2016-05-24T05:52:56+00:00\",\"dateModified\":\"2019-09-13T12:06:31+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/blog.capdata.fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.capdata.fr\/#website\",\"url\":\"https:\/\/blog.capdata.fr\/\",\"name\":\"Capdata TECH BLOG\",\"description\":\"Le blog technique sur les bases de donn\u00e9es de CAP DATA Consulting\",\"publisher\":{\"@id\":\"https:\/\/blog.capdata.fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.capdata.fr\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/blog.capdata.fr\/#organization\",\"name\":\"Capdata TECH BLOG\",\"url\":\"https:\/\/blog.capdata.fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp\",\"contentUrl\":\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp\",\"width\":800,\"height\":254,\"caption\":\"Capdata TECH BLOG\"},\"image\":{\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/company\/cap-data-consulting\/mycompany\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf\",\"name\":\"David Baffaleuf\",\"sameAs\":[\"http:\/\/www.capdata.fr\"],\"url\":\"https:\/\/blog.capdata.fr\/index.php\/author\/dbaffaleuf\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/","og_locale":"fr_FR","og_type":"article","og_title":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG","og_description":"Lors de l&#8217;\u00e9criture de l&#8217;agent AllDB pour PostgreSQL je me suis pas mal frott\u00e9 avec les solutions de remont\u00e9e de requ\u00eates lentes donc je me suis dit qu&#8217;un petit post sur le sujet ne ferait pas de mal. En gros&hellip; Continuer la lecture &rarr;","og_url":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/","og_site_name":"Capdata TECH BLOG","article_published_time":"2016-05-24T05:52:56+00:00","article_modified_time":"2019-09-13T12:06:31+00:00","og_image":[{"width":651,"height":384,"url":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2016\/05\/pgstatstatements.png","type":"image\/png"}],"author":"David Baffaleuf","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"David Baffaleuf","Dur\u00e9e de lecture estim\u00e9e":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#article","isPartOf":{"@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/"},"author":{"name":"David Baffaleuf","@id":"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf"},"headline":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1)","datePublished":"2016-05-24T05:52:56+00:00","dateModified":"2019-09-13T12:06:31+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/"},"wordCount":1168,"commentCount":0,"publisher":{"@id":"https:\/\/blog.capdata.fr\/#organization"},"keywords":["pgstats"],"articleSection":["PostgreSQL"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/","url":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/","name":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1) - Capdata TECH BLOG","isPartOf":{"@id":"https:\/\/blog.capdata.fr\/#website"},"datePublished":"2016-05-24T05:52:56+00:00","dateModified":"2019-09-13T12:06:31+00:00","breadcrumb":{"@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.capdata.fr\/index.php\/requetes-consommatrices-sous-postgresql-episode-1\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/blog.capdata.fr\/"},{"@type":"ListItem","position":2,"name":"Requ\u00eates consommatrices sous PostgreSQL (\u00e9pisode 1)"}]},{"@type":"WebSite","@id":"https:\/\/blog.capdata.fr\/#website","url":"https:\/\/blog.capdata.fr\/","name":"Capdata TECH BLOG","description":"Le blog technique sur les bases de donn\u00e9es de CAP DATA Consulting","publisher":{"@id":"https:\/\/blog.capdata.fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.capdata.fr\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/blog.capdata.fr\/#organization","name":"Capdata TECH BLOG","url":"https:\/\/blog.capdata.fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/","url":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp","contentUrl":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp","width":800,"height":254,"caption":"Capdata TECH BLOG"},"image":{"@id":"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/company\/cap-data-consulting\/mycompany\/"]},{"@type":"Person","@id":"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf","name":"David Baffaleuf","sameAs":["http:\/\/www.capdata.fr"],"url":"https:\/\/blog.capdata.fr\/index.php\/author\/dbaffaleuf\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/5480","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/comments?post=5480"}],"version-history":[{"count":133,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/5480\/revisions"}],"predecessor-version":[{"id":5690,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/5480\/revisions\/5690"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/media\/7821"}],"wp:attachment":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/media?parent=5480"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/categories?post=5480"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/tags?post=5480"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}