diff --git a/feed.atom b/feed.atom index 7baa795..823a4e2 100644 --- a/feed.atom +++ b/feed.atom @@ -1,7 +1,7 @@ Planète PostgreSQL https://planete.postgresql.fr/ - 2024-11-27T08:42:37Z + 2024-11-27T12:59:03Z L'actualité de PostgreSQL de français @@ -242,7 +242,7 @@ PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup 2024-07-16T11:24:05Z tag:blog.capdata.fr,2024-07-16:/index.php/postgresql-17-sauvegardes-incrementales/ - <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%2F%3Fp%3D10584&#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 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%2F%3Fp%3D10584&#038;title=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img 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=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10584" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px"><img 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><img loading="lazy" decoding="async" class="alignnone size-full wp-image-10592" src="https://blog.capdata.fr/wp-content/uploads/2024/07/SalesGrowth.jpg" alt="" width="279" height="180" /></p> <p>&nbsp;</p> <p>Bonjour</p> <p>Les 11 et 12 juin derniers, nous étions aux journées PGDAY à Lille pour découvrir les nouveautés autour de PostgreSQL.<br /> Cette conférence regroupe différents professionnels, de la communauté francophone, qui agissent en contribuant sur des sujets techniques mais aussi sur les bonnes pratiques afin d&#8217;utiliser PostgreSQL dans les meilleurs conditions.</p> <p>Un article m&#8217;a particulièrement intéressé cette année, c&#8217;est celui de <a href="https://www.linkedin.com/in/stefan-fercot/?originalSubdomain=be">Stefan Fercot</a> Senior DBA PostgreSQL qui vit en Belgique, et travaille pour une société allemande experte dans les solutions PostgreSQL. Sa présentation portait sur le sujet &#8220;démystifier les sauvegardes incrémentales sous PostgreSQL&#8221;.</p> <p>J&#8217;ai écouté sa conférence tout en ayant hâte de tester sa mise en place dès mon retour de Lille.</p> <p>Je tiens à remercier Stefan pour son travail sur ce sujet sauvegardes PostgreSQL.</p> <p>&nbsp;</p> <p>Tout d&#8217;abord, il faut savoir que les sujets sauvegardes incrémentales ont été déjà abordés avec des outils comme <strong>Barman</strong> ou <strong>Pg_BackRest</strong>, et que certaines instances PostgreSQL de production sont sauvegardées via ces mécanismes depuis quelques années maintenant.</p> <p>Ici, nous parlons de la solution &#8220;backup incremental&#8221; inclu nativement dans le moteur PostgreSQL, et disponible avec l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8220;. C&#8217;est d&#8217;ailleurs ce point que Stefan a souligné durant la journée PGDAY du 11 juin dernier.</p> <p>Cette nouvelle fonctionnalité fait partie de la version <strong>PostgreSQL 17</strong> qui est pour le moment, en version<strong> Beta 2</strong>.<br /> Celle ci devrait sortir, comme à l&#8217;accoutumé, au cour de l&#8217;automne prochain.</p> <p>Preuve que PostgreSQL est en perpétuel évolution, et rejoint la liste des SGBD étant capable, comme peuvent le faire Oracle et SQL Server, de proposer nativement des sauvegardes incrémentales.</p> <p>&nbsp;</p> <h2>Installation de PostgreSQL 17</h2> <p>&nbsp;</p> <p>Pour tester cette fonctionnalité, nous devons installer la toute dernière version de PostgreSQL , la 17 beta 2. Attention, celle ci n&#8217;étant pas disponible dans les dépôts PGDG, nous devons nous charger d&#8217;installer cette version via le site postgresql.org</p> <p><a href="https://download.postgresql.org/pub/repos/yum/testing/17/redhat/rhel-8-x86_64/">https://download.postgresql.org/pub/repos/yum/testing/17/redhat/rhel-8-x86_64/</a></p> <p>Nous disposons d&#8217;un serveur Linux fork Red Hat 8 (Rocky Linux). Il nous faut donc télécharger les &#8220;rpm&#8221; liés à cette version.</p> <p>Les packages dont nous avons besoin sont les suivants</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate"># ls -lrt postgresql1* | awk '{print$9}' postgresql17-contrib-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-libs-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-server-17-beta2_1PGDG.rhel8.x86_64.rpm</pre> <p>&nbsp;</p> <p>Nous les installons avec le compte <strong>root</strong> de notre serveur.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[root@ tmp]# rpm -i postgresql17-libs-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-server-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-contrib-17-beta2_1PGDG.rhel8.x86_64.rpm</pre> <p>&nbsp;</p> <p>Comme nous sommes sur un environnement &#8220;Red Hat like&#8221;, la création d&#8217;une première instance via &#8220;initdb&#8221; est nécessaire.<br /> Surtout, ne pas oublier d&#8217;activer les &#8220;data checksums&#8221; (option -k), nous verrons pourquoi dans la suite de cet article. La suite est à faire avec le compte <strong>postgres</strong>.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ initdb -D /data/postgres/17/pg_data -k The files belonging to this database system will be owned by user &quot;postgres&quot;. This user must also own the server process. The database cluster will be initialized with locale &quot;en_US.UTF-8&quot;. The default database encoding has accordingly been set to &quot;UTF8&quot;. The default text search configuration will be set to &quot;english&quot;. Data page checksums are enabled. creating directory /data/postgres/17/pg_data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default &quot;max_connections&quot; ... 100 selecting default &quot;shared_buffers&quot; ... 128MB selecting default time zone ... UTC creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok initdb: warning: enabling &quot;trust&quot; authentication for local connections initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. Success. You can now start the database server using: pg_ctl -D /data/postgres/17/pg_data -l logfile start</pre> <p>&nbsp;</p> <p>Démarrer cette instance pour s&#8217;assurer que tout fonctionne</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_ctl -D /data/postgres/17/pg_data -l logfile start waiting for server to start.... done server started</pre> <p>&nbsp;</p> <p>Notre version enregistrée est bien une Beta 2. Version qui ne doit pas être mise sur un environnement de production comme le rappelle le site de la communauté PostgreSQL.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ psql (postgres@[local]:5437) [postgres] &gt; select * from version(); version ------------------------------------------------------------------------------------------------------------ PostgreSQL 17beta2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-22), 64-bit (1 row)</pre> <p>&nbsp;</p> <h3>Upgrade de version</h3> <p>&nbsp;</p> <p>Comme nous disposions deja d&#8217;une version PostgreSQL15 sur ce serveur, nous passons par un upgrade via l&#8217;outil &#8220;pg_upgrade&#8221; toujours disponible dans cette nouvelle version.</p> <p>Lancer pg_upgrade en mode check</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_upgrade -b /usr/pgsql-15/bin/ -B /usr/pgsql-17/bin/ -c -d /data/postgres/15/pg_data/ -D /data/postgres/17/pg_data/ -p 5434 -P 5437 ..... ..... *Clusters are compatible* &quot;/usr/pgsql-17/bin/pg_ctl&quot; -w -D &quot;/data/postgres/17/pg_data&quot; -o &quot;&quot; -m smart stop &quot;/data/postgres/17/pg_data/pg_upgrade_output.d/20240708T085906.955/log/pg_upgrade_server.log&quot; </pre> <p>la log est générée dans le $PGDATA de la version 17.</p> <p>Puis lancer l&#8217;exécution de pg_upgrade</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_upgrade -b /usr/pgsql-15/bin/ -B /usr/pgsql-17/bin/ -d /data/postgres/15/pg_data/ -D /data/postgres/17/pg_data/ -p 5434 -P 5437</pre> <p>&nbsp;</p> <h2>Effectuer une sauvegarde</h2> <p>&nbsp;</p> <h3>Prérequis</h3> <p>Avant de pouvoir effectuer une première sauvegarde avec l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8221; natif, il est primordial de respecter certains prérequis important.</p> <ul> <li>L&#8217;instance PostgreSQL doit être créée avec les &#8216;data checksums&#8217; activés. Si ce n&#8217;est pas le cas, utiliser l&#8217;outil &#8220;<strong>pg_checksums</strong>&#8221; avec l&#8217;option &#8220;<strong>-e</strong>&#8220;.</li> </ul> <p>&nbsp;</p> <ul> <li>Si vous lancez une sauvegarde full puis une incrémentale immédiatement, vous avez toutes les chances de tomber sur cette erreur</li> </ul> <pre class="brush: bash; title: ; notranslate">pg_basebackup: error: could not initiate base backup: ERROR: incremental backups cannot be taken unless WAL summarization is enabled</pre> <p>En effet, pour avoir toutes les informations concernant les blocks modifiés, PostgreSQL a besoin de tracer dans les WALs toutes les modifications sur les objets en base.<br /> Pour les DBA Oracle, le &#8220;block change tracking&#8221; de la version Enterprise Edition vous parlera très certainement&#8230;.<br /> Il s&#8217;agit ici de la même fonctionnalité, c&#8217;est à dire, tracer les modifications effectuées dans les blocks de données.<br /> Cette option est le &#8220;<strong>summarize_wal</strong>&#8220;.</p> <p>Pour activer l&#8217;option, nous aurons 2 paramètres à modifier, soit via un ALTER SYSTEM directement sous psql, ou bien dans le fichier &#8220;postgresql.conf&#8221;.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres backup]$ vi $PGDATA/postgresql.conf ... # - WAL Summarization - #summarize_wal = off # run WAL summarizer process? #wal_summary_keep_time = '10d' # when to remove old summary files, 0 = never</pre> <p>Le premier paramètre permet d&#8217;activer cette option.<br /> Le second définit un temps de conservation des informations concernant les blocks modifiés entre une sauvegarde FULL et un incrémentale.</p> <p>Nous activons donc l&#8217;option &#8220;<strong>summarize_wal</strong>&#8221; et la passons à <strong>ON</strong> et laissons à 10 jours le &#8220;<strong>wal_summary_keep_time</strong>&#8220;.</p> <p>Attention, activez ces deux paramètres avant votre première sauvegarde FULL. Si vous le faites après, vous risquez de rencontrer l&#8217;erreur suivante</p> <pre class="brush: bash; title: ; notranslate">pg_basebackup: error: could not initiate base backup: ERROR: WAL summaries are required on timeline 1 from 1/AA000028 to 1/AC000060, but the summaries for that timeline and LSN range are incomplete DETAIL: The first unsummarized LSN in this range is 1/AA000028.</pre> <p>Le LSN pris lors de la première sauvegarde FULL n&#8217;est pas reconnu, et donc la sauvegarde incrémentale ne peut s&#8217;appuyer dessus.</p> <p>&nbsp;</p> <p>Redémarrer l&#8217;instance une fois les modifications effectuées</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_ctl -D /data/postgres/17/pg_data/ restart</pre> <p>&nbsp;</p> <h3>Lancer une sauvegarde FULL</h3> <p>&nbsp;</p> <p>Voici la nouvelle option présente pour l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8221;</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres -]$ pg_basebackup --help pg_basebackup takes a base backup of a running PostgreSQL server. Usage: pg_basebackup [OPTION]... Options controlling the output: -D, --pgdata=DIRECTORY receive base backup into directory -F, --format=p|t output format (plain (default), tar) -i, --incremental=OLDMANIFEST take incremental backup -r, --max-rate=RATE maximum transfer rate to transfer data directory (in kB/s, or use suffix &quot;k&quot; or &quot;M&quot;) .... </pre> <p>&nbsp;</p> <p>Depuis la <a href="https://blog.capdata.fr/index.php/postgresql-13-les-nouveautes-interessantes/">version 13</a> de PostgreSQL, nous disposons pour chaque sauvegarde, d&#8217;un fichier nommé &#8220;backup_manifest&#8221;. Il s&#8217;agit d&#8217;un fichier json qui recense entièrement les objets bases de données sauvegardés avec leur emplacement, leur taille, leur date de modification et leur &#8220;checksum&#8221;.</p> <p>Celui ci est essentiel pour vérifier l&#8217;intégrité de notre sauvegarde avec &#8220;<strong>pg_verifybackup</strong>&#8220;.</p> <p>Nous pouvons à présent faire une première sauvegarde FULL de notre instance PG17.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres -]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17 -F p -l &quot;Full Backup PG17&quot; -P -v pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/AD000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8048&quot; 3097788/3097788 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/AD000158 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>Puis on effectue quelques transactions : création d&#8217;une table et insertions de données sur cette table de test</p> <pre class="brush: sql; title: ; notranslate">(postgres@[local]:5437) [manu] $ &gt; create table backup (nom varchar(20), type varchar(20), date_backup date); CREATE TABLE Time: 3.344 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','FULL','2024-07-08 12:00:00'); INSERT 0 1 Time: 3.612 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','incremental','2024-07-08 13:00:00'); INSERT 0 1 Time: 1.461 ms (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+-------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 (2 rows)</pre> <p>&nbsp;</p> <p>Repérer le fichier &#8220;backup_manifest&#8221; de la sauvegarde FULL réalisée dans le dossier &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17</strong>&#8221;</p> <pre class="brush: bash; title: ; notranslate">[postgres PG17]$ ls -lrt backup* -rw-------. 1 postgres postgres 218 Jul 8 09:19 backup_label -rw-------. 1 postgres postgres 433295 Jul 8 09:20 backup_manifest</pre> <p>&nbsp;</p> <h3>Effectuer une sauvegarde incrémentale</h3> <p>&nbsp;</p> <p>A partir de là, lancer une sauvegarde incrémentale. Nous utilisons l&#8217;option &#8220;<strong>-i</strong>&#8221; pour indiquer à <strong>pg_basebackup</strong> ou est situé le &#8220;backup_manifest&#8221; de la dernière sauvegarde FULL.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17_incr -l &quot;Incremental Backup PG17&quot; -P -v -i /data/postgres/backup/pg_basebackup/PG17/backup_manifest pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/AF000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8139&quot; 12485/3097787 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/AF000120 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>S&#8217;il l&#8217;on compare les deux répertoires de sauvegardes &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17</strong>&#8221; et &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_incr</strong>&#8220;, nous voyons que les tailles sont bien différentes</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ du -h /data/postgres/backup/pg_basebackup/PG17 ...... 3.0G /data/postgres/backup/pg_basebackup/PG17 [postgres - ]$ du -h /data/postgres/backup/pg_basebackup/PG17_incr ...... 35M /data/postgres/backup/pg_basebackup/PG17_incr</pre> <p>&nbsp;</p> <p>Un volume de 3Go pour la sauvegarde FULL de l&#8217;instance contre 35Mo pour l&#8217;incrémentale.<br /> La taille occupée par les objets dans chacune des bases est bien plus faible dans la sauvegarde incrémentale.</p> <p>Nous continuons à insérer des données :</p> <pre class="brush: sql; title: ; notranslate"> [postgres - ]$ psql -d manu (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+-------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 (2 rows) Time: 0.614 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','incremental 2','2024-07-08 14:00:00'); INSERT 0 1 Time: 1.436 ms (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+---------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 sauvegarde | incremental 2 | 2024-07-08 (3 rows)</pre> <p>&nbsp;</p> <p>Puis on lance une seconde sauvegarde incrémentale :</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17_incr_2 -l &quot;Incremental 2 Backup PG17&quot; -P -v -i /data/postgres/backup/pg_basebackup/PG17_incr/backup_manifest pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/B1000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8313&quot; 12260/3097787 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/B1000120 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>Nous remarquons l&#8217;appel au &#8220;backup manifest&#8221; de la dernière sauvegarde incrémentale présente dans le répertoire &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_incr</strong>&#8221;</p> <p>Si l&#8217;on regarde la taille de ce nouveau backup</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres pg_basebackup]$ du -h PG17_incr_2 ....... 35M PG17_incr_2</pre> <p>&nbsp;</p> <p>A nouveau 35 Mo, mais vu le peu de modifications effectuées, la taille n&#8217;est pas très représentative.</p> <p>Ce qu&#8217;il faut retenir, c&#8217;est qu&#8217;en fonction du fichier &#8220;backup manifest&#8221; pris lors de l&#8217;appel à <strong>pg_basebackup</strong>, vous pourrez faire soit<br /> &#8211; une sauvegarde incrémentale qui prendra les dernières modifications depuis la dernière sauvegarde incrémentale effectuée.<br /> &#8211; une sauvegarde différentielle qui prendra les modifications faites depuis la dernière sauvegarde FULL si vous vous appuyez toujours sur le &#8220;backup manifest&#8221; de votre sauvegarde FULL.</p> <p>C&#8217;est donc ce fichier json &#8220;backup manifest&#8221; qui a un rôle essentiel dans l&#8217;élaboration de votre stratégie de sauvegarde au fur et à mesure du temps.</p> <p>&nbsp;</p> <h2>Et la restauration , comment ca se passe ?</h2> <p>&nbsp;</p> <p>Si l&#8217;on souhaite restaurer tous ces jeux de sauvegardes, nous utilisons un nouvel outil qui est &#8220;<strong>pg_combinebackup</strong>&#8220;.<br /> Cet outil permet de &#8220;merger&#8221; les différentes sauvegardes dans un et un seul dossier que l&#8217;on restaurera par la suite.</p> <p>Dans notre exemple, nous avons fait 1 sauvegarde FULL puis 2 incrémentales.<br /> Nous allons donc restaurer ces 3 jeux de sauvegardes afin de retrouver les données. A noter qu&#8217;il existe une option &#8220;&#8211;dry-run&#8221; pour tester la commande</p> <p>Exécuter la commande en prenant en paramètre les dossiers de sauvegardes dans l&#8217;ordre chronologique.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_combinebackup -n -o /data/postgres/backup/pg_basebackup/PG17_ALL /data/postgres/backup/pg_basebackup/PG17 /data/postgres/backup/pg_basebackup/PG17_incr /data/postgres/backup/pg_basebackup/PG17_incr_2 </pre> <p>Si aucune erreur en sortie, on exécute sans l&#8217;option &#8220;dry run&#8221;.</p> <pre class="brush: bash; title: ; notranslate"> [postgres - ]$ pg_combinebackup -o /data/postgres/backup/pg_basebackup/PG17_ALL /data/postgres/backup/pg_basebackup/PG17 /data/postgres/backup/pg_basebackup/PG17_incr /data/postgres/backup/pg_basebackup/PG17_incr_2 </pre> <p>&nbsp;</p> <p>Le répertoire &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_ALL</strong>&#8221; ainsi généré, doit avoir une taille très légèrement supérieure au dossier de la sauvegarde FULL.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ du -h PG17_ALL .... 3.0G PG17_ALL</pre> <p>&nbsp;</p> <p>Dernière étape, nous passons à la restauration des données.</p> <p>Nous arrêtons l&#8217;instance PG17</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_ctl -D /data/postgres/17/pg_data/ stop waiting for server to shut down.... done server stopped</pre> <p>Nous supprimons les données dans $PGDATA</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ rm -rf /data/postgres/17/pg_data/* </pre> <p>Puis nous restaurons ce jeu complet de données avec une simple copie.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ cp -r /data/postgres/backup/pg_basebackup/PG17_ALL/* /data/postgres/17/pg_data/ </pre> <p>Enfin redémarrons l&#8217;instance</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_ctl -D /data/postgres/17/pg_data/ start waiting for server to start....2024-07-08 10:51:45.671 UTC [8909] LOG: redirecting log output to logging collector process 2024-07-08 10:51:45.671 UTC [8909] HINT: Future log output will appear in directory &quot;log&quot;. done server started</pre> <p>&nbsp;</p> <p>Puis contrôler que nous récupérons bien toutes les lignes de notre table &#8220;backup&#8221;.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres@ip-172-44-2-96 pg_basebackup]$ psql -d manu (postgres@[local]:5437) [manu] primaire $ &gt; select * from backup; nom | type | date_backup ------------+---------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 sauvegarde | incremental 2 | 2024-07-08 </pre> <h3></h3> <h3></h3> <h3>Remarques</h3> <ul> <li>Attention, toujours vérifier les sauvegardes à chaque étape avec l&#8217;outil <strong>pg_verifybackup </strong>car rien ne garantit qu&#8217;au moment de l&#8217;appel à <strong>pg_combinebackup</strong> les différents jeux de sauvegardes FULL et/ou incrémentales ne soient pas corrompus.</li> </ul> <p>&nbsp;</p> <ul> <li>Assurez vous d&#8217;être en mode &#8220;data_checksum&#8221; activé et ne pas changer de mode entre les jeux de backup. Le &#8220;backup manifest&#8221; s&#8217;appuie sur ce paramétrage pour valider les checksums de chaque fichier.</li> </ul> <p>&nbsp;</p> <ul> <li>Le mode TAR pour <strong>pg_basebackup</strong> n&#8217;est pas compatible pour les sauvegardes full et incrémentales même si celui ci est possible. Mais c&#8217;est à vous de détarer les fichiers &#8220;<strong>base.tar.gz</strong>&#8221; Et au moment de la restauration  avec &#8220;<strong>pg_combinebackup</strong>&#8220;, une possible corruption est rencontrée.</li> </ul> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_combinebackup -o /data/postgres/backup/pg_basebackup/PG17_all_tar /data/postgres/backup/pg_basebackup/PG17_TAR /data/postgres/backup/pg_basebackup/PG17_incr_TAR pg_combinebackup: error: could not write to file &quot;/data/postgres/backup/pg_basebackup/PG17_all_tar/base/25284/25332&quot;, offset 122470400: wrote 380928 of 409600 pg_combinebackup: removing output directory &quot;/data/postgres/backup/pg_basebackup/PG17_all_tar&quot; </pre> <p>La compression a potentiellement ajoutée une corruption ne rendant pas possible l&#8217;opération de &#8220;merge&#8221; des données.</p> <p>&nbsp;</p> <ul> <li>La restauration PITR est possible bien entendu. N&#8217;oubliez pas de créer le &#8220;<strong>recovery.signal</strong>&#8221; dans $PGDATA et de définir dans le fichier &#8220;postgresql.conf&#8221; les quelques paramètres suivants <ul> <li><span style="color: #3366ff;">recovery_target_name </span></li> <li><span style="color: #3366ff;">recovery_target_time </span></li> <li><span style="color: #3366ff;">recovery_target_xid </span></li> <li><span style="color: #3366ff;">recovery_target_lsn </span></li> <li><span style="color: #808000;">recovery_target_inclusive = off ou on</span></li> <li><span style="color: #808000;">recovery_target_timeline = &#8216;latest&#8217; </span></li> <li><span style="color: #808000;">recovery_target_action = &#8216;pause&#8217; </span></li> </ul> </li> </ul> <p>&nbsp;</p> <p><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p> <p>&nbsp;<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/postgresql-la-streaming-replication-en-12/" rel="bookmark" title="19 novembre 2019">PostgreSQL : la streaming replication en 12.</a> (Emmanuel RAMI) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-13-les-nouveautes-interessantes/" rel="bookmark" title="30 octobre 2020">PostgreSQL 13 : présentation</a> (Emmanuel RAMI) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/oracle-rds-effectuer-des-backup-rman-en-mode-paas/" rel="bookmark" title="25 juin 2019">Oracle RDS : effectuer des backup RMAN en mode PaaS.</a> (Emmanuel RAMI) [AWSNon classéOracle]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-comparatif-entre-barman-et-pgbackrest/" rel="bookmark" title="4 février 2020">PostgreSQL : Comparatif entre Barman et pgBackRest</a> (Capdata team) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/haute-disponibilite-de-postgresql-avec-patroni/" rel="bookmark" title="2 février 2022">Haute disponibilité de PostgreSQL avec Patroni</a> (Ludovic AUGEREAU) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 4.379 ms --></p> <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%2F%3Fp%3D10584&#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 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%2F%3Fp%3D10584&#038;title=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img 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=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10584" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px"><img 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/">PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10584&#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 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%2F%3Fp%3D10584&#038;title=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img 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=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10584" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px"><img 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><img loading="lazy" decoding="async" class="alignnone size-full wp-image-10592" src="https://blog.capdata.fr/wp-content/uploads/2024/07/SalesGrowth.jpg" alt="" width="279" height="180" /></p> <p>&nbsp;</p> <p>Bonjour</p> <p>Les 11 et 12 juin derniers, nous étions aux journées PGDAY à Lille pour découvrir les nouveautés autour de PostgreSQL.<br /> Cette conférence regroupe différents professionnels, de la communauté francophone, qui agissent en contribuant sur des sujets techniques mais aussi sur les bonnes pratiques afin d&#8217;utiliser PostgreSQL dans les meilleurs conditions.</p> <p>Un article m&#8217;a particulièrement intéressé cette année, c&#8217;est celui de <a href="https://www.linkedin.com/in/stefan-fercot/?originalSubdomain=be">Stefan Fercot</a> Senior DBA PostgreSQL qui vit en Belgique, et travaille pour une société allemande experte dans les solutions PostgreSQL. Sa présentation portait sur le sujet &#8220;démystifier les sauvegardes incrémentales sous PostgreSQL&#8221;.</p> <p>J&#8217;ai écouté sa conférence tout en ayant hâte de tester sa mise en place dès mon retour de Lille.</p> <p>Je tiens à remercier Stefan pour son travail sur ce sujet sauvegardes PostgreSQL.</p> <p>&nbsp;</p> <p>Tout d&#8217;abord, il faut savoir que les sujets sauvegardes incrémentales ont été déjà abordés avec des outils comme <strong>Barman</strong> ou <strong>Pg_BackRest</strong>, et que certaines instances PostgreSQL de production sont sauvegardées via ces mécanismes depuis quelques années maintenant.</p> <p>Ici, nous parlons de la solution &#8220;backup incremental&#8221; inclu nativement dans le moteur PostgreSQL, et disponible avec l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8220;. C&#8217;est d&#8217;ailleurs ce point que Stefan a souligné durant la journée PGDAY du 11 juin dernier.</p> <p>Cette nouvelle fonctionnalité fait partie de la version <strong>PostgreSQL 17</strong> qui est pour le moment, en version<strong> Beta 2</strong>.<br /> Celle ci devrait sortir, comme à l&#8217;accoutumé, au cour de l&#8217;automne prochain.</p> <p>Preuve que PostgreSQL est en perpétuel évolution, et rejoint la liste des SGBD étant capable, comme peuvent le faire Oracle et SQL Server, de proposer nativement des sauvegardes incrémentales.</p> <p>&nbsp;</p> <h2>Installation de PostgreSQL 17</h2> <p>&nbsp;</p> <p>Pour tester cette fonctionnalité, nous devons installer la toute dernière version de PostgreSQL , la 17 beta 2. Attention, celle ci n&#8217;étant pas disponible dans les dépôts PGDG, nous devons nous charger d&#8217;installer cette version via le site postgresql.org</p> <p><a href="https://download.postgresql.org/pub/repos/yum/testing/17/redhat/rhel-8-x86_64/">https://download.postgresql.org/pub/repos/yum/testing/17/redhat/rhel-8-x86_64/</a></p> <p>Nous disposons d&#8217;un serveur Linux fork Red Hat 8 (Rocky Linux). Il nous faut donc télécharger les &#8220;rpm&#8221; liés à cette version.</p> <p>Les packages dont nous avons besoin sont les suivants</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate"># ls -lrt postgresql1* | awk '{print$9}' postgresql17-contrib-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-libs-17-beta2_1PGDG.rhel8.x86_64.rpm postgresql17-server-17-beta2_1PGDG.rhel8.x86_64.rpm</pre> <p>&nbsp;</p> <p>Nous les installons avec le compte <strong>root</strong> de notre serveur.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[root@ tmp]# rpm -i postgresql17-libs-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-server-17-beta2_1PGDG.rhel8.x86_64.rpm [root@ tmp]# rpm -i postgresql17-contrib-17-beta2_1PGDG.rhel8.x86_64.rpm</pre> <p>&nbsp;</p> <p>Comme nous sommes sur un environnement &#8220;Red Hat like&#8221;, la création d&#8217;une première instance via &#8220;initdb&#8221; est nécessaire.<br /> Surtout, ne pas oublier d&#8217;activer les &#8220;data checksums&#8221; (option -k), nous verrons pourquoi dans la suite de cet article. La suite est à faire avec le compte <strong>postgres</strong>.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ initdb -D /data/postgres/17/pg_data -k The files belonging to this database system will be owned by user &quot;postgres&quot;. This user must also own the server process. The database cluster will be initialized with locale &quot;en_US.UTF-8&quot;. The default database encoding has accordingly been set to &quot;UTF8&quot;. The default text search configuration will be set to &quot;english&quot;. Data page checksums are enabled. creating directory /data/postgres/17/pg_data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default &quot;max_connections&quot; ... 100 selecting default &quot;shared_buffers&quot; ... 128MB selecting default time zone ... UTC creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok initdb: warning: enabling &quot;trust&quot; authentication for local connections initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. Success. You can now start the database server using: pg_ctl -D /data/postgres/17/pg_data -l logfile start</pre> <p>&nbsp;</p> <p>Démarrer cette instance pour s&#8217;assurer que tout fonctionne</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_ctl -D /data/postgres/17/pg_data -l logfile start waiting for server to start.... done server started</pre> <p>&nbsp;</p> <p>Notre version enregistrée est bien une Beta 2. Version qui ne doit pas être mise sur un environnement de production comme le rappelle le site de la communauté PostgreSQL.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ psql (postgres@[local]:5437) [postgres] &gt; select * from version(); version ------------------------------------------------------------------------------------------------------------ PostgreSQL 17beta2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-22), 64-bit (1 row)</pre> <p>&nbsp;</p> <h3>Upgrade de version</h3> <p>&nbsp;</p> <p>Comme nous disposions deja d&#8217;une version PostgreSQL15 sur ce serveur, nous passons par un upgrade via l&#8217;outil &#8220;pg_upgrade&#8221; toujours disponible dans cette nouvelle version.</p> <p>Lancer pg_upgrade en mode check</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_upgrade -b /usr/pgsql-15/bin/ -B /usr/pgsql-17/bin/ -c -d /data/postgres/15/pg_data/ -D /data/postgres/17/pg_data/ -p 5434 -P 5437 ..... ..... *Clusters are compatible* &quot;/usr/pgsql-17/bin/pg_ctl&quot; -w -D &quot;/data/postgres/17/pg_data&quot; -o &quot;&quot; -m smart stop &quot;/data/postgres/17/pg_data/pg_upgrade_output.d/20240708T085906.955/log/pg_upgrade_server.log&quot; </pre> <p>la log est générée dans le $PGDATA de la version 17.</p> <p>Puis lancer l&#8217;exécution de pg_upgrade</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_upgrade -b /usr/pgsql-15/bin/ -B /usr/pgsql-17/bin/ -d /data/postgres/15/pg_data/ -D /data/postgres/17/pg_data/ -p 5434 -P 5437</pre> <p>&nbsp;</p> <h2>Effectuer une sauvegarde</h2> <p>&nbsp;</p> <h3>Prérequis</h3> <p>Avant de pouvoir effectuer une première sauvegarde avec l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8221; natif, il est primordial de respecter certains prérequis important.</p> <ul> <li>L&#8217;instance PostgreSQL doit être créée avec les &#8216;data checksums&#8217; activés. Si ce n&#8217;est pas le cas, utiliser l&#8217;outil &#8220;<strong>pg_checksums</strong>&#8221; avec l&#8217;option &#8220;<strong>-e</strong>&#8220;.</li> </ul> <p>&nbsp;</p> <ul> <li>Si vous lancez une sauvegarde full puis une incrémentale immédiatement, vous avez toutes les chances de tomber sur cette erreur</li> </ul> <pre class="brush: bash; title: ; notranslate">pg_basebackup: error: could not initiate base backup: ERROR: incremental backups cannot be taken unless WAL summarization is enabled</pre> <p>En effet, pour avoir toutes les informations concernant les blocks modifiés, PostgreSQL a besoin de tracer dans les WALs toutes les modifications sur les objets en base.<br /> Pour les DBA Oracle, le &#8220;block change tracking&#8221; de la version Enterprise Edition vous parlera très certainement&#8230;.<br /> Il s&#8217;agit ici de la même fonctionnalité, c&#8217;est à dire, tracer les modifications effectuées dans les blocks de données.<br /> Cette option est le &#8220;<strong>summarize_wal</strong>&#8220;.</p> <p>Pour activer l&#8217;option, nous aurons 2 paramètres à modifier, soit via un ALTER SYSTEM directement sous psql, ou bien dans le fichier &#8220;postgresql.conf&#8221;.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres backup]$ vi $PGDATA/postgresql.conf ... # - WAL Summarization - #summarize_wal = off # run WAL summarizer process? #wal_summary_keep_time = '10d' # when to remove old summary files, 0 = never</pre> <p>Le premier paramètre permet d&#8217;activer cette option.<br /> Le second définit un temps de conservation des informations concernant les blocks modifiés entre une sauvegarde FULL et un incrémentale.</p> <p>Nous activons donc l&#8217;option &#8220;<strong>summarize_wal</strong>&#8221; et la passons à <strong>ON</strong> et laissons à 10 jours le &#8220;<strong>wal_summary_keep_time</strong>&#8220;.</p> <p>Attention, activez ces deux paramètres avant votre première sauvegarde FULL. Si vous le faites après, vous risquez de rencontrer l&#8217;erreur suivante</p> <pre class="brush: bash; title: ; notranslate">pg_basebackup: error: could not initiate base backup: ERROR: WAL summaries are required on timeline 1 from 1/AA000028 to 1/AC000060, but the summaries for that timeline and LSN range are incomplete DETAIL: The first unsummarized LSN in this range is 1/AA000028.</pre> <p>Le LSN pris lors de la première sauvegarde FULL n&#8217;est pas reconnu, et donc la sauvegarde incrémentale ne peut s&#8217;appuyer dessus.</p> <p>&nbsp;</p> <p>Redémarrer l&#8217;instance une fois les modifications effectuées</p> <pre class="brush: bash; title: ; notranslate">[postgres ~]$ pg_ctl -D /data/postgres/17/pg_data/ restart</pre> <p>&nbsp;</p> <h3>Lancer une sauvegarde FULL</h3> <p>&nbsp;</p> <p>Voici la nouvelle option présente pour l&#8217;outil &#8220;<strong>pg_basebackup</strong>&#8221;</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres -]$ pg_basebackup --help pg_basebackup takes a base backup of a running PostgreSQL server. Usage: pg_basebackup [OPTION]... Options controlling the output: -D, --pgdata=DIRECTORY receive base backup into directory -F, --format=p|t output format (plain (default), tar) -i, --incremental=OLDMANIFEST take incremental backup -r, --max-rate=RATE maximum transfer rate to transfer data directory (in kB/s, or use suffix &quot;k&quot; or &quot;M&quot;) .... </pre> <p>&nbsp;</p> <p>Depuis la <a href="https://blog.capdata.fr/index.php/postgresql-13-les-nouveautes-interessantes/">version 13</a> de PostgreSQL, nous disposons pour chaque sauvegarde, d&#8217;un fichier nommé &#8220;backup_manifest&#8221;. Il s&#8217;agit d&#8217;un fichier json qui recense entièrement les objets bases de données sauvegardés avec leur emplacement, leur taille, leur date de modification et leur &#8220;checksum&#8221;.</p> <p>Celui ci est essentiel pour vérifier l&#8217;intégrité de notre sauvegarde avec &#8220;<strong>pg_verifybackup</strong>&#8220;.</p> <p>Nous pouvons à présent faire une première sauvegarde FULL de notre instance PG17.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres -]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17 -F p -l &quot;Full Backup PG17&quot; -P -v pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/AD000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8048&quot; 3097788/3097788 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/AD000158 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>Puis on effectue quelques transactions : création d&#8217;une table et insertions de données sur cette table de test</p> <pre class="brush: sql; title: ; notranslate">(postgres@[local]:5437) [manu] $ &gt; create table backup (nom varchar(20), type varchar(20), date_backup date); CREATE TABLE Time: 3.344 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','FULL','2024-07-08 12:00:00'); INSERT 0 1 Time: 3.612 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','incremental','2024-07-08 13:00:00'); INSERT 0 1 Time: 1.461 ms (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+-------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 (2 rows)</pre> <p>&nbsp;</p> <p>Repérer le fichier &#8220;backup_manifest&#8221; de la sauvegarde FULL réalisée dans le dossier &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17</strong>&#8221;</p> <pre class="brush: bash; title: ; notranslate">[postgres PG17]$ ls -lrt backup* -rw-------. 1 postgres postgres 218 Jul 8 09:19 backup_label -rw-------. 1 postgres postgres 433295 Jul 8 09:20 backup_manifest</pre> <p>&nbsp;</p> <h3>Effectuer une sauvegarde incrémentale</h3> <p>&nbsp;</p> <p>A partir de là, lancer une sauvegarde incrémentale. Nous utilisons l&#8217;option &#8220;<strong>-i</strong>&#8221; pour indiquer à <strong>pg_basebackup</strong> ou est situé le &#8220;backup_manifest&#8221; de la dernière sauvegarde FULL.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17_incr -l &quot;Incremental Backup PG17&quot; -P -v -i /data/postgres/backup/pg_basebackup/PG17/backup_manifest pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/AF000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8139&quot; 12485/3097787 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/AF000120 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>S&#8217;il l&#8217;on compare les deux répertoires de sauvegardes &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17</strong>&#8221; et &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_incr</strong>&#8220;, nous voyons que les tailles sont bien différentes</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ du -h /data/postgres/backup/pg_basebackup/PG17 ...... 3.0G /data/postgres/backup/pg_basebackup/PG17 [postgres - ]$ du -h /data/postgres/backup/pg_basebackup/PG17_incr ...... 35M /data/postgres/backup/pg_basebackup/PG17_incr</pre> <p>&nbsp;</p> <p>Un volume de 3Go pour la sauvegarde FULL de l&#8217;instance contre 35Mo pour l&#8217;incrémentale.<br /> La taille occupée par les objets dans chacune des bases est bien plus faible dans la sauvegarde incrémentale.</p> <p>Nous continuons à insérer des données :</p> <pre class="brush: sql; title: ; notranslate"> [postgres - ]$ psql -d manu (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+-------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 (2 rows) Time: 0.614 ms (postgres@[local]:5437) [manu] $ &gt; insert into backup values ('sauvegarde','incremental 2','2024-07-08 14:00:00'); INSERT 0 1 Time: 1.436 ms (postgres@[local]:5437) [manu] $ &gt; select * from backup; nom | type | date_backup ------------+---------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 sauvegarde | incremental 2 | 2024-07-08 (3 rows)</pre> <p>&nbsp;</p> <p>Puis on lance une seconde sauvegarde incrémentale :</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_basebackup -D /data/postgres/backup/pg_basebackup/PG17_incr_2 -l &quot;Incremental 2 Backup PG17&quot; -P -v -i /data/postgres/backup/pg_basebackup/PG17_incr/backup_manifest pg_basebackup: initiating base backup, waiting for checkpoint to complete pg_basebackup: checkpoint completed pg_basebackup: write-ahead log start point: 1/B1000028 on timeline 1 pg_basebackup: starting background WAL receiver pg_basebackup: created temporary replication slot &quot;pg_basebackup_8313&quot; 12260/3097787 kB (100%), 1/1 tablespace pg_basebackup: write-ahead log end point: 1/B1000120 pg_basebackup: waiting for background process to finish streaming ... pg_basebackup: syncing data to disk ... pg_basebackup: renaming backup_manifest.tmp to backup_manifest pg_basebackup: base backup completed</pre> <p>&nbsp;</p> <p>Nous remarquons l&#8217;appel au &#8220;backup manifest&#8221; de la dernière sauvegarde incrémentale présente dans le répertoire &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_incr</strong>&#8221;</p> <p>Si l&#8217;on regarde la taille de ce nouveau backup</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres pg_basebackup]$ du -h PG17_incr_2 ....... 35M PG17_incr_2</pre> <p>&nbsp;</p> <p>A nouveau 35 Mo, mais vu le peu de modifications effectuées, la taille n&#8217;est pas très représentative.</p> <p>Ce qu&#8217;il faut retenir, c&#8217;est qu&#8217;en fonction du fichier &#8220;backup manifest&#8221; pris lors de l&#8217;appel à <strong>pg_basebackup</strong>, vous pourrez faire soit<br /> &#8211; une sauvegarde incrémentale qui prendra les dernières modifications depuis la dernière sauvegarde incrémentale effectuée.<br /> &#8211; une sauvegarde différentielle qui prendra les modifications faites depuis la dernière sauvegarde FULL si vous vous appuyez toujours sur le &#8220;backup manifest&#8221; de votre sauvegarde FULL.</p> <p>C&#8217;est donc ce fichier json &#8220;backup manifest&#8221; qui a un rôle essentiel dans l&#8217;élaboration de votre stratégie de sauvegarde au fur et à mesure du temps.</p> <p>&nbsp;</p> <h2>Et la restauration , comment ca se passe ?</h2> <p>&nbsp;</p> <p>Si l&#8217;on souhaite restaurer tous ces jeux de sauvegardes, nous utilisons un nouvel outil qui est &#8220;<strong>pg_combinebackup</strong>&#8220;.<br /> Cet outil permet de &#8220;merger&#8221; les différentes sauvegardes dans un et un seul dossier que l&#8217;on restaurera par la suite.</p> <p>Dans notre exemple, nous avons fait 1 sauvegarde FULL puis 2 incrémentales.<br /> Nous allons donc restaurer ces 3 jeux de sauvegardes afin de retrouver les données. A noter qu&#8217;il existe une option &#8220;&#8211;dry-run&#8221; pour tester la commande</p> <p>Exécuter la commande en prenant en paramètre les dossiers de sauvegardes dans l&#8217;ordre chronologique.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_combinebackup -n -o /data/postgres/backup/pg_basebackup/PG17_ALL /data/postgres/backup/pg_basebackup/PG17 /data/postgres/backup/pg_basebackup/PG17_incr /data/postgres/backup/pg_basebackup/PG17_incr_2 </pre> <p>Si aucune erreur en sortie, on exécute sans l&#8217;option &#8220;dry run&#8221;.</p> <pre class="brush: bash; title: ; notranslate"> [postgres - ]$ pg_combinebackup -o /data/postgres/backup/pg_basebackup/PG17_ALL /data/postgres/backup/pg_basebackup/PG17 /data/postgres/backup/pg_basebackup/PG17_incr /data/postgres/backup/pg_basebackup/PG17_incr_2 </pre> <p>&nbsp;</p> <p>Le répertoire &#8220;<strong>/data/postgres/backup/pg_basebackup/PG17_ALL</strong>&#8221; ainsi généré, doit avoir une taille très légèrement supérieure au dossier de la sauvegarde FULL.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ du -h PG17_ALL .... 3.0G PG17_ALL</pre> <p>&nbsp;</p> <p>Dernière étape, nous passons à la restauration des données.</p> <p>Nous arrêtons l&#8217;instance PG17</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_ctl -D /data/postgres/17/pg_data/ stop waiting for server to shut down.... done server stopped</pre> <p>Nous supprimons les données dans $PGDATA</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ rm -rf /data/postgres/17/pg_data/* </pre> <p>Puis nous restaurons ce jeu complet de données avec une simple copie.</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ cp -r /data/postgres/backup/pg_basebackup/PG17_ALL/* /data/postgres/17/pg_data/ </pre> <p>Enfin redémarrons l&#8217;instance</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_ctl -D /data/postgres/17/pg_data/ start waiting for server to start....2024-07-08 10:51:45.671 UTC [8909] LOG: redirecting log output to logging collector process 2024-07-08 10:51:45.671 UTC [8909] HINT: Future log output will appear in directory &quot;log&quot;. done server started</pre> <p>&nbsp;</p> <p>Puis contrôler que nous récupérons bien toutes les lignes de notre table &#8220;backup&#8221;.</p> <p>&nbsp;</p> <pre class="brush: bash; title: ; notranslate">[postgres@ip-172-44-2-96 pg_basebackup]$ psql -d manu (postgres@[local]:5437) [manu] primaire $ &gt; select * from backup; nom | type | date_backup ------------+---------------+------------- sauvegarde | FULL | 2024-07-08 sauvegarde | incremental | 2024-07-08 sauvegarde | incremental 2 | 2024-07-08 </pre> <h3></h3> <h3></h3> <h3>Remarques</h3> <ul> <li>Attention, toujours vérifier les sauvegardes à chaque étape avec l&#8217;outil <strong>pg_verifybackup </strong>car rien ne garantit qu&#8217;au moment de l&#8217;appel à <strong>pg_combinebackup</strong> les différents jeux de sauvegardes FULL et/ou incrémentales ne soient pas corrompus.</li> </ul> <p>&nbsp;</p> <ul> <li>Assurez vous d&#8217;être en mode &#8220;data_checksum&#8221; activé et ne pas changer de mode entre les jeux de backup. Le &#8220;backup manifest&#8221; s&#8217;appuie sur ce paramétrage pour valider les checksums de chaque fichier.</li> </ul> <p>&nbsp;</p> <ul> <li>Le mode TAR pour <strong>pg_basebackup</strong> n&#8217;est pas compatible pour les sauvegardes full et incrémentales même si celui ci est possible. Mais c&#8217;est à vous de détarer les fichiers &#8220;<strong>base.tar.gz</strong>&#8221; Et au moment de la restauration  avec &#8220;<strong>pg_combinebackup</strong>&#8220;, une possible corruption est rencontrée.</li> </ul> <pre class="brush: bash; title: ; notranslate">[postgres - ]$ pg_combinebackup -o /data/postgres/backup/pg_basebackup/PG17_all_tar /data/postgres/backup/pg_basebackup/PG17_TAR /data/postgres/backup/pg_basebackup/PG17_incr_TAR pg_combinebackup: error: could not write to file &quot;/data/postgres/backup/pg_basebackup/PG17_all_tar/base/25284/25332&quot;, offset 122470400: wrote 380928 of 409600 pg_combinebackup: removing output directory &quot;/data/postgres/backup/pg_basebackup/PG17_all_tar&quot; </pre> <p>La compression a potentiellement ajoutée une corruption ne rendant pas possible l&#8217;opération de &#8220;merge&#8221; des données.</p> <p>&nbsp;</p> <ul> <li>La restauration PITR est possible bien entendu. N&#8217;oubliez pas de créer le &#8220;<strong>recovery.signal</strong>&#8221; dans $PGDATA et de définir dans le fichier &#8220;postgresql.conf&#8221; les quelques paramètres suivants <ul> <li><span style="color: #3366ff;">recovery_target_name </span></li> <li><span style="color: #3366ff;">recovery_target_time </span></li> <li><span style="color: #3366ff;">recovery_target_xid </span></li> <li><span style="color: #3366ff;">recovery_target_lsn </span></li> <li><span style="color: #808000;">recovery_target_inclusive = off ou on</span></li> <li><span style="color: #808000;">recovery_target_timeline = &#8216;latest&#8217; </span></li> <li><span style="color: #808000;">recovery_target_action = &#8216;pause&#8217; </span></li> </ul> </li> </ul> <p>&nbsp;</p> <p><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p> <p>&nbsp;<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/postgresql-la-streaming-replication-en-12/" rel="bookmark" title="19 novembre 2019">PostgreSQL : la streaming replication en 12.</a> (Emmanuel RAMI) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-13-les-nouveautes-interessantes/" rel="bookmark" title="30 octobre 2020">PostgreSQL 13 : présentation</a> (Emmanuel RAMI) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/oracle-rds-effectuer-des-backup-rman-en-mode-paas/" rel="bookmark" title="25 juin 2019">Oracle RDS : effectuer des backup RMAN en mode PaaS.</a> (Emmanuel RAMI) [AWSNon classéOracle]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-comparatif-entre-barman-et-pgbackrest/" rel="bookmark" title="4 février 2020">PostgreSQL : Comparatif entre Barman et pgBackRest</a> (Capdata team) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/haute-disponibilite-de-postgresql-avec-patroni/" rel="bookmark" title="2 février 2022">Haute disponibilité de PostgreSQL avec Patroni</a> (Ludovic AUGEREAU) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 6.737 ms --></p> <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%2F%3Fp%3D10584&#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 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%2F%3Fp%3D10584&#038;title=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img 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=PostgreSQL%2017%20%3A%20des%20sauvegardes%20incr%C3%A9mentales%20avec%20pg_basebackup&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10584" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px"><img 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/">PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>&#160; Bonjour Les 11 et 12 juin derniers, nous étions aux journées PGDAY à Lille pour découvrir les nouveautés autour de PostgreSQL. Cette conférence regroupe différents professionnels, de la communauté francophone, qui agissent en contribuant sur des sujets techniques mais&#8230; <a href="https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/">PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> @@ -253,7 +253,7 @@ PGO : la suite 2024-05-29T08:58:17Z tag:blog.capdata.fr,2024-05-29:/index.php/pgo-la-suite/ - <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%2F%3Fp%3D10562&#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%2F%3Fp%3D10562&#038;title=PGO%20%3A%20la%20suite" 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=PGO%20%3A%20la%20suite&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10562" 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>La gestion efficace des clusters PostgreSQL dans un environnement Kubernetes est un défi complexe auquel sont confrontées de nombreuses entreprises aujourd&#8217;hui. PGO offre une solution déclarative qui automatise la gestion des clusters PostgreSQL, simplifiant ainsi le déploiement, la mise à l&#8217;échelle et la gestion des bases de données PostgreSQL dans un environnement Kubernetes.</p> <p>Pour faire suite à l&#8217;article de David sur PGO et à la demande d&#8217;un de nos clients, j&#8217;ai réalisé une étude approfondie de plusieurs fonctionnalités de PGO.<br /> Cet article va faire un petit tour d&#8217;horizon des outils principaux inclus dans l&#8217;implémentation de PGO. Que ce soit pour la sauvegarde avec pgbackrest, pour la balance des connexion avec pgbouncer ou pour le monitoring avec prometheus, PGO ne manque pas d&#8217;utilitaire dont l&#8217;utilisation est facilitée par la solution tout embarqué.</p> <h3>Pgbackrest :</h3> <h4>Utilité :</h4> <p>PgBackRest est une solution de sauvegarde et de restauration pour les bases de données PostgreSQL qui propose plusieurs fonctionnalités, telles que la sauvegarde et la restauration parallèles, la compression, les sauvegardes complètes, différentielles et incrémentielles, la rotation des sauvegardes et l&#8217;expiration des archives, l&#8217;intégrité des sauvegardes, etc. Il prend en charge plusieurs référentiels, qui peuvent être situés localement ou à distance via TLS/SSH, ou être des stockages fournis par le cloud comme S3/GCS/Azure.<br /> L&#8217;architecture de pgbackrest pour PGO est la suivante :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10564" src="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1-300x168.png" alt="" width="300" height="168" srcset="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1-300x168.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image1.png 605w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p> <h4>Mise en place :</h4> <p>On peut imaginer plusieurs moyens de mettre en place le pgbackrest. Dans un premier temps, nous avons la sauvegarde classique en système de fichier, comme dans notre exemple sur le blog :</p> <h5>1) La sauvegarde sur volume persistant Kubernetes :</h5> <pre class="brush: yaml; title: ; notranslate"> - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>Ce type de sauvegarde utilise un volume persistant de Kubernetes pour recueillir nos sauvegardes et les garder.<br /> Une PersistentVolumeClaim (PVC) est une demande de stockage faite par un utilisateur. Elle est similaire à un Pod. Les Pods consomment des ressources de nœud et les PVC consomment des ressources de PV (PersistentVolume). Les Pods peuvent demander des niveaux spécifiques de ressources (CPU et mémoire). Les revendications peuvent demander une taille spécifique et des modes d&#8217;accès spécifiques (par exemple, elles peuvent être montées en ReadWriteOnce, ReadOnlyMany, ReadWriteMany, ou ReadWriteOncePod, voir AccessModes).</p> <h5>2) Le stockage pour S3 :</h5> <p>Pour pouvoir faire du stockage dans S3, il faut rajouter un fichier de configuration dans notre dossier de déploiement. Le fichier doit s’appeler s3.conf. Ce fichier contient les crédential de connexion à un AWS S3 :</p> <pre class="brush: bash; title: ; notranslate"> repo1-s3-key=$YOUR_AWS_S3_KEY repo1-s3-key-secret=$YOUR_AWS_S3_KEY_SECRET </pre> <p>Une fois que c’est configuré dans votre fichier, il ne reste plus qu’à modifier le postgresql.yaml, et configurer dans la partie backup :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-s3-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster1/repo1 repos: - name: repo1 s3: bucket: &quot;&lt;YOUR_AWS_S3_BUCKET_NAME&gt;&quot; endpoint: &quot;&lt;YOUR_AWS_S3_ENDPOINT&gt;&quot; region: &quot;&lt;YOUR_AWS_S3_REGION&gt;&quot; </pre> <p>Une fois configuré, et le job mis dans le cron, vous devriez voir apparaitre les sauvegardes sur le volume S3.</p> <h5>3) Le stockage GCS :</h5> <p>Comme pour Amazon S3 on peut sauvegarder nos backups dans Google Cloud Storage. Pour pouvoir le faire fonctionner il vous faut copier votre GCS key secret (qui est un fichier JSON) dans un gcs.conf que vous allez placer dans votre dossier Kustomize.<br /> Il vous suffit ensuite de modifier votre fichier postgres.yaml pour ajouter dans la partie backup la configuration pour une sauvegarde gcs :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-gcs-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster1/repo1 repos: - name: repo1 gcs: bucket: &quot;&lt;YOUR_GCS_BUCKET_NAME&gt;&quot; </pre> <p>Il ne vous reste plus qu’à regénérer vos pods, et votre sauvegarde arrivera directement dans votre Google Cloud Service.</p> <h5>4) Le stockage Azur Blob Storage :</h5> <p>Comme pour les deux points précédents, vous pouvez également stocker vos sauvegardes sur le blob storage d’Azure. Pour cela il vous faut créer un fichier dans votre kustomize, avec à l’intérieur la configuration pour votre point de sauvegarde Azure. Il vous faut l’appeler azure.conf et il devra contenir les lignes suivantes :</p> <pre class="brush: bash; title: ; notranslate"> repo1-azure-account=$YOUR_AZURE_ACCOUNT repo1-azure-key=$YOUR_AZURE_KEY </pre> <p>Il faut ensuite intégrer ces modifications dans votre fichier postgres.yaml :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-azure-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster/repo1 repos: - name: repo1 azure: container: &quot;&lt;YOUR_AZURE_CONTAINER&gt;&quot; </pre> <p>Bien sur rien ne vous interdit, et c’est même conseillé, de joindre plusieurs moyens de sauvegarde. Cela permet notamment de s’assurer une plus grande fiabilité du système de sauvegarde, en s’assurant qu’elles sont disponibles à plusieurs endroits.<br /> Une fois que vous avez décidé d’où vous allez stocker vos sauvegardes, et que vous l’avez configuré, il faut maintenant décider des différents paramètres de ces sauvegardes : la programmation, la rétention…</p> <h5>5) La programmation des sauvegardes :</h5> <p>Il faut savoir que par défaut, PGO sauvegarde automatiquement les WAL dans la méthode de sauvegarde que vous lui avez configuré. C’est donc une forme de sauvegarde en soit.<br /> Mais dans le cadre d’une récupération après incident majeur, il peut aussi être utilise d’avoir des sauvegardes full programmées. Pgbackrest, qui est l’outil utilisé par PGO permet de mettre en place trois types de sauvegarde : les incrémentales, les différentielles et les fulls.<br /> Chaque type de sauvegarde peut être programmée en suivant une notation identique à celle des crontab. Par exemple :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: repos: - name: repo1 schedules: full: &quot;0 1 * * 0&quot; differential: &quot;0 1 * * 1-6&quot; </pre> <p>Le fait d’implémenter ces planifications créera des CronJobs dans Kubernetes.</p> <h5>6) La rétention des backups :</h5> <p>Vous pouvez définir une rétention maximum pour vos backups sur le support de backup de votre choix. Une fois que cette rétention sera atteinte, pgbackrest fera le ménage tout seul des sauvegardes et des WAL qui lui sont reliées.<br /> Il y a deux types de rétentions que l’on peut définir : les rétentions « count » basées sur le nombre de backup que l’on souhaite garder et les rétentions « time » basées sur le nombre de jours ou vous souhaitez garder votre sauvegarde.</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: global: repo1-retention-full: &quot;14&quot; repo1-retention-full-type: time </pre> <h5>7) La sauvegarde unique :</h5> <p>Si dans le cadre d’un besoin particuliers, une grosse modification ou une migration par exemple, vous avez besoin de prendre une sauvegarde immédiate sans forcément attendre que le cron n’arrive, vous pouvez le faire.<br /> Pour la configuration de cette sauvegarde, il faudra l’annoter comme « manuelle » :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: manual: repoName: repo1 options: - --type=full </pre> <p>Il vous faudra ensuite déclencher cette sauvegarde avec une commande manuelle. Dans le cadre de notre cluster exemple pgcluster1 :<br /> kubectl annotate -n postgres-operator postgrescluster pgcluster1 \ postgres-operator.crunchydata.com/pgbackrest-backup=&#8221;$(date)&#8221;</p> <h5>8) Faire un clone à partir d’un repo :</h5> <p>Quand on a configuré un repo sur notre instance primaire, on peut facilement créer un clone de notre instance à l’aide de notre sauvegarde. Ainsi, on créer un tout nouveau Pods à partir des informations stockées à propos du pod que l’on possède déjà. Ici, nous allons créer un nouveau pod à partir de notre pod pgcluster1 :</p> <pre class="brush: yaml; title: ; notranslate"> apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: name: pgcluster2 spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.2-0 postgresVersion: 16 instances: - dataVolumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 repos: - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>Ici on peut noter entre autres la partie spec de la configuration, qui est le morceau de yaml nous permettant de dire qu’on s’appuie sur le cluster existant pour créer un clone indépendant :</p> <pre class="brush: yaml; title: ; notranslate"> spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 </pre> <h5>9) Point in Time Recovery :</h5> <p>De la même façon, si l’on veut faire une restauration PITR, nous allons remplir la balise spec de notre yaml. Attention cependant, pour faire une restauration PITR, nous avons besoin de posséder encore la sauvegarde. On ne peut pas faire une restauration PITR sur une sauvegarde lointaine qu’on ne possèderait plus. Imaginons que je souhaite repartir d’une sauvegarde datant d’hier soir à 20h30 de mon instance pgcluster1 sur mon instance pgcluster2, la configuration serait la suivante :</p> <pre class="brush: yaml; title: ; notranslate"> apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: name: pgcluster2 spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 options: - --type=time - --target=&quot;2024-04-09 20:30:00-00&quot; image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.2-0 postgresVersion: 16 instances: - dataVolumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 repos: - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>La partie qui nous intéresse ici est la partie spec, ou nous avons rajouter un type de restauration (ici time) et une heure target. Cela indique à pgbackrest qu’il doit aller chercher tous les fichiers de sauvegarde et WAL sur notre point de sauvegarde repo1 venant de l’instance pgcluster1 pour les réappliquer sur notre nouveau cluster pgcluster2.<br /> Vous pouvez également vouloir réaliser une restauration In Place, c’est-à-dire écraser l’instance présente pour la remplacer par la restauration. Auquel cas, plutôt que de préciser comment s’appellera notre nouveau cluster, il faut alors passer par la balise restore :</p> <pre class="brush: yaml; title: ; notranslate"> spec: backups: pgbackrest: restore: enabled: true repoName: repo1 options: - --type=time - --target=&quot;2024-04-09 20:30:00-00&quot; </pre> <p>Ici, comme précédemment, nous restaurons à l’heure de 20 :30 hier soir, et cela sur notre propre instance. Ne reste plus qu’à lancer la restauration :</p> <pre class="brush: bash; title: ; notranslate">kubectl annotate -n postgres-operator postgrescluster pgcluster1 --overwrite \ postgres-operator.crunchydata.com/pgbackrest-restore=&quot;$(date)&quot;</pre> <p>A noter qu’il ne faut pas oublier de désactiver ensuite le restore en le passant à false si vous ne souhaitez pas qu’il soit de nouveau écrasé au prochain changement de configuration.</p> <h5>10) Restaurer une base de données spécifique :</h5> <p>Si votre besoin est de restaurer une base de données spécifique plutôt que l’intégralité de l’instance, vous pouvez le préciser dans les paramètres de votre restauration.<br /> Attention cependant, ce n’est pas une restauration comme le serais un pg_dump. Ici si vous restaurez simplement une seule base de données et pas le reste du cluster, les autres bases que vous n’avez pas choisit de restaurer deviendront inaccessibles.<br /> Si nous voulons restaurer une base de données, et uniquement elle, voici la procédure :</p> <pre class="brush: yaml; title: ; notranslate"> spec: backups: pgbackrest: restore: enabled: true repoName: repo1 options: - --db-include=capdata </pre> <p>Ici, on ne restaurera que la base de données capdata, et aucunes autres bases à partir de notre repo1.</p> <h3>PgBouncer :</h3> <h4>Utilité :</h4> <p>PgBouncer est un pooler de connexion pour PostgreSQL. Un pooler de connexion permet de maintenir ouvertes des sessions entre lui-même et le serveur, ce qui rend plus rapide l&#8217;ouverture de sessions depuis les clients, une application Web par exemple.<br /> PgBouncer permet aussi de mutualiser les sessions dans le serveur, économisant ainsi des ressources. PgBouncer propose plusieurs modes de partage : par requête (default), par transaction ou par session.</p> <h4>Mise en place :</h4> <p>Pour ajouter un bouncer à notre configuration c’est une réalité très simple. Il suffit d’ajouter dans notre fichier postgres.yaml la rubrique proxy :</p> <pre class="brush: yaml; title: ; notranslate"> proxy: pgBouncer: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.21-3 </pre> <p>Une fois que vous avez rajouté cela dans la configuration, il n’y a plus qu’à appliquer celle-ci :</p> <pre class="brush: bash; title: ; notranslate"> kubectl apply -k kustomize/keycloak </pre> <p>Quand PGO créé un nouveau connexion pooler sur notre instance déployée, il modifier le fichier secrets de l’utilisateur.<br /> On voit que plusieurs champs qui concerne pg_bouncer sont apparus. Ils constituent les informations qui vont vous permettre de vous connecter sur votre bouncer nouvellement créé :</p> <pre class="brush: yaml; title: ; notranslate"> { &quot;apiVersion&quot;: &quot;v1&quot;, &quot;data&quot;: { &quot;dbname&quot;: &quot;cGdjbHVzdGVyMQ==&quot;, &quot;host&quot;: &quot;cGdjbHVzdGVyMS1wcmltYXJ5LnBvc3RncmVzLW9wZXJhdG9yLnN2Yw==&quot;, &quot;jdbc-uri&quot;: &quot;amRiYzpwb3N0Z3Jlc3FsOi8vcGdjbHVzdGVyMS1wcmltYXJ5LnBvc3RncmVzLW9wZXJhdG9yLnN2Yzo1NDMyL3BnY2x1c3RlcjE/cGFzc3dvcmQ9NXNSaSUzRCU1QmZZbSUzQ2lSSGslMkElNUIlM0VuWGhqaiU3Q1EmdXNlcj1wZ2NsdXN0ZXIx&quot;, &quot;password&quot;: &quot;NXNSaT1bZlltPGlSSGsqWz5uWGhqanxR&quot;, &quot;pgbouncer-host&quot;: &quot;cGdjbHVzdGVyMS1wZ2JvdW5jZXIucG9zdGdyZXMtb3BlcmF0b3Iuc3Zj&quot;, &quot;pgbouncer-jdbc-uri&quot;: &quot;amRiYzpwb3N0Z3Jlc3FsOi8vcGdjbHVzdGVyMS1wZ2JvdW5jZXIucG9zdGdyZXMtb3BlcmF0b3Iuc3ZjOjU0MzIvcGdjbHVzdGVyMT9wYXNzd29yZD01c1JpJTNEJTVCZlltJTNDaVJIayUyQSU1QiUzRW5YaGpqJTdDUSZwcmVwYXJlVGhyZXNob2xkPTAmdXNlcj1wZ2NsdXN0ZXIx&quot;, &quot;pgbouncer-port&quot;: &quot;NTQzMg==&quot;, &quot;pgbouncer-uri&quot;: &quot;cG9zdGdyZXNxbDovL3BnY2x1c3RlcjE6NXNSaT0lNUJmWW0lM0NpUkhrJTJBJTVCJTNFblhoamolN0NRQHBnY2x1c3RlcjEtcGdib3VuY2VyLnBvc3RncmVzLW9wZXJhdG9yLnN2Yzo1NDMyL3BnY2x1c3RlcjE=&quot;, &quot;port&quot;: &quot;NTQzMg==&quot;, &quot;uri&quot;: &quot;cG9zdGdyZXNxbDovL3BnY2x1c3RlcjE6NXNSaT0lNUJmWW0lM0NpUkhrJTJBJTVCJTNFblhoamolN0NRQHBnY2x1c3RlcjEtcHJpbWFyeS5wb3N0Z3Jlcy1vcGVyYXRvci5zdmM6NTQzMi9wZ2NsdXN0ZXIx&quot;, &quot;user&quot;: &quot;cGdjbHVzdGVyMQ==&quot;, &quot;verifier&quot;: &quot;U0NSQU0tU0hBLTI1NiQ0MDk2OlgyQ3NQRU1FZjh3QkVlc05McDFJTkE9PSRKcDhKakl5Q0o1ZEpFRVhia1ptUERTNE5rR3d0V00rczdrMElsQmx0YkpvPTpEaHg3VzNCOE5vNDRYSHJ1Qm1RdENMQW9jNEtnSUZQa2dIeStUMkVWUUowPQ==&quot; }, &quot;kind&quot;: &quot;Secret&quot;, &quot;metadata&quot;: { &quot;creationTimestamp&quot;: &quot;2024-04-09T16:37:36Z&quot;, &quot;labels&quot;: { &quot;postgres-operator.crunchydata.com/cluster&quot;: &quot;pgcluster1&quot;, &quot;postgres-operator.crunchydata.com/pguser&quot;: &quot;pgcluster1&quot;, &quot;postgres-operator.crunchydata.com/role&quot;: &quot;pguser&quot; }, &quot;name&quot;: &quot;pgcluster1-pguser-pgcluster1&quot;, &quot;namespace&quot;: &quot;postgres-operator&quot;, &quot;ownerReferences&quot;: [ { &quot;apiVersion&quot;: &quot;postgres-operator.crunchydata.com/v1beta1&quot;, &quot;blockOwnerDeletion&quot;: true, &quot;controller&quot;: true, &quot;kind&quot;: &quot;PostgresCluster&quot;, &quot;name&quot;: &quot;pgcluster1&quot;, &quot;uid&quot;: &quot;7260b882-116f-4b02-b51a-18d4fe3a8038&quot; } ], &quot;resourceVersion&quot;: &quot;9495&quot;, &quot;uid&quot;: &quot;1fbdf1d2-48ea-4a45-b7d6-01248317dbee&quot; }, &quot;type&quot;: &quot;Opaque&quot; } </pre> <p>Pour se connecter à notre pgbouncer, il suffit d’utiliser les informations fournies par le fichier de secret à la place de nos infos de connexion habituelles, et cela nous permet d’accéder directement au bouncer et non plus à l’instance elle-même.</p> <p>Cette connexion peut être facilement modifiée en utilisant la documentation de pgbouncer afin de pouvoir configurer à notre guise notre pgbouncer. Un exemple de configuration qu’on pourrais rencontrer serait :</p> <pre class="brush: yaml; title: ; notranslate"> proxy: pgBouncer: image: {{.Values.image.pgBouncer }} config: global: default_pool_size: &quot;100&quot; max_client_conn: &quot;10000&quot; pool_mode: transaction </pre> <p>Pour cet exemple on voit qu’on a définit un nombre de client maximum, la taille du pool à 100 et un mode transaction pour notre pool.</p> <h3>PGO et Prometheus</h3> <h4>Utilité :</h4> <p>Prometheus est une trousse à outils de surveillance et d&#8217;alerte des systèmes en open source.<br /> Prometheus collecte et stocke ses métriques sous forme de données de séries temporelles, c&#8217;est-à-dire que les informations de métriques sont stockées avec le timestamp auquel elles ont été enregistrées, aux côtés de paires clé-valeur optionnelles appelées labels.<br /> &#8211; Un modèle de données multidimensionnel avec des données de séries temporelles identifiées par le nom de la métrique et des paires clé-valeur<br /> &#8211; PromQL, un langage de requête flexible pour exploiter cette dimensionnalité<br /> &#8211; Aucune dépendance sur le stockage distribué ; les nœuds de serveur individuels sont autonomes<br /> &#8211; La collecte de séries temporelles se fait via un modèle de tirage sur HTTP<br /> &#8211; La poussée de séries temporelles est prise en charge via une passerelle intermédiaire<br /> &#8211; Les cibles sont découvertes via la découverte de service ou la configuration statique<br /> &#8211; Prise en charge de plusieurs modes de graphiques et de tableaux de bord</p> <h4>Mise en place :</h4> <p>Pour pouvoir mettre en place une surveillance pour notre cluster, il est plus simple de télécharger et compléter le modèle fournit dans les exemples de pgo.<br /> Ainsi, on peut récupérer les exemples à l’aide de git :</p> <pre class="brush: bash; title: ; notranslate"> YOUR_GITHUB_UN=&quot;$YOUR_GITHUB_USERNAME&quot; git clone --depth 1 &quot;git@github.com:${YOUR_GITHUB_UN}/postgres-operator-examples.git&quot; cd postgres-operator-examples </pre> <p>Les différentes configurations se trouvent dans le dossier kustomize/monitoring.<br /> Pour activer le monitoring de notre instance, il faut ajouter la balise monitoring à notre fichier postgres.yaml :</p> <pre class="brush: yaml; title: ; notranslate"> monitoring: pgmonitor: exporter: image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-5.5.1-0 </pre> <p>Une fois notre configuration modifiée, on l’applique afin que PGO détecte les changements et configure tout seul l’exporter pour qu’il puisse se connecter à nos bases de données et récupérer les métriques.</p> <pre class="brush: bash; title: ; notranslate"> kubectl apply -k kustomize/postgres </pre> <p>Il faut ensuite appliquer la configuration de base de pgmonitor pour qu’il créé lui-même les fichiers de configuration pour prometheus (il le fera en même temps pour Grafana et Alertmanager qui sont deux autres outils de surveillance). Pour cela on applique le kustomize présent dans le dossier monitoring :</p> <pre class="brush: bash; title: ; notranslate"> $kubectl apply -k kustomize\postgres postgrescluster.postgres-operator.crunchydata.com/pgcluster1 configured $kubectl apply -k kustomize\monitoring serviceaccount/alertmanager created serviceaccount/grafana created serviceaccount/prometheus created clusterrole.rbac.authorization.k8s.io/prometheus created clusterrolebinding.rbac.authorization.k8s.io/prometheus created configmap/alert-rules-config created configmap/alertmanager-config created configmap/crunchy-prometheus created configmap/grafana-dashboards created configmap/grafana-datasources created secret/grafana-admin created service/crunchy-alertmanager created service/crunchy-grafana created service/crunchy-prometheus created persistentvolumeclaim/alertmanagerdata created persistentvolumeclaim/grafanadata created persistentvolumeclaim/prometheusdata created deployment.apps/crunchy-alertmanager created deployment.apps/crunchy-grafana created deployment.apps/crunchy-prometheus created </pre> <p>Nos services ont été correctement déployés, il ne nous reste plus qu’à utiliser celui qui nous intéresse, ici service/crunchy-prometheus et lui indiquer de commencer à envoyer les informations sur notre prometheus :</p> <pre class="brush: bash; title: ; notranslate"> $kubectl -n postgres-operator port-forward service/crunchy-prometheus 9090:9090 Forwarding from 127.0.0.1:9090 -&gt; 9090 Forwarding from [::1]:9090 -&gt; 9090 Handling connection for 9090 Handling connection for 9090 </pre> <p>Afin d’accéder à notre service prometheus, il ne nous reste plus qu’à se connecter avec l’adresse de notre machine, sur le port 9090 préalablement ouvert, pour voir apparaitre le dashboard de prometheus :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2.jpg"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10567" src="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-300x66.jpg" alt="" width="300" height="66" srcset="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-300x66.jpg 300w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-1024x226.jpg 1024w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-768x170.jpg 768w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2.jpg 1386w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p> <h3>PGO Client :</h3> <h4>Utilité :</h4> <p>Pour pouvoir gérer plus facilement le cluster créé par PGO, CrunchyData à développé une surcouche à kubectl qui permet de faciliter les commandes que nous pouvons réaliser sur le cluster.<br /> Cela permet de ne pas avoir à taper les longues lignes de commandes qui permettent par exemple de démarrer les sauvegardes unitaires.</p> <h4>Mise en place :</h4> <p>Pour pouvoir installer cette surcouche, il faut télécharger la version qui correspond au système d’exploitation à partir du GIT de pgo client :</p> <pre class="brush: bash; title: ; notranslate"> # wget https://github.com/CrunchyData/postgres-operator-client/releases/download/v0.4.1/kubectl-pgo-linux-arm64 --2024-04-11 12:07:45-- https://github.com/CrunchyData/postgres-operator-client/releases/download/v0.4.1/kubectl-pgo-linux-arm64 Resolving github.com (github.com)... 140.82.121.4 Connecting to github.com (github.com)|140.82.121.4|:443... connected. HTTP request sent, awaiting response... 302 Found Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.110.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 47895849 (46M) [application/octet-stream] Saving to: ‘kubectl-pgo-linux-arm64’ kubectl-pgo-linux-arm64 100%[========================================================================================================================================&gt;] 45.68M --.-KB/s in 0.1s 2024-04-11 12:07:45 (373 MB/s) - ‘kubectl-pgo-linux-arm64’ saved [47895849/47895849] </pre> <p>On renome le fichier téléchargé en kubectl-pgo et on le déplace dans nos bin pour pouvoir les utiliser :</p> <pre class="brush: bash; title: ; notranslate"> # mv kubectl-pgo-linux-arm64 kubectl-pgo # sudo mv kubectl-pgo /usr/local/bin/kubectl-pgo # sudo chmod +x /usr/local/bin/kubectl-pgo Une fois que ces actions sont réalisées, on peut tester le fonctionnement : # kubectl pgo version Client Version: v0.4.1 Operator Version: v5.5.1 </pre> <p>Les commandes disponibles avec cette extension sont les suivantes :<br /> &#8211; backup : Backup cluster<br /> &#8211; create : Create a resource<br /> &#8211; delete : Delete a resource<br /> &#8211; help : Help about any command<br /> &#8211; restore : Restore cluster<br /> &#8211; show Show : PostgresCluster details<br /> &#8211; start : Start cluster<br /> &#8211; stop : Stop cluster<br /> &#8211; support : Crunchy Support commands for PGO<br /> &#8211; version : PGO client<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pgo-operateurs-kubernetes-pour-postgresql-la-suite/" rel="bookmark" title="6 juin 2023">PGO : opérateurs kubernetes pour PostgreSQL, la suite !</a> (David Baffaleuf) [ContainerDevopsPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/kubegres-loperateur-kubernetes-cle-en-main-pour-postgresql/" rel="bookmark" title="26 avril 2023">Kubegres : l&#8217;opérateur Kubernetes clé en main pour PostgreSQL</a> (David Baffaleuf) [ContainerDevopsPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-sur-la-solution-kubernetes-locale-minikube/" rel="bookmark" title="29 mars 2023">PostgreSQL sur la solution Kubernetes locale Minikube</a> (Emmanuel RAMI) [ContainerPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-comparatif-entre-barman-et-pgbackrest/" rel="bookmark" title="4 février 2020">PostgreSQL : Comparatif entre Barman et pgBackRest</a> (Capdata team) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/sauvegardes-sql-server-dans-un-azure-blob-storage/" rel="bookmark" title="21 août 2018">Sauvegardes SQL Server dans un Azure Blob Storage</a> (Capdata team) [AzureSQL Server]</li> </ul> <p><!-- Similar Posts took 3.982 ms --></p> <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%2F%3Fp%3D10562&#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%2F%3Fp%3D10562&#038;title=PGO%20%3A%20la%20suite" 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=PGO%20%3A%20la%20suite&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10562" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pgo-la-suite/">PGO : la suite</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10562&#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%2F%3Fp%3D10562&#038;title=PGO%20%3A%20la%20suite" 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=PGO%20%3A%20la%20suite&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10562" 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>La gestion efficace des clusters PostgreSQL dans un environnement Kubernetes est un défi complexe auquel sont confrontées de nombreuses entreprises aujourd&#8217;hui. PGO offre une solution déclarative qui automatise la gestion des clusters PostgreSQL, simplifiant ainsi le déploiement, la mise à l&#8217;échelle et la gestion des bases de données PostgreSQL dans un environnement Kubernetes.</p> <p>Pour faire suite à l&#8217;article de David sur PGO et à la demande d&#8217;un de nos clients, j&#8217;ai réalisé une étude approfondie de plusieurs fonctionnalités de PGO.<br /> Cet article va faire un petit tour d&#8217;horizon des outils principaux inclus dans l&#8217;implémentation de PGO. Que ce soit pour la sauvegarde avec pgbackrest, pour la balance des connexion avec pgbouncer ou pour le monitoring avec prometheus, PGO ne manque pas d&#8217;utilitaire dont l&#8217;utilisation est facilitée par la solution tout embarqué.</p> <h3>Pgbackrest :</h3> <h4>Utilité :</h4> <p>PgBackRest est une solution de sauvegarde et de restauration pour les bases de données PostgreSQL qui propose plusieurs fonctionnalités, telles que la sauvegarde et la restauration parallèles, la compression, les sauvegardes complètes, différentielles et incrémentielles, la rotation des sauvegardes et l&#8217;expiration des archives, l&#8217;intégrité des sauvegardes, etc. Il prend en charge plusieurs référentiels, qui peuvent être situés localement ou à distance via TLS/SSH, ou être des stockages fournis par le cloud comme S3/GCS/Azure.<br /> L&#8217;architecture de pgbackrest pour PGO est la suivante :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10564" src="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1-300x168.png" alt="" width="300" height="168" srcset="https://blog.capdata.fr/wp-content/uploads/2024/05/Image1-300x168.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image1.png 605w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p> <h4>Mise en place :</h4> <p>On peut imaginer plusieurs moyens de mettre en place le pgbackrest. Dans un premier temps, nous avons la sauvegarde classique en système de fichier, comme dans notre exemple sur le blog :</p> <h5>1) La sauvegarde sur volume persistant Kubernetes :</h5> <pre class="brush: yaml; title: ; notranslate"> - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>Ce type de sauvegarde utilise un volume persistant de Kubernetes pour recueillir nos sauvegardes et les garder.<br /> Une PersistentVolumeClaim (PVC) est une demande de stockage faite par un utilisateur. Elle est similaire à un Pod. Les Pods consomment des ressources de nœud et les PVC consomment des ressources de PV (PersistentVolume). Les Pods peuvent demander des niveaux spécifiques de ressources (CPU et mémoire). Les revendications peuvent demander une taille spécifique et des modes d&#8217;accès spécifiques (par exemple, elles peuvent être montées en ReadWriteOnce, ReadOnlyMany, ReadWriteMany, ou ReadWriteOncePod, voir AccessModes).</p> <h5>2) Le stockage pour S3 :</h5> <p>Pour pouvoir faire du stockage dans S3, il faut rajouter un fichier de configuration dans notre dossier de déploiement. Le fichier doit s’appeler s3.conf. Ce fichier contient les crédential de connexion à un AWS S3 :</p> <pre class="brush: bash; title: ; notranslate"> repo1-s3-key=$YOUR_AWS_S3_KEY repo1-s3-key-secret=$YOUR_AWS_S3_KEY_SECRET </pre> <p>Une fois que c’est configuré dans votre fichier, il ne reste plus qu’à modifier le postgresql.yaml, et configurer dans la partie backup :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-s3-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster1/repo1 repos: - name: repo1 s3: bucket: &quot;&lt;YOUR_AWS_S3_BUCKET_NAME&gt;&quot; endpoint: &quot;&lt;YOUR_AWS_S3_ENDPOINT&gt;&quot; region: &quot;&lt;YOUR_AWS_S3_REGION&gt;&quot; </pre> <p>Une fois configuré, et le job mis dans le cron, vous devriez voir apparaitre les sauvegardes sur le volume S3.</p> <h5>3) Le stockage GCS :</h5> <p>Comme pour Amazon S3 on peut sauvegarder nos backups dans Google Cloud Storage. Pour pouvoir le faire fonctionner il vous faut copier votre GCS key secret (qui est un fichier JSON) dans un gcs.conf que vous allez placer dans votre dossier Kustomize.<br /> Il vous suffit ensuite de modifier votre fichier postgres.yaml pour ajouter dans la partie backup la configuration pour une sauvegarde gcs :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-gcs-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster1/repo1 repos: - name: repo1 gcs: bucket: &quot;&lt;YOUR_GCS_BUCKET_NAME&gt;&quot; </pre> <p>Il ne vous reste plus qu’à regénérer vos pods, et votre sauvegarde arrivera directement dans votre Google Cloud Service.</p> <h5>4) Le stockage Azur Blob Storage :</h5> <p>Comme pour les deux points précédents, vous pouvez également stocker vos sauvegardes sur le blob storage d’Azure. Pour cela il vous faut créer un fichier dans votre kustomize, avec à l’intérieur la configuration pour votre point de sauvegarde Azure. Il vous faut l’appeler azure.conf et il devra contenir les lignes suivantes :</p> <pre class="brush: bash; title: ; notranslate"> repo1-azure-account=$YOUR_AZURE_ACCOUNT repo1-azure-key=$YOUR_AZURE_KEY </pre> <p>Il faut ensuite intégrer ces modifications dans votre fichier postgres.yaml :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 configuration: - secret: name: pgo-azure-creds global: repo1-path: /pgbackrest/postgres-operator/pgcluster/repo1 repos: - name: repo1 azure: container: &quot;&lt;YOUR_AZURE_CONTAINER&gt;&quot; </pre> <p>Bien sur rien ne vous interdit, et c’est même conseillé, de joindre plusieurs moyens de sauvegarde. Cela permet notamment de s’assurer une plus grande fiabilité du système de sauvegarde, en s’assurant qu’elles sont disponibles à plusieurs endroits.<br /> Une fois que vous avez décidé d’où vous allez stocker vos sauvegardes, et que vous l’avez configuré, il faut maintenant décider des différents paramètres de ces sauvegardes : la programmation, la rétention…</p> <h5>5) La programmation des sauvegardes :</h5> <p>Il faut savoir que par défaut, PGO sauvegarde automatiquement les WAL dans la méthode de sauvegarde que vous lui avez configuré. C’est donc une forme de sauvegarde en soit.<br /> Mais dans le cadre d’une récupération après incident majeur, il peut aussi être utilise d’avoir des sauvegardes full programmées. Pgbackrest, qui est l’outil utilisé par PGO permet de mettre en place trois types de sauvegarde : les incrémentales, les différentielles et les fulls.<br /> Chaque type de sauvegarde peut être programmée en suivant une notation identique à celle des crontab. Par exemple :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: repos: - name: repo1 schedules: full: &quot;0 1 * * 0&quot; differential: &quot;0 1 * * 1-6&quot; </pre> <p>Le fait d’implémenter ces planifications créera des CronJobs dans Kubernetes.</p> <h5>6) La rétention des backups :</h5> <p>Vous pouvez définir une rétention maximum pour vos backups sur le support de backup de votre choix. Une fois que cette rétention sera atteinte, pgbackrest fera le ménage tout seul des sauvegardes et des WAL qui lui sont reliées.<br /> Il y a deux types de rétentions que l’on peut définir : les rétentions « count » basées sur le nombre de backup que l’on souhaite garder et les rétentions « time » basées sur le nombre de jours ou vous souhaitez garder votre sauvegarde.</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: global: repo1-retention-full: &quot;14&quot; repo1-retention-full-type: time </pre> <h5>7) La sauvegarde unique :</h5> <p>Si dans le cadre d’un besoin particuliers, une grosse modification ou une migration par exemple, vous avez besoin de prendre une sauvegarde immédiate sans forcément attendre que le cron n’arrive, vous pouvez le faire.<br /> Pour la configuration de cette sauvegarde, il faudra l’annoter comme « manuelle » :</p> <pre class="brush: yaml; title: ; notranslate"> backups: pgbackrest: manual: repoName: repo1 options: - --type=full </pre> <p>Il vous faudra ensuite déclencher cette sauvegarde avec une commande manuelle. Dans le cadre de notre cluster exemple pgcluster1 :<br /> kubectl annotate -n postgres-operator postgrescluster pgcluster1 \ postgres-operator.crunchydata.com/pgbackrest-backup=&#8221;$(date)&#8221;</p> <h5>8) Faire un clone à partir d’un repo :</h5> <p>Quand on a configuré un repo sur notre instance primaire, on peut facilement créer un clone de notre instance à l’aide de notre sauvegarde. Ainsi, on créer un tout nouveau Pods à partir des informations stockées à propos du pod que l’on possède déjà. Ici, nous allons créer un nouveau pod à partir de notre pod pgcluster1 :</p> <pre class="brush: yaml; title: ; notranslate"> apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: name: pgcluster2 spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.2-0 postgresVersion: 16 instances: - dataVolumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 repos: - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>Ici on peut noter entre autres la partie spec de la configuration, qui est le morceau de yaml nous permettant de dire qu’on s’appuie sur le cluster existant pour créer un clone indépendant :</p> <pre class="brush: yaml; title: ; notranslate"> spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 </pre> <h5>9) Point in Time Recovery :</h5> <p>De la même façon, si l’on veut faire une restauration PITR, nous allons remplir la balise spec de notre yaml. Attention cependant, pour faire une restauration PITR, nous avons besoin de posséder encore la sauvegarde. On ne peut pas faire une restauration PITR sur une sauvegarde lointaine qu’on ne possèderait plus. Imaginons que je souhaite repartir d’une sauvegarde datant d’hier soir à 20h30 de mon instance pgcluster1 sur mon instance pgcluster2, la configuration serait la suivante :</p> <pre class="brush: yaml; title: ; notranslate"> apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: name: pgcluster2 spec: dataSource: postgresCluster: clusterName: pgcluster1 repoName: repo1 options: - --type=time - --target=&quot;2024-04-09 20:30:00-00&quot; image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.2-0 postgresVersion: 16 instances: - dataVolumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.49-0 repos: - name: repo1 volume: volumeClaimSpec: accessModes: - &quot;ReadWriteOnce&quot; resources: requests: storage: 1Gi </pre> <p>La partie qui nous intéresse ici est la partie spec, ou nous avons rajouter un type de restauration (ici time) et une heure target. Cela indique à pgbackrest qu’il doit aller chercher tous les fichiers de sauvegarde et WAL sur notre point de sauvegarde repo1 venant de l’instance pgcluster1 pour les réappliquer sur notre nouveau cluster pgcluster2.<br /> Vous pouvez également vouloir réaliser une restauration In Place, c’est-à-dire écraser l’instance présente pour la remplacer par la restauration. Auquel cas, plutôt que de préciser comment s’appellera notre nouveau cluster, il faut alors passer par la balise restore :</p> <pre class="brush: yaml; title: ; notranslate"> spec: backups: pgbackrest: restore: enabled: true repoName: repo1 options: - --type=time - --target=&quot;2024-04-09 20:30:00-00&quot; </pre> <p>Ici, comme précédemment, nous restaurons à l’heure de 20 :30 hier soir, et cela sur notre propre instance. Ne reste plus qu’à lancer la restauration :</p> <pre class="brush: bash; title: ; notranslate">kubectl annotate -n postgres-operator postgrescluster pgcluster1 --overwrite \ postgres-operator.crunchydata.com/pgbackrest-restore=&quot;$(date)&quot;</pre> <p>A noter qu’il ne faut pas oublier de désactiver ensuite le restore en le passant à false si vous ne souhaitez pas qu’il soit de nouveau écrasé au prochain changement de configuration.</p> <h5>10) Restaurer une base de données spécifique :</h5> <p>Si votre besoin est de restaurer une base de données spécifique plutôt que l’intégralité de l’instance, vous pouvez le préciser dans les paramètres de votre restauration.<br /> Attention cependant, ce n’est pas une restauration comme le serais un pg_dump. Ici si vous restaurez simplement une seule base de données et pas le reste du cluster, les autres bases que vous n’avez pas choisit de restaurer deviendront inaccessibles.<br /> Si nous voulons restaurer une base de données, et uniquement elle, voici la procédure :</p> <pre class="brush: yaml; title: ; notranslate"> spec: backups: pgbackrest: restore: enabled: true repoName: repo1 options: - --db-include=capdata </pre> <p>Ici, on ne restaurera que la base de données capdata, et aucunes autres bases à partir de notre repo1.</p> <h3>PgBouncer :</h3> <h4>Utilité :</h4> <p>PgBouncer est un pooler de connexion pour PostgreSQL. Un pooler de connexion permet de maintenir ouvertes des sessions entre lui-même et le serveur, ce qui rend plus rapide l&#8217;ouverture de sessions depuis les clients, une application Web par exemple.<br /> PgBouncer permet aussi de mutualiser les sessions dans le serveur, économisant ainsi des ressources. PgBouncer propose plusieurs modes de partage : par requête (default), par transaction ou par session.</p> <h4>Mise en place :</h4> <p>Pour ajouter un bouncer à notre configuration c’est une réalité très simple. Il suffit d’ajouter dans notre fichier postgres.yaml la rubrique proxy :</p> <pre class="brush: yaml; title: ; notranslate"> proxy: pgBouncer: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.21-3 </pre> <p>Une fois que vous avez rajouté cela dans la configuration, il n’y a plus qu’à appliquer celle-ci :</p> <pre class="brush: bash; title: ; notranslate"> kubectl apply -k kustomize/keycloak </pre> <p>Quand PGO créé un nouveau connexion pooler sur notre instance déployée, il modifier le fichier secrets de l’utilisateur.<br /> On voit que plusieurs champs qui concerne pg_bouncer sont apparus. Ils constituent les informations qui vont vous permettre de vous connecter sur votre bouncer nouvellement créé :</p> <pre class="brush: yaml; title: ; notranslate"> { &quot;apiVersion&quot;: &quot;v1&quot;, &quot;data&quot;: { &quot;dbname&quot;: &quot;cGdjbHVzdGVyMQ==&quot;, &quot;host&quot;: &quot;cGdjbHVzdGVyMS1wcmltYXJ5LnBvc3RncmVzLW9wZXJhdG9yLnN2Yw==&quot;, &quot;jdbc-uri&quot;: &quot;amRiYzpwb3N0Z3Jlc3FsOi8vcGdjbHVzdGVyMS1wcmltYXJ5LnBvc3RncmVzLW9wZXJhdG9yLnN2Yzo1NDMyL3BnY2x1c3RlcjE/cGFzc3dvcmQ9NXNSaSUzRCU1QmZZbSUzQ2lSSGslMkElNUIlM0VuWGhqaiU3Q1EmdXNlcj1wZ2NsdXN0ZXIx&quot;, &quot;password&quot;: &quot;NXNSaT1bZlltPGlSSGsqWz5uWGhqanxR&quot;, &quot;pgbouncer-host&quot;: &quot;cGdjbHVzdGVyMS1wZ2JvdW5jZXIucG9zdGdyZXMtb3BlcmF0b3Iuc3Zj&quot;, &quot;pgbouncer-jdbc-uri&quot;: &quot;amRiYzpwb3N0Z3Jlc3FsOi8vcGdjbHVzdGVyMS1wZ2JvdW5jZXIucG9zdGdyZXMtb3BlcmF0b3Iuc3ZjOjU0MzIvcGdjbHVzdGVyMT9wYXNzd29yZD01c1JpJTNEJTVCZlltJTNDaVJIayUyQSU1QiUzRW5YaGpqJTdDUSZwcmVwYXJlVGhyZXNob2xkPTAmdXNlcj1wZ2NsdXN0ZXIx&quot;, &quot;pgbouncer-port&quot;: &quot;NTQzMg==&quot;, &quot;pgbouncer-uri&quot;: &quot;cG9zdGdyZXNxbDovL3BnY2x1c3RlcjE6NXNSaT0lNUJmWW0lM0NpUkhrJTJBJTVCJTNFblhoamolN0NRQHBnY2x1c3RlcjEtcGdib3VuY2VyLnBvc3RncmVzLW9wZXJhdG9yLnN2Yzo1NDMyL3BnY2x1c3RlcjE=&quot;, &quot;port&quot;: &quot;NTQzMg==&quot;, &quot;uri&quot;: &quot;cG9zdGdyZXNxbDovL3BnY2x1c3RlcjE6NXNSaT0lNUJmWW0lM0NpUkhrJTJBJTVCJTNFblhoamolN0NRQHBnY2x1c3RlcjEtcHJpbWFyeS5wb3N0Z3Jlcy1vcGVyYXRvci5zdmM6NTQzMi9wZ2NsdXN0ZXIx&quot;, &quot;user&quot;: &quot;cGdjbHVzdGVyMQ==&quot;, &quot;verifier&quot;: &quot;U0NSQU0tU0hBLTI1NiQ0MDk2OlgyQ3NQRU1FZjh3QkVlc05McDFJTkE9PSRKcDhKakl5Q0o1ZEpFRVhia1ptUERTNE5rR3d0V00rczdrMElsQmx0YkpvPTpEaHg3VzNCOE5vNDRYSHJ1Qm1RdENMQW9jNEtnSUZQa2dIeStUMkVWUUowPQ==&quot; }, &quot;kind&quot;: &quot;Secret&quot;, &quot;metadata&quot;: { &quot;creationTimestamp&quot;: &quot;2024-04-09T16:37:36Z&quot;, &quot;labels&quot;: { &quot;postgres-operator.crunchydata.com/cluster&quot;: &quot;pgcluster1&quot;, &quot;postgres-operator.crunchydata.com/pguser&quot;: &quot;pgcluster1&quot;, &quot;postgres-operator.crunchydata.com/role&quot;: &quot;pguser&quot; }, &quot;name&quot;: &quot;pgcluster1-pguser-pgcluster1&quot;, &quot;namespace&quot;: &quot;postgres-operator&quot;, &quot;ownerReferences&quot;: [ { &quot;apiVersion&quot;: &quot;postgres-operator.crunchydata.com/v1beta1&quot;, &quot;blockOwnerDeletion&quot;: true, &quot;controller&quot;: true, &quot;kind&quot;: &quot;PostgresCluster&quot;, &quot;name&quot;: &quot;pgcluster1&quot;, &quot;uid&quot;: &quot;7260b882-116f-4b02-b51a-18d4fe3a8038&quot; } ], &quot;resourceVersion&quot;: &quot;9495&quot;, &quot;uid&quot;: &quot;1fbdf1d2-48ea-4a45-b7d6-01248317dbee&quot; }, &quot;type&quot;: &quot;Opaque&quot; } </pre> <p>Pour se connecter à notre pgbouncer, il suffit d’utiliser les informations fournies par le fichier de secret à la place de nos infos de connexion habituelles, et cela nous permet d’accéder directement au bouncer et non plus à l’instance elle-même.</p> <p>Cette connexion peut être facilement modifiée en utilisant la documentation de pgbouncer afin de pouvoir configurer à notre guise notre pgbouncer. Un exemple de configuration qu’on pourrais rencontrer serait :</p> <pre class="brush: yaml; title: ; notranslate"> proxy: pgBouncer: image: {{.Values.image.pgBouncer }} config: global: default_pool_size: &quot;100&quot; max_client_conn: &quot;10000&quot; pool_mode: transaction </pre> <p>Pour cet exemple on voit qu’on a définit un nombre de client maximum, la taille du pool à 100 et un mode transaction pour notre pool.</p> <h3>PGO et Prometheus</h3> <h4>Utilité :</h4> <p>Prometheus est une trousse à outils de surveillance et d&#8217;alerte des systèmes en open source.<br /> Prometheus collecte et stocke ses métriques sous forme de données de séries temporelles, c&#8217;est-à-dire que les informations de métriques sont stockées avec le timestamp auquel elles ont été enregistrées, aux côtés de paires clé-valeur optionnelles appelées labels.<br /> &#8211; Un modèle de données multidimensionnel avec des données de séries temporelles identifiées par le nom de la métrique et des paires clé-valeur<br /> &#8211; PromQL, un langage de requête flexible pour exploiter cette dimensionnalité<br /> &#8211; Aucune dépendance sur le stockage distribué ; les nœuds de serveur individuels sont autonomes<br /> &#8211; La collecte de séries temporelles se fait via un modèle de tirage sur HTTP<br /> &#8211; La poussée de séries temporelles est prise en charge via une passerelle intermédiaire<br /> &#8211; Les cibles sont découvertes via la découverte de service ou la configuration statique<br /> &#8211; Prise en charge de plusieurs modes de graphiques et de tableaux de bord</p> <h4>Mise en place :</h4> <p>Pour pouvoir mettre en place une surveillance pour notre cluster, il est plus simple de télécharger et compléter le modèle fournit dans les exemples de pgo.<br /> Ainsi, on peut récupérer les exemples à l’aide de git :</p> <pre class="brush: bash; title: ; notranslate"> YOUR_GITHUB_UN=&quot;$YOUR_GITHUB_USERNAME&quot; git clone --depth 1 &quot;git@github.com:${YOUR_GITHUB_UN}/postgres-operator-examples.git&quot; cd postgres-operator-examples </pre> <p>Les différentes configurations se trouvent dans le dossier kustomize/monitoring.<br /> Pour activer le monitoring de notre instance, il faut ajouter la balise monitoring à notre fichier postgres.yaml :</p> <pre class="brush: yaml; title: ; notranslate"> monitoring: pgmonitor: exporter: image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-5.5.1-0 </pre> <p>Une fois notre configuration modifiée, on l’applique afin que PGO détecte les changements et configure tout seul l’exporter pour qu’il puisse se connecter à nos bases de données et récupérer les métriques.</p> <pre class="brush: bash; title: ; notranslate"> kubectl apply -k kustomize/postgres </pre> <p>Il faut ensuite appliquer la configuration de base de pgmonitor pour qu’il créé lui-même les fichiers de configuration pour prometheus (il le fera en même temps pour Grafana et Alertmanager qui sont deux autres outils de surveillance). Pour cela on applique le kustomize présent dans le dossier monitoring :</p> <pre class="brush: bash; title: ; notranslate"> $kubectl apply -k kustomize\postgres postgrescluster.postgres-operator.crunchydata.com/pgcluster1 configured $kubectl apply -k kustomize\monitoring serviceaccount/alertmanager created serviceaccount/grafana created serviceaccount/prometheus created clusterrole.rbac.authorization.k8s.io/prometheus created clusterrolebinding.rbac.authorization.k8s.io/prometheus created configmap/alert-rules-config created configmap/alertmanager-config created configmap/crunchy-prometheus created configmap/grafana-dashboards created configmap/grafana-datasources created secret/grafana-admin created service/crunchy-alertmanager created service/crunchy-grafana created service/crunchy-prometheus created persistentvolumeclaim/alertmanagerdata created persistentvolumeclaim/grafanadata created persistentvolumeclaim/prometheusdata created deployment.apps/crunchy-alertmanager created deployment.apps/crunchy-grafana created deployment.apps/crunchy-prometheus created </pre> <p>Nos services ont été correctement déployés, il ne nous reste plus qu’à utiliser celui qui nous intéresse, ici service/crunchy-prometheus et lui indiquer de commencer à envoyer les informations sur notre prometheus :</p> <pre class="brush: bash; title: ; notranslate"> $kubectl -n postgres-operator port-forward service/crunchy-prometheus 9090:9090 Forwarding from 127.0.0.1:9090 -&gt; 9090 Forwarding from [::1]:9090 -&gt; 9090 Handling connection for 9090 Handling connection for 9090 </pre> <p>Afin d’accéder à notre service prometheus, il ne nous reste plus qu’à se connecter avec l’adresse de notre machine, sur le port 9090 préalablement ouvert, pour voir apparaitre le dashboard de prometheus :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2.jpg"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10567" src="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-300x66.jpg" alt="" width="300" height="66" srcset="https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-300x66.jpg 300w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-1024x226.jpg 1024w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2-768x170.jpg 768w, https://blog.capdata.fr/wp-content/uploads/2024/05/Image2.jpg 1386w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p> <h3>PGO Client :</h3> <h4>Utilité :</h4> <p>Pour pouvoir gérer plus facilement le cluster créé par PGO, CrunchyData à développé une surcouche à kubectl qui permet de faciliter les commandes que nous pouvons réaliser sur le cluster.<br /> Cela permet de ne pas avoir à taper les longues lignes de commandes qui permettent par exemple de démarrer les sauvegardes unitaires.</p> <h4>Mise en place :</h4> <p>Pour pouvoir installer cette surcouche, il faut télécharger la version qui correspond au système d’exploitation à partir du GIT de pgo client :</p> <pre class="brush: bash; title: ; notranslate"> # wget https://github.com/CrunchyData/postgres-operator-client/releases/download/v0.4.1/kubectl-pgo-linux-arm64 --2024-04-11 12:07:45-- https://github.com/CrunchyData/postgres-operator-client/releases/download/v0.4.1/kubectl-pgo-linux-arm64 Resolving github.com (github.com)... 140.82.121.4 Connecting to github.com (github.com)|140.82.121.4|:443... connected. HTTP request sent, awaiting response... 302 Found Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.110.133, ... Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 47895849 (46M) [application/octet-stream] Saving to: ‘kubectl-pgo-linux-arm64’ kubectl-pgo-linux-arm64 100%[========================================================================================================================================&gt;] 45.68M --.-KB/s in 0.1s 2024-04-11 12:07:45 (373 MB/s) - ‘kubectl-pgo-linux-arm64’ saved [47895849/47895849] </pre> <p>On renome le fichier téléchargé en kubectl-pgo et on le déplace dans nos bin pour pouvoir les utiliser :</p> <pre class="brush: bash; title: ; notranslate"> # mv kubectl-pgo-linux-arm64 kubectl-pgo # sudo mv kubectl-pgo /usr/local/bin/kubectl-pgo # sudo chmod +x /usr/local/bin/kubectl-pgo Une fois que ces actions sont réalisées, on peut tester le fonctionnement : # kubectl pgo version Client Version: v0.4.1 Operator Version: v5.5.1 </pre> <p>Les commandes disponibles avec cette extension sont les suivantes :<br /> &#8211; backup : Backup cluster<br /> &#8211; create : Create a resource<br /> &#8211; delete : Delete a resource<br /> &#8211; help : Help about any command<br /> &#8211; restore : Restore cluster<br /> &#8211; show Show : PostgresCluster details<br /> &#8211; start : Start cluster<br /> &#8211; stop : Stop cluster<br /> &#8211; support : Crunchy Support commands for PGO<br /> &#8211; version : PGO client<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pgo-operateurs-kubernetes-pour-postgresql-la-suite/" rel="bookmark" title="6 juin 2023">PGO : opérateurs kubernetes pour PostgreSQL, la suite !</a> (David Baffaleuf) [ContainerDevopsPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/kubegres-loperateur-kubernetes-cle-en-main-pour-postgresql/" rel="bookmark" title="26 avril 2023">Kubegres : l&#8217;opérateur Kubernetes clé en main pour PostgreSQL</a> (David Baffaleuf) [ContainerDevopsPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-sur-la-solution-kubernetes-locale-minikube/" rel="bookmark" title="29 mars 2023">PostgreSQL sur la solution Kubernetes locale Minikube</a> (Emmanuel RAMI) [ContainerPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-comparatif-entre-barman-et-pgbackrest/" rel="bookmark" title="4 février 2020">PostgreSQL : Comparatif entre Barman et pgBackRest</a> (Capdata team) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/sauvegardes-sql-server-dans-un-azure-blob-storage/" rel="bookmark" title="21 août 2018">Sauvegardes SQL Server dans un Azure Blob Storage</a> (Capdata team) [AzureSQL Server]</li> </ul> <p><!-- Similar Posts took 2.260 ms --></p> <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%2F%3Fp%3D10562&#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%2F%3Fp%3D10562&#038;title=PGO%20%3A%20la%20suite" 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=PGO%20%3A%20la%20suite&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10562" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pgo-la-suite/">PGO : la suite</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>La gestion efficace des clusters PostgreSQL dans un environnement Kubernetes est un défi complexe auquel sont confrontées de nombreuses entreprises aujourd&#8217;hui. PGO offre une solution déclarative qui automatise la gestion des clusters PostgreSQL, simplifiant ainsi le déploiement, la mise à&#8230; <a href="https://blog.capdata.fr/index.php/pgo-la-suite/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pgo-la-suite/">PGO : la suite</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> @@ -264,7 +264,7 @@ pg_recursively_delete : Simplifier les suppressions récursives 2024-04-03T13:11:08Z tag:blog.capdata.fr,2024-04-03:/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/ - <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%2F%3Fp%3D10505&#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%2F%3Fp%3D10505&#038;title=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives" 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=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10505" 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>Si vous avez été amené au fil de votre carrière à manipuler de gros volumes de données contenus dans plusieurs tables possédant des références croisées entre elles, dépendantes d&#8217;autres tables, qui elles-mêmes dépendent d&#8217;autres tables, vous savez à quel point il peut être compliqué de remonter l&#8217;intégralité de l&#8217;arbre de dépendance pour supprimer la moindre ligne. Cela peut être long et fastidieux.</p> <p>Vous ne savez pas vraiment ce que vous supprimez, dans quelles tables, et quels impacts cela peut avoir sur votre base de données. Si les dépendances sont nombreuses, il est d&#8217;autant plus compliqué de tout retracer et d&#8217;être sûr à 100 % de ce que votre DELETE va entraîner.</p> <p>Dans cet article, je vais vous présenter rapidement un petit outil sous la forme d&#8217;une extension que je trouve pratique à utiliser dans ce cas de figure. L&#8217;outil s&#8217;appelle pg_recursively_delete, et il permet de tracer avant d&#8217;exécuter l&#8217;ordre de suppression de votre ligne, et d&#8217;avoir une arborescence des différentes données que vous allez impacter.</p> <h2>Installation d&#8217;un moteur et de l&#8217;extension :</h2> <p>Pour cet article, j&#8217;ai choisi d&#8217;utiliser PostgreSQL en version 16 pour tester si l&#8217;extension fonctionnait toujours.</p> <pre class="brush: bash; title: ; notranslate">root:~#sudo apt update &amp;amp;&amp;amp; sudo apt upgrade root:~#sudo sh -c 'echo &quot;deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main&quot; &amp;gt; /etc/apt/sources.list.d/pgdg.list' root:~#wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - root:~#sudo apt -y update root:~#sudo apt -y install postgresql-16</pre> <p>Notre moteur de base de données est installé, à présent il nous faut télécharger les sources de l&#8217;extension, et l&#8217;installer.</p> <pre class="brush: bash; title: ; notranslate">root:~# git clone https://github.com/trlorenz/PG-recursively_delete.git Cloning into 'PG-recursively_delete'... remote: Enumerating objects: 155, done. remote: Counting objects: 100% (95/95), done. remote: Compressing objects: 100% (62/62), done. remote: Total 155 (delta 41), reused 74 (delta 29), pack-reused 60 Receiving objects: 100% (155/155), 38.55 KiB | 3.21 MiB/s, done. Resolving deltas: 100% (70/70), done. root:~# cd PG-recursively_delete/ root:~/PG-recursively_delete# make cp sql/recursively_delete.sql sql/recursively_delete--0.1.5.sql root:~/PG-recursively_delete# sudo make install /bin/mkdir -p '/usr/share/postgresql/16/extension' /bin/mkdir -p '/usr/share/postgresql/16/extension' /bin/mkdir -p '/usr/share/doc/postgresql-doc-16/extension' /usr/bin/install -c -m 644 .//recursively_delete.control '/usr/share/postgresql/16/extension/' /usr/bin/install -c -m 644 .//sql/recursively_delete--0.1.5.sql '/usr/share/postgresql/16/extension/' /usr/bin/install -c -m 644 .//doc/changelog.md '/usr/share/doc/postgresql-doc-16/extension/'</pre> <h2>Mise en place de l&#8217;environnement</h2> <p>Pour illustrer le fonctionnement de l&#8217;extension, je vais utiliser la base de données de démonstration dvdrental. Nous allons donc la télécharger et la charger dans une toute nouvelle base de données que nous aurons créée sur notre instance fraîchement créée :</p> <pre class="brush: bash; title: ; notranslate"> postgres:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2024-03-11 08:34:54-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2024-03-11 08:34:54 (46.0 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>Une fois téléchargée, on la dezippe :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ ls -l total 544 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip postgres:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar postgres:~$ ls -l total 3316 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 2835456 May 12 2019 dvdrental.tar -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip</pre> <p>On créé la base de données pour accueillir nos données, et on charge le fichier de sauvegarde :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges -----------+----------+----------+-----------------+---------+---------+------------+-----------+----------------------- dvdrental | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres (4 rows)</pre> <pre class="brush: bash; title: ; notranslate">postgres:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Une fois que c&#8217;est fait, on peut se connecter pour vérifier que tout a bien été chargé :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# \c dvdrental You are now connected to database &quot;dvdrental&quot; as user &quot;postgres&quot;. dvdrental=# \dt List of relations Schema | Name | Type | Owner --------+---------------+-------+---------- public | actor | table | postgres public | address | table | postgres public | category | table | postgres public | city | table | postgres public | country | table | postgres public | customer | table | postgres public | film | table | postgres public | film_actor | table | postgres public | film_category | table | postgres public | inventory | table | postgres public | language | table | postgres public | payment | table | postgres public | rental | table | postgres public | staff | table | postgres public | store | table | postgres (15 rows) </pre> <h2>L&#8217;extension :</h2> <p>Pour tester l&#8217;extension, nous allons essayer de supprimer un client de la liste des clients.<br /> Le schéma de la base de données dvdrental est le suivant :<br /> <a href="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram.png"><img loading="lazy" decoding="async" class="alignnone wp-image-10507" src="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram-238x300.png" alt="" width="336" height="424" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram-238x300.png 238w, https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram.png 730w" sizes="auto, (max-width: 336px) 100vw, 336px" /></a></p> <p>Si l&#8217;on observe attentivement le schéma ci-dessus, en voulant supprimer une donnée de la table customer, cela devrait avoir un impact sur les tables rental et payment qui sont directement liées à la table customer. De plus, ces deux tables sont également liées entre elles, ce qui signifie que supprimer une donnée dans la table rental modifiera nécessairement la table payment.</p> <p>Prenons l&#8217;exemple de la suppression du client numéro 1. Si nous recherchons les dépendances de ce client dans la table rental, nous obtenons 32 lignes associées au customer_id 1 :</p> <pre class="brush: sql; title: ; notranslate"> dvdrental=# select count(*) from rental where customer_id = 1; count ------- 32 (1 row) </pre> <p>Et si nous allons maintenant chercher toutes les occurrences de ce même client dans la table des paiements, nous obtenons :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select count(*) from payment where customer_id = 1; count ------- 30 (1 row) </pre> <p>À présent, avec l&#8217;extension recursive_delete, nous allons chercher à obtenir le schéma de suppression pour vérifier si les résultats que nous avons trouvés sont corrects :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# create extension recursively_delete; CREATE EXTENSION dvdrental=# \set VERBOSITY terse dvdrental=# select recursively_delete('customer', 1); INFO: DAMAGE PREVIEW (recursively_delete v0.1.5) INFO: INFO: 1 customer INFO: 30 r | payment.[&quot;customer_id&quot;] INFO: 32 r | rental.[&quot;customer_id&quot;] INFO: ~ n | | payment.[&quot;rental_id&quot;] INFO: recursively_delete -------------------- 0 (1 row) </pre> <p>La fonction de suppression de l&#8217;extension fonctionne avec les paramètres suivants :</p> <ul> <li>Le nom de la table en premier paramètre</li> <li>La clause WHERE du DELETE en second paramètre, qui peut être de multiples types (des entiers, des chaînes de caractères, des listes, des UUID&#8230;)</li> <li>Le mode de fonctionnement de l&#8217;extension, par défaut à false, qui indique au programme de ne pas effectuer les suppressions, mais simplement de dresser le schéma. Le passer à true entraînerait les suppressions.</li> </ul> <p>Pour interpréter le schéma, voici la composition de chaque nœud :</p> <ol> <li>La première colonne correspond au nombre de lignes</li> <li>Le type de contraintes qui expliquent l&#8217;implication de la table dans le schéma : &#8216;a&#8217;, &#8216;r&#8217;, &#8216;c&#8217;, &#8216;n&#8217;, ou &#8216;d&#8217; (&#8216;no action&#8217;, &#8216;restrict&#8217;, &#8216;cascade&#8217;, &#8216;set null&#8217;, ou &#8216;set default&#8217;)</li> <li>Un indicateur de si oui ou non le champ en question participe à une référence circulaire.</li> </ol> <p>En examinant le résultat renvoyé par notre extension, nous constatons que nous obtenons les mêmes résultats : 30 lignes pour payment et 32 lignes pour rental. Nous obtenons également une dernière ligne qui nous indique que payment possède une référence à rental dans sa structure, et qu&#8217;il va lui aussi procéder à des suppressions en fonction du rental_id. Cela pourrait être par exemple le cas où une location effectuée par un client serait payée par un autre.</p> <p>Pour effectuer la suppression, il suffit simplement de préciser true en troisième paramètre.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select recursively_delete('customer', 1, true); recursively_delete -------------------- 1 (1 row) </pre> <p>Et à présent, si nous consultons notre table customer, la ligne 1 a disparu, ainsi que toutes les lignes qui la concernent dans d&#8217;autres tables également.</p> <pre class="brush: sql; title: ; notranslate"> dvdrental=# select count(*) from customer where customer_id = 1; count ------- 0 (1 row) dvdrental=# select count(*) from rental where customer_id = 1; count ------- 0 (1 row) dvdrental=# select count(*) from payment where customer_id = 1; count ------- 0 (1 row) </pre> <p>Nos lignes ont bel et bien disparu.</p> <p>Cette extension fonctionne également avec les clés primaires composites. Il suffit de préciser entre crochets les deux valeurs de notre clé primaire, et le tour est joué.</p> <p>Pour illustrer davantage le fonctionnement, je vais réaliser une suppression sur la table film. Cette table possède quelques dépendances.<br /> Disons que nous souhaitons supprimer les 10 premiers films de notre liste, car ils ne sont plus loués étant trop anciens (plus personne n&#8217;a de magnétoscope pour regarder de bonnes vieilles cassettes !).</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select recursively_delete('film', (SELECT array_agg(film_id) FROM film WHERE film_id between 1 and 10)); INFO: DAMAGE PREVIEW (recursively_delete v0.1.5) INFO: INFO: 10 film INFO: 62 r | film_actor.[&quot;film_id&quot;] INFO: 10 r | film_category.[&quot;film_id&quot;] INFO: 52 r | inventory.[&quot;film_id&quot;] INFO: 165 r | | rental.[&quot;inventory_id&quot;] INFO: ~ n | | | payment.[&quot;rental_id&quot;] INFO: recursively_delete -------------------- 0 (1 row)</pre> <p>Nous observons donc que notre suppression de 10 films (dans un array) entraîne la suppression d&#8217;acteurs, de catégories, d&#8217;inventaires, et par extension, de locations et de paiements</p> <h2>Conclusion :</h2> <p>En conclusion, l&#8217;extension pg_recursively_delete offre une solution pratique pour supprimer récursivement des données dans PostgreSQL, simplifiant ainsi les tâches de maintenance et de nettoyage des bases de données. Cependant, malgré ses avantages, cette extension présente certaines limites en termes de performances.</p> <p>L&#8217;une des principales limitations réside dans le fait que la suppression récursive peut entraîner des opérations coûteuses en termes de temps d&#8217;exécution, surtout lorsque les données concernées sont fortement imbriquées ou que la base de données est volumineuse. Les performances peuvent également être affectées lorsque les tables impliquées dans la suppression ont des index complexes ou des contraintes de clés étrangères.</p> <p>De plus, il est crucial de reconnaître les risques associés à la suppression de données ayant de nombreuses dépendances dans une base de données. La suppression inconsidérée de telles données peut entraîner des incohérences dans la base de données, des erreurs d&#8217;intégrité référentielle et même des pertes de données importantes. Il est donc essentiel de procéder avec prudence et de prendre en compte toutes les implications potentielles avant d&#8217;utiliser cette extension.</p> <p>En résumé, bien que l&#8217;extension pg_recursively_delete offre une fonctionnalité utile pour gérer les opérations de suppression récursive dans PostgreSQL, il est essentiel pour les utilisateurs de comprendre ses limites en termes de performances et les risques potentiels associés à la suppression de données avec de nombreuses dépendances. Une utilisation judicieuse et une évaluation minutieuse des scénarios d&#8217;utilisation sont indispensables pour garantir l&#8217;intégrité et la performance de la base de données.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/" rel="bookmark" title="27 mars 2024">pg_dirtyread où comment réparer facilement un delete sauvage</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-anonymizer/" rel="bookmark" title="7 juillet 2022">PostgreSQL Anonymizer</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/" rel="bookmark" title="30 janvier 2024">Pseudonymiser vos bases PostgreSQL</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 2.393 ms --></p> <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%2F%3Fp%3D10505&#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%2F%3Fp%3D10505&#038;title=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives" 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=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10505" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/">pg_recursively_delete : Simplifier les suppressions récursives</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10505&#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%2F%3Fp%3D10505&#038;title=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives" 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=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10505" 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>Si vous avez été amené au fil de votre carrière à manipuler de gros volumes de données contenus dans plusieurs tables possédant des références croisées entre elles, dépendantes d&#8217;autres tables, qui elles-mêmes dépendent d&#8217;autres tables, vous savez à quel point il peut être compliqué de remonter l&#8217;intégralité de l&#8217;arbre de dépendance pour supprimer la moindre ligne. Cela peut être long et fastidieux.</p> <p>Vous ne savez pas vraiment ce que vous supprimez, dans quelles tables, et quels impacts cela peut avoir sur votre base de données. Si les dépendances sont nombreuses, il est d&#8217;autant plus compliqué de tout retracer et d&#8217;être sûr à 100 % de ce que votre DELETE va entraîner.</p> <p>Dans cet article, je vais vous présenter rapidement un petit outil sous la forme d&#8217;une extension que je trouve pratique à utiliser dans ce cas de figure. L&#8217;outil s&#8217;appelle pg_recursively_delete, et il permet de tracer avant d&#8217;exécuter l&#8217;ordre de suppression de votre ligne, et d&#8217;avoir une arborescence des différentes données que vous allez impacter.</p> <h2>Installation d&#8217;un moteur et de l&#8217;extension :</h2> <p>Pour cet article, j&#8217;ai choisi d&#8217;utiliser PostgreSQL en version 16 pour tester si l&#8217;extension fonctionnait toujours.</p> <pre class="brush: bash; title: ; notranslate">root:~#sudo apt update &amp;amp;&amp;amp; sudo apt upgrade root:~#sudo sh -c 'echo &quot;deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main&quot; &amp;gt; /etc/apt/sources.list.d/pgdg.list' root:~#wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - root:~#sudo apt -y update root:~#sudo apt -y install postgresql-16</pre> <p>Notre moteur de base de données est installé, à présent il nous faut télécharger les sources de l&#8217;extension, et l&#8217;installer.</p> <pre class="brush: bash; title: ; notranslate">root:~# git clone https://github.com/trlorenz/PG-recursively_delete.git Cloning into 'PG-recursively_delete'... remote: Enumerating objects: 155, done. remote: Counting objects: 100% (95/95), done. remote: Compressing objects: 100% (62/62), done. remote: Total 155 (delta 41), reused 74 (delta 29), pack-reused 60 Receiving objects: 100% (155/155), 38.55 KiB | 3.21 MiB/s, done. Resolving deltas: 100% (70/70), done. root:~# cd PG-recursively_delete/ root:~/PG-recursively_delete# make cp sql/recursively_delete.sql sql/recursively_delete--0.1.5.sql root:~/PG-recursively_delete# sudo make install /bin/mkdir -p '/usr/share/postgresql/16/extension' /bin/mkdir -p '/usr/share/postgresql/16/extension' /bin/mkdir -p '/usr/share/doc/postgresql-doc-16/extension' /usr/bin/install -c -m 644 .//recursively_delete.control '/usr/share/postgresql/16/extension/' /usr/bin/install -c -m 644 .//sql/recursively_delete--0.1.5.sql '/usr/share/postgresql/16/extension/' /usr/bin/install -c -m 644 .//doc/changelog.md '/usr/share/doc/postgresql-doc-16/extension/'</pre> <h2>Mise en place de l&#8217;environnement</h2> <p>Pour illustrer le fonctionnement de l&#8217;extension, je vais utiliser la base de données de démonstration dvdrental. Nous allons donc la télécharger et la charger dans une toute nouvelle base de données que nous aurons créée sur notre instance fraîchement créée :</p> <pre class="brush: bash; title: ; notranslate"> postgres:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2024-03-11 08:34:54-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2024-03-11 08:34:54 (46.0 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>Une fois téléchargée, on la dezippe :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ ls -l total 544 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip postgres:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar postgres:~$ ls -l total 3316 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 2835456 May 12 2019 dvdrental.tar -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip</pre> <p>On créé la base de données pour accueillir nos données, et on charge le fichier de sauvegarde :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges -----------+----------+----------+-----------------+---------+---------+------------+-----------+----------------------- dvdrental | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres (4 rows)</pre> <pre class="brush: bash; title: ; notranslate">postgres:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Une fois que c&#8217;est fait, on peut se connecter pour vérifier que tout a bien été chargé :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# \c dvdrental You are now connected to database &quot;dvdrental&quot; as user &quot;postgres&quot;. dvdrental=# \dt List of relations Schema | Name | Type | Owner --------+---------------+-------+---------- public | actor | table | postgres public | address | table | postgres public | category | table | postgres public | city | table | postgres public | country | table | postgres public | customer | table | postgres public | film | table | postgres public | film_actor | table | postgres public | film_category | table | postgres public | inventory | table | postgres public | language | table | postgres public | payment | table | postgres public | rental | table | postgres public | staff | table | postgres public | store | table | postgres (15 rows) </pre> <h2>L&#8217;extension :</h2> <p>Pour tester l&#8217;extension, nous allons essayer de supprimer un client de la liste des clients.<br /> Le schéma de la base de données dvdrental est le suivant :<br /> <a href="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram.png"><img loading="lazy" decoding="async" class="alignnone wp-image-10507" src="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram-238x300.png" alt="" width="336" height="424" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram-238x300.png 238w, https://blog.capdata.fr/wp-content/uploads/2024/03/dvd-rental-sample-database-diagram.png 730w" sizes="auto, (max-width: 336px) 100vw, 336px" /></a></p> <p>Si l&#8217;on observe attentivement le schéma ci-dessus, en voulant supprimer une donnée de la table customer, cela devrait avoir un impact sur les tables rental et payment qui sont directement liées à la table customer. De plus, ces deux tables sont également liées entre elles, ce qui signifie que supprimer une donnée dans la table rental modifiera nécessairement la table payment.</p> <p>Prenons l&#8217;exemple de la suppression du client numéro 1. Si nous recherchons les dépendances de ce client dans la table rental, nous obtenons 32 lignes associées au customer_id 1 :</p> <pre class="brush: sql; title: ; notranslate"> dvdrental=# select count(*) from rental where customer_id = 1; count ------- 32 (1 row) </pre> <p>Et si nous allons maintenant chercher toutes les occurrences de ce même client dans la table des paiements, nous obtenons :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select count(*) from payment where customer_id = 1; count ------- 30 (1 row) </pre> <p>À présent, avec l&#8217;extension recursive_delete, nous allons chercher à obtenir le schéma de suppression pour vérifier si les résultats que nous avons trouvés sont corrects :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# create extension recursively_delete; CREATE EXTENSION dvdrental=# \set VERBOSITY terse dvdrental=# select recursively_delete('customer', 1); INFO: DAMAGE PREVIEW (recursively_delete v0.1.5) INFO: INFO: 1 customer INFO: 30 r | payment.[&quot;customer_id&quot;] INFO: 32 r | rental.[&quot;customer_id&quot;] INFO: ~ n | | payment.[&quot;rental_id&quot;] INFO: recursively_delete -------------------- 0 (1 row) </pre> <p>La fonction de suppression de l&#8217;extension fonctionne avec les paramètres suivants :</p> <ul> <li>Le nom de la table en premier paramètre</li> <li>La clause WHERE du DELETE en second paramètre, qui peut être de multiples types (des entiers, des chaînes de caractères, des listes, des UUID&#8230;)</li> <li>Le mode de fonctionnement de l&#8217;extension, par défaut à false, qui indique au programme de ne pas effectuer les suppressions, mais simplement de dresser le schéma. Le passer à true entraînerait les suppressions.</li> </ul> <p>Pour interpréter le schéma, voici la composition de chaque nœud :</p> <ol> <li>La première colonne correspond au nombre de lignes</li> <li>Le type de contraintes qui expliquent l&#8217;implication de la table dans le schéma : &#8216;a&#8217;, &#8216;r&#8217;, &#8216;c&#8217;, &#8216;n&#8217;, ou &#8216;d&#8217; (&#8216;no action&#8217;, &#8216;restrict&#8217;, &#8216;cascade&#8217;, &#8216;set null&#8217;, ou &#8216;set default&#8217;)</li> <li>Un indicateur de si oui ou non le champ en question participe à une référence circulaire.</li> </ol> <p>En examinant le résultat renvoyé par notre extension, nous constatons que nous obtenons les mêmes résultats : 30 lignes pour payment et 32 lignes pour rental. Nous obtenons également une dernière ligne qui nous indique que payment possède une référence à rental dans sa structure, et qu&#8217;il va lui aussi procéder à des suppressions en fonction du rental_id. Cela pourrait être par exemple le cas où une location effectuée par un client serait payée par un autre.</p> <p>Pour effectuer la suppression, il suffit simplement de préciser true en troisième paramètre.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select recursively_delete('customer', 1, true); recursively_delete -------------------- 1 (1 row) </pre> <p>Et à présent, si nous consultons notre table customer, la ligne 1 a disparu, ainsi que toutes les lignes qui la concernent dans d&#8217;autres tables également.</p> <pre class="brush: sql; title: ; notranslate"> dvdrental=# select count(*) from customer where customer_id = 1; count ------- 0 (1 row) dvdrental=# select count(*) from rental where customer_id = 1; count ------- 0 (1 row) dvdrental=# select count(*) from payment where customer_id = 1; count ------- 0 (1 row) </pre> <p>Nos lignes ont bel et bien disparu.</p> <p>Cette extension fonctionne également avec les clés primaires composites. Il suffit de préciser entre crochets les deux valeurs de notre clé primaire, et le tour est joué.</p> <p>Pour illustrer davantage le fonctionnement, je vais réaliser une suppression sur la table film. Cette table possède quelques dépendances.<br /> Disons que nous souhaitons supprimer les 10 premiers films de notre liste, car ils ne sont plus loués étant trop anciens (plus personne n&#8217;a de magnétoscope pour regarder de bonnes vieilles cassettes !).</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select recursively_delete('film', (SELECT array_agg(film_id) FROM film WHERE film_id between 1 and 10)); INFO: DAMAGE PREVIEW (recursively_delete v0.1.5) INFO: INFO: 10 film INFO: 62 r | film_actor.[&quot;film_id&quot;] INFO: 10 r | film_category.[&quot;film_id&quot;] INFO: 52 r | inventory.[&quot;film_id&quot;] INFO: 165 r | | rental.[&quot;inventory_id&quot;] INFO: ~ n | | | payment.[&quot;rental_id&quot;] INFO: recursively_delete -------------------- 0 (1 row)</pre> <p>Nous observons donc que notre suppression de 10 films (dans un array) entraîne la suppression d&#8217;acteurs, de catégories, d&#8217;inventaires, et par extension, de locations et de paiements</p> <h2>Conclusion :</h2> <p>En conclusion, l&#8217;extension pg_recursively_delete offre une solution pratique pour supprimer récursivement des données dans PostgreSQL, simplifiant ainsi les tâches de maintenance et de nettoyage des bases de données. Cependant, malgré ses avantages, cette extension présente certaines limites en termes de performances.</p> <p>L&#8217;une des principales limitations réside dans le fait que la suppression récursive peut entraîner des opérations coûteuses en termes de temps d&#8217;exécution, surtout lorsque les données concernées sont fortement imbriquées ou que la base de données est volumineuse. Les performances peuvent également être affectées lorsque les tables impliquées dans la suppression ont des index complexes ou des contraintes de clés étrangères.</p> <p>De plus, il est crucial de reconnaître les risques associés à la suppression de données ayant de nombreuses dépendances dans une base de données. La suppression inconsidérée de telles données peut entraîner des incohérences dans la base de données, des erreurs d&#8217;intégrité référentielle et même des pertes de données importantes. Il est donc essentiel de procéder avec prudence et de prendre en compte toutes les implications potentielles avant d&#8217;utiliser cette extension.</p> <p>En résumé, bien que l&#8217;extension pg_recursively_delete offre une fonctionnalité utile pour gérer les opérations de suppression récursive dans PostgreSQL, il est essentiel pour les utilisateurs de comprendre ses limites en termes de performances et les risques potentiels associés à la suppression de données avec de nombreuses dépendances. Une utilisation judicieuse et une évaluation minutieuse des scénarios d&#8217;utilisation sont indispensables pour garantir l&#8217;intégrité et la performance de la base de données.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/" rel="bookmark" title="27 mars 2024">pg_dirtyread où comment réparer facilement un delete sauvage</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-anonymizer/" rel="bookmark" title="7 juillet 2022">PostgreSQL Anonymizer</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/" rel="bookmark" title="30 janvier 2024">Pseudonymiser vos bases PostgreSQL</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 2.100 ms --></p> <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%2F%3Fp%3D10505&#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%2F%3Fp%3D10505&#038;title=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives" 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=pg_recursively_delete%20%3A%20Simplifier%20les%20suppressions%20r%C3%A9cursives&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10505" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/">pg_recursively_delete : Simplifier les suppressions récursives</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>Si vous avez été amené au fil de votre carrière à manipuler de gros volumes de données contenus dans plusieurs tables possédant des références croisées entre elles, dépendantes d&#8217;autres tables, qui elles-mêmes dépendent d&#8217;autres tables, vous savez à quel point&#8230; <a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/">pg_recursively_delete : Simplifier les suppressions récursives</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> @@ -275,7 +275,7 @@ pg_dirtyread où comment réparer facilement un delete sauvage 2024-03-27T14:24:14Z tag:blog.capdata.fr,2024-03-27:/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/ - <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%2F%3Fp%3D10494&#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%2F%3Fp%3D10494&#038;title=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage" 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=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10494" 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>Imaginez le scénario : nous sommes vendredi soir, en fin de journée, et pour une raison quelconque, un développeur qui a eu une semaine fatiguante se dit qu&#8217;il serait bien de supprimer les lignes qu&#8217;il a modifiées dans la base de données de développement, afin que lundi il puisse reprendre le travail à zéro. Il se connecte donc et, à l&#8217;aide d&#8217;une commande de suppression sur la table concernée, il efface ses quelques lignes&#8230; Avant de se rendre compte qu&#8217;il vient de le faire en production. </p> <p>Cela nous est arrivé à tous, au moins une fois dans notre carrière. Un manque d&#8217;attention, une absence de sécurisation, une erreur est si vite arrivée. Oui, mais voilà. La solution pour pallier à ce genre de problèmes, c&#8217;est généralement de recharger une sauvegarde de la base de données, pour ne surtout pas perdre cette ligne essentielle des paiements enregistrés&#8230; C&#8217;est long. La base est volumineuse&#8230; Et puis, nous n&#8217;avons pas un plan de sauvegarde fiable&#8230; Au secours !</p> <p>Avant de céder à la panique, peut-être existe-t-il une autre solution à votre problème.</p> <p>L&#8217;extension pg_dirtyread pourrait être une idée. Cette extension permet aux utilisateurs de lire des données supprimées dans les tables, ce qui est normalement impossible en temps normal. Cette extension est disponible gratuitement sur GIT, et elle peut vous sauver la vie dans le scénario évoqué juste avant. Nous allons voir ensemble comment l&#8217;utiliser :</p> <h2>Installer PostgreSQL et pg_dirtyread :</h2> <p>C&#8217;est assez rare pour le souligner, mais pg_dirtyread possède son propre package Ubuntu tout inclus. Ce package, si vous pouvez vous en servir, contient l&#8217;installation du moteur, l&#8217;installation des dépendances de développement de PostgreSQL, le client, et l&#8217;extension elle-même. Cela simplifie grandement le travail. Vous n&#8217;avez qu&#8217;à télécharger le package dans la version qui vous intéresse. Pour cet article, j&#8217;ai choisi de télécharger et installer la dernière version de PostgreSQL ainsi que la dernière version de l&#8217;extension.</p> <pre class="brush: bash; title: ; notranslate"> root:~/pg_dirtyread# sudo apt -y install postgresql-16-dirtyread Reading package lists... Done Building dependency tree... Done Reading state information... Done The following additional packages will be installed: libcommon-sense-perl libjson-perl libjson-xs-perl libpq5 libsensors-config libsensors5 libtypes-serialiser-perl postgresql-16 postgresql-client-16 postgresql-client-common postgresql-common ssl-cert sysstat Suggested packages: lm-sensors postgresql-doc-16 isag The following NEW packages will be installed: libcommon-sense-perl libjson-perl libjson-xs-perl libpq5 libsensors-config libsensors5 libtypes-serialiser-perl postgresql-16 postgresql-16-dirtyread postgresql-client-16 postgresql-client-common postgresql-common ssl-cert sysstat 0 upgraded, 14 newly installed, 0 to remove and 0 not upgraded. Need to get 21.3 MB of archives. ... Running kernel seems to be up-to-date. No services need to be restarted. No containers need to be restarted. No user sessions are running outdated binaries. No VM guests are running outdated hypervisor (qemu) binaries on this host.</pre> <p>Il existe une autre méthode d&#8217;installation, la plus classique. Il suffit de télécharger les sources depuis le dépôt GIT et de les compiler sur votre machine en utilisant les commandes &#8216;make&#8217; et &#8216;make install&#8217;.</p> <h2>Mise en place d&#8217;un environnement :</h2> <p>Pour notre test, j&#8217;ai choisit de me servir encore une fois de la base de données dvdrental, accessible à tous. Il me faut donc la télécharger :</p> <pre class="brush: bash; title: ; notranslate"> postgres:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2024-03-11 08:34:54-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2024-03-11 08:34:54 (46.0 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>Une fois téléchargée, on la dezippe :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ ls -l total 544 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip postgres:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar postgres:~$ ls -l total 3316 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 2835456 May 12 2019 dvdrental.tar -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip</pre> <p>On créé la base de données pour accueillir nos données, et on charge le fichier de sauvegarde :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges -----------+----------+----------+-----------------+---------+---------+------------+-----------+----------------------- dvdrental | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres (4 rows)</pre> <pre class="brush: bash; title: ; notranslate">postgres:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Une fois que c&#8217;est fait, on peut se connecter pour vérifier que tout a bien été chargé :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# \c dvdrental You are now connected to database &quot;dvdrental&quot; as user &quot;postgres&quot;. dvdrental=# \dt List of relations Schema | Name | Type | Owner --------+---------------+-------+---------- public | actor | table | postgres public | address | table | postgres public | category | table | postgres public | city | table | postgres public | country | table | postgres public | customer | table | postgres public | film | table | postgres public | film_actor | table | postgres public | film_category | table | postgres public | inventory | table | postgres public | language | table | postgres public | payment | table | postgres public | rental | table | postgres public | staff | table | postgres public | store | table | postgres (15 rows) </pre> <h2>Premier scénario : Suppression de lignes dans une table</h2> <p>Prenons ici le cas de la situation décrite dans l&#8217;introduction. Une ou plusieurs lignes ont été supprimées d&#8217;une ou plusieurs tables. Recharger l&#8217;intégralité d&#8217;une base de données juste pour ces quelques lignes, aussi essentielles soient-elles, demande un investissement énorme.</p> <p>Pour notre exemple, je vais supprimer un certain nombre de lignes de la table &#8216;payment&#8217;.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from payment limit 10; payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+---------------------------- 17503 | 341 | 2 | 1520 | 7.99 | 2007-02-15 22:25:46.996577 17504 | 341 | 1 | 1778 | 1.99 | 2007-02-16 17:23:14.996577 17505 | 341 | 1 | 1849 | 7.99 | 2007-02-16 22:41:45.996577 17506 | 341 | 2 | 2829 | 2.99 | 2007-02-19 19:39:56.996577 17507 | 341 | 2 | 3130 | 7.99 | 2007-02-20 17:31:48.996577 17508 | 341 | 1 | 3382 | 5.99 | 2007-02-21 12:33:49.996577 17509 | 342 | 2 | 2190 | 5.99 | 2007-02-17 23:58:17.996577 17510 | 342 | 1 | 2914 | 5.99 | 2007-02-20 02:11:44.996577 17511 | 342 | 1 | 3081 | 2.99 | 2007-02-20 13:57:39.996577 17512 | 343 | 2 | 1547 | 4.99 | 2007-02-16 00:10:50.996577 (10 rows) dvdrental=# delete from payment where payment_id between 17523 and 17532; DELETE 10</pre> <div class="flex-1 overflow-hidden"> <div class="react-scroll-to-bottom--css-toqmf-79elbk h-full"> <div class="react-scroll-to-bottom--css-toqmf-1n7m0yu"> <div class="flex flex-col text-sm pb-9"> <div class="w-full text-token-text-primary" data-testid="conversation-turn-17"> <div class="px-4 py-2 justify-center text-base md:gap-6 m-auto"> <div class="flex flex-1 text-base mx-auto gap-3 md:px-5 lg:px-1 xl:px-5 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem] group final-completion"> <div class="relative flex w-full flex-col agent-turn"> <div class="flex-col gap-1 md:gap-3"> <div class="flex flex-grow flex-col max-w-full"> <div class="min-h-[20px] text-message flex flex-col items-start gap-3 whitespace-pre-wrap break-words [.text-message+&amp;]:mt-5 overflow-x-auto" data-message-author-role="assistant" data-message-id="107c8c40-0671-4f34-88f1-895d9fe2648b"> <div class="markdown prose w-full break-words dark:prose-invert light"> <p>La bêtise est faite, les données ont disparu. Lorsque j&#8217;essaie de les requêter pour voir si elles sont encore là, je me heurte à la réalité : elles ont été supprimées.</p> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from payment where payment_id between 17523 and 17532; payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+-------------- (0 rows)</pre> <p>La première chose à faire, en toute urgence, c&#8217;est de désactiver l&#8217;auto-vacuum. C&#8217;est votre pire ennemi dans notre scénario. C&#8217;est même la première chose à faire lorsque vous vous rendez compte que l&#8217;erreur a été commise :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter table payment set (autovacuum_enabled=false);</pre> <p>Si l&#8217;auto-vacuum est passé sur la table avant que vous n&#8217;ayez eu le temps de le désactiver, malheureusement, pg_dirtyread ne vous servira plus à rien. Une fois cela fait, on initialise l&#8217;extension :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# CREATE EXTENSION pg_dirtyread; CREATE EXTENSION</pre> <p>L&#8217;extension étant maintenant créée, on peut requêter les données perdues. La syntaxe d&#8217;utilisation exige que vous rappeliez le schéma de la table en question au moment de requêter les lignes supprimées. Vous pouvez choisir de ne pas inclure toutes les colonnes, ou même d&#8217;en ajouter, comme nous le verrons à la fin de l&#8217;article. Dans notre exemple, la requête pour consulter les données effacées sera la suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') AS t(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), payment_date timestamp without time zone); payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 17524 | 345 | 2 | 1550 | 0.99 | 2007-02-16 00:27:01.996577 17525 | 345 | 2 | 2766 | 4.99 | 2007-02-19 16:13:41.996577 17526 | 346 | 1 | 1994 | 5.99 | 2007-02-17 09:35:32.996577 17527 | 346 | 2 | 3372 | 2.99 | 2007-02-21 12:02:45.996577 17528 | 346 | 1 | 3421 | 2.99 | 2007-02-21 15:51:24.996577 17529 | 347 | 2 | 1711 | 8.99 | 2007-02-16 12:40:18.996577 17530 | 347 | 2 | 2274 | 0.99 | 2007-02-18 04:59:41.996577 17531 | 347 | 1 | 3026 | 4.99 | 2007-02-20 10:16:26.996577 17532 | 347 | 1 | 3092 | 8.99 | 2007-02-20 14:33:08.996577 17533 | 347 | 1 | 3326 | 7.99 | 2007-02-21 07:33:16.996577 17534 | 348 | 1 | 1654 | 2.99 | 2007-02-16 08:11:14.996577 17535 | 348 | 1 | 2041 | 8.99 | 2007-02-17 12:47:26.996577 17536 | 348 | 2 | 2499 | 0.99 | 2007-02-18 21:30:02.996577</pre> <p>On voit bien apparaître en début de liste nos lignes qui sont censées ne plus exister. Cela nous permet éventuellement d&#8217;extraire les données qu&#8217;elles contiennent pour pouvoir les réinjecter ensuite avec un INSERT. On peut également filtrer les données pour ne rechercher que celles que l&#8217;on a supprimées :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') payment(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), dropped_6 timestamp without time zone) where payment_id = 17523; payment_id | customer_id | staff_id | rental_id | amount | dropped_6 ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 (1 row) </pre> <h2>Deuxième scénario : On a supprimé une colonne entière</h2> <p>Deuxième cas typique, sûrement plus rare, celui où une colonne entière est supprimée. Pour notre exemple, je vais supprimer la colonne contenant la date de paiement :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter table payment drop column payment_date; ALTER TABLE</pre> <p>pg_dirtyread nous permet de retrouver les informations de cette colonne à condition qu&#8217;aucun VACUUM FULL ou CLUSTER n&#8217;ait été exécuté. Pour ce faire, il faut connaître la position de la colonne dans l&#8217;ordre des colonnes de la table. Pour notre exemple, la colonne de date est la dernière colonne de la table, donc en position 6. Pour retrouver nos données, on peut donc exécuter la commande suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') payment(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), dropped_6 timestamp without time zone); payment_id | customer_id | staff_id | rental_id | amount | dropped_6 ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 17524 | 345 | 2 | 1550 | 0.99 | 2007-02-16 00:27:01.996577 17525 | 345 | 2 | 2766 | 4.99 | 2007-02-19 16:13:41.996577 17526 | 346 | 1 | 1994 | 5.99 | 2007-02-17 09:35:32.996577 17527 | 346 | 2 | 3372 | 2.99 | 2007-02-21 12:02:45.996577 </pre> <p>Le cas d&#8217;une restauration d&#8217;une colonne entière supprimée est compliqué à gérer. Il faudrait la recréer, puis modifier toutes les lignes une à une pour réajouter les valeurs de cette fameuse colonne. Cela peut s&#8217;avérer un peu complexe à réaliser. Cependant, si vous ne disposez pas d&#8217;une sauvegarde pg_dump, vous n&#8217;aurez peut-être pas d&#8217;autres alternatives que cela, à part recharger entièrement la base.</p> <h2>Bonus : Récupérer les informations système des lignes supprimées</h2> <p>Avec pg_dirtyread, il est possible de récupérer les informations système des lignes qui ont été supprimées. Pour ce faire, rien de plus simple : il suffit d&#8217;indiquer dans la requête les informations que vous souhaitez récupérer. De plus, il existe une colonne qui indique si la ligne est supposément supprimée ou non, ce qui pourrait être pratique pour trier les différentes lignes selon leur état :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT * FROM pg_dirtyread('payment') AS t(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean); tableoid | ctid | xmin | xmax | cmin | cmax | dead ----------+-----------+------+------+------+------+------ 16505 | (0,21) | 835 | 941 | 0 | 0 | f 16505 | (0,22) | 835 | 941 | 0 | 0 | f 16505 | (0,23) | 835 | 941 | 0 | 0 | f 16505 | (0,24) | 835 | 941 | 0 | 0 | f 16505 | (0,25) | 835 | 941 | 0 | 0 | f 16505 | (0,26) | 835 | 941 | 0 | 0 | f 16505 | (0,27) | 835 | 941 | 0 | 0 | f 16505 | (0,28) | 835 | 941 | 0 | 0 | f 16505 | (0,29) | 835 | 941 | 0 | 0 | f 16505 | (0,30) | 835 | 941 | 0 | 0 | f </pre> <h2>Conclusion :</h2> <p>En conclusion, l&#8217;extension pg_dirtyread offre une solution précieuse pour accéder aux données supprimées dans une base de données PostgreSQL. Son utilisation peut être cruciale dans des cas d&#8217;incidents critiques tels que la récupération de données perdues accidentellement ou la résolution d&#8217;incohérences de données. Cependant, il est essentiel de rappeler que son application reste extrêmement situationnelle et que pour qu&#8217;elle puisse être efficace, l&#8217;autovacuum doit être désactivé, ce qui n&#8217;est pas forcément recommandé.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/aws-rds-les-extensions-postgresql/" rel="bookmark" title="21 janvier 2020">AWS RDS : les extensions PostgreSQL</a> (Emmanuel RAMI) [AWSPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/" rel="bookmark" title="30 janvier 2024">Pseudonymiser vos bases PostgreSQL</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 3.014 ms --></p> <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%2F%3Fp%3D10494&#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%2F%3Fp%3D10494&#038;title=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage" 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=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10494" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/">pg_dirtyread où comment réparer facilement un delete sauvage</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10494&#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%2F%3Fp%3D10494&#038;title=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage" 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=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10494" 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>Imaginez le scénario : nous sommes vendredi soir, en fin de journée, et pour une raison quelconque, un développeur qui a eu une semaine fatiguante se dit qu&#8217;il serait bien de supprimer les lignes qu&#8217;il a modifiées dans la base de données de développement, afin que lundi il puisse reprendre le travail à zéro. Il se connecte donc et, à l&#8217;aide d&#8217;une commande de suppression sur la table concernée, il efface ses quelques lignes&#8230; Avant de se rendre compte qu&#8217;il vient de le faire en production. </p> <p>Cela nous est arrivé à tous, au moins une fois dans notre carrière. Un manque d&#8217;attention, une absence de sécurisation, une erreur est si vite arrivée. Oui, mais voilà. La solution pour pallier à ce genre de problèmes, c&#8217;est généralement de recharger une sauvegarde de la base de données, pour ne surtout pas perdre cette ligne essentielle des paiements enregistrés&#8230; C&#8217;est long. La base est volumineuse&#8230; Et puis, nous n&#8217;avons pas un plan de sauvegarde fiable&#8230; Au secours !</p> <p>Avant de céder à la panique, peut-être existe-t-il une autre solution à votre problème.</p> <p>L&#8217;extension pg_dirtyread pourrait être une idée. Cette extension permet aux utilisateurs de lire des données supprimées dans les tables, ce qui est normalement impossible en temps normal. Cette extension est disponible gratuitement sur GIT, et elle peut vous sauver la vie dans le scénario évoqué juste avant. Nous allons voir ensemble comment l&#8217;utiliser :</p> <h2>Installer PostgreSQL et pg_dirtyread :</h2> <p>C&#8217;est assez rare pour le souligner, mais pg_dirtyread possède son propre package Ubuntu tout inclus. Ce package, si vous pouvez vous en servir, contient l&#8217;installation du moteur, l&#8217;installation des dépendances de développement de PostgreSQL, le client, et l&#8217;extension elle-même. Cela simplifie grandement le travail. Vous n&#8217;avez qu&#8217;à télécharger le package dans la version qui vous intéresse. Pour cet article, j&#8217;ai choisi de télécharger et installer la dernière version de PostgreSQL ainsi que la dernière version de l&#8217;extension.</p> <pre class="brush: bash; title: ; notranslate"> root:~/pg_dirtyread# sudo apt -y install postgresql-16-dirtyread Reading package lists... Done Building dependency tree... Done Reading state information... Done The following additional packages will be installed: libcommon-sense-perl libjson-perl libjson-xs-perl libpq5 libsensors-config libsensors5 libtypes-serialiser-perl postgresql-16 postgresql-client-16 postgresql-client-common postgresql-common ssl-cert sysstat Suggested packages: lm-sensors postgresql-doc-16 isag The following NEW packages will be installed: libcommon-sense-perl libjson-perl libjson-xs-perl libpq5 libsensors-config libsensors5 libtypes-serialiser-perl postgresql-16 postgresql-16-dirtyread postgresql-client-16 postgresql-client-common postgresql-common ssl-cert sysstat 0 upgraded, 14 newly installed, 0 to remove and 0 not upgraded. Need to get 21.3 MB of archives. ... Running kernel seems to be up-to-date. No services need to be restarted. No containers need to be restarted. No user sessions are running outdated binaries. No VM guests are running outdated hypervisor (qemu) binaries on this host.</pre> <p>Il existe une autre méthode d&#8217;installation, la plus classique. Il suffit de télécharger les sources depuis le dépôt GIT et de les compiler sur votre machine en utilisant les commandes &#8216;make&#8217; et &#8216;make install&#8217;.</p> <h2>Mise en place d&#8217;un environnement :</h2> <p>Pour notre test, j&#8217;ai choisit de me servir encore une fois de la base de données dvdrental, accessible à tous. Il me faut donc la télécharger :</p> <pre class="brush: bash; title: ; notranslate"> postgres:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2024-03-11 08:34:54-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2024-03-11 08:34:54 (46.0 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>Une fois téléchargée, on la dezippe :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ ls -l total 544 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip postgres:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar postgres:~$ ls -l total 3316 drwxr-xr-x 3 postgres postgres 4096 Mar 11 08:30 16 -rw-rw-r-- 1 postgres postgres 2835456 May 12 2019 dvdrental.tar -rw-rw-r-- 1 postgres postgres 550906 May 12 2019 dvdrental.zip</pre> <p>On créé la base de données pour accueillir nos données, et on charge le fichier de sauvegarde :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges -----------+----------+----------+-----------------+---------+---------+------------+-----------+----------------------- dvdrental | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | postgres | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | template0 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | libc | C.UTF-8 | C.UTF-8 | | | =c/postgres + | | | | | | | | postgres=CTc/postgres (4 rows)</pre> <pre class="brush: bash; title: ; notranslate">postgres:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Une fois que c&#8217;est fait, on peut se connecter pour vérifier que tout a bien été chargé :</p> <pre class="brush: bash; title: ; notranslate">postgres:~$ psql psql (16.2 (Ubuntu 16.2-1.pgdg22.04+1)) Type &quot;help&quot; for help. </pre> <pre class="brush: sql; title: ; notranslate">postgres=# \c dvdrental You are now connected to database &quot;dvdrental&quot; as user &quot;postgres&quot;. dvdrental=# \dt List of relations Schema | Name | Type | Owner --------+---------------+-------+---------- public | actor | table | postgres public | address | table | postgres public | category | table | postgres public | city | table | postgres public | country | table | postgres public | customer | table | postgres public | film | table | postgres public | film_actor | table | postgres public | film_category | table | postgres public | inventory | table | postgres public | language | table | postgres public | payment | table | postgres public | rental | table | postgres public | staff | table | postgres public | store | table | postgres (15 rows) </pre> <h2>Premier scénario : Suppression de lignes dans une table</h2> <p>Prenons ici le cas de la situation décrite dans l&#8217;introduction. Une ou plusieurs lignes ont été supprimées d&#8217;une ou plusieurs tables. Recharger l&#8217;intégralité d&#8217;une base de données juste pour ces quelques lignes, aussi essentielles soient-elles, demande un investissement énorme.</p> <p>Pour notre exemple, je vais supprimer un certain nombre de lignes de la table &#8216;payment&#8217;.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from payment limit 10; payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+---------------------------- 17503 | 341 | 2 | 1520 | 7.99 | 2007-02-15 22:25:46.996577 17504 | 341 | 1 | 1778 | 1.99 | 2007-02-16 17:23:14.996577 17505 | 341 | 1 | 1849 | 7.99 | 2007-02-16 22:41:45.996577 17506 | 341 | 2 | 2829 | 2.99 | 2007-02-19 19:39:56.996577 17507 | 341 | 2 | 3130 | 7.99 | 2007-02-20 17:31:48.996577 17508 | 341 | 1 | 3382 | 5.99 | 2007-02-21 12:33:49.996577 17509 | 342 | 2 | 2190 | 5.99 | 2007-02-17 23:58:17.996577 17510 | 342 | 1 | 2914 | 5.99 | 2007-02-20 02:11:44.996577 17511 | 342 | 1 | 3081 | 2.99 | 2007-02-20 13:57:39.996577 17512 | 343 | 2 | 1547 | 4.99 | 2007-02-16 00:10:50.996577 (10 rows) dvdrental=# delete from payment where payment_id between 17523 and 17532; DELETE 10</pre> <div class="flex-1 overflow-hidden"> <div class="react-scroll-to-bottom--css-toqmf-79elbk h-full"> <div class="react-scroll-to-bottom--css-toqmf-1n7m0yu"> <div class="flex flex-col text-sm pb-9"> <div class="w-full text-token-text-primary" data-testid="conversation-turn-17"> <div class="px-4 py-2 justify-center text-base md:gap-6 m-auto"> <div class="flex flex-1 text-base mx-auto gap-3 md:px-5 lg:px-1 xl:px-5 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem] group final-completion"> <div class="relative flex w-full flex-col agent-turn"> <div class="flex-col gap-1 md:gap-3"> <div class="flex flex-grow flex-col max-w-full"> <div class="min-h-[20px] text-message flex flex-col items-start gap-3 whitespace-pre-wrap break-words [.text-message+&amp;]:mt-5 overflow-x-auto" data-message-author-role="assistant" data-message-id="107c8c40-0671-4f34-88f1-895d9fe2648b"> <div class="markdown prose w-full break-words dark:prose-invert light"> <p>La bêtise est faite, les données ont disparu. Lorsque j&#8217;essaie de les requêter pour voir si elles sont encore là, je me heurte à la réalité : elles ont été supprimées.</p> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from payment where payment_id between 17523 and 17532; payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+-------------- (0 rows)</pre> <p>La première chose à faire, en toute urgence, c&#8217;est de désactiver l&#8217;auto-vacuum. C&#8217;est votre pire ennemi dans notre scénario. C&#8217;est même la première chose à faire lorsque vous vous rendez compte que l&#8217;erreur a été commise :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter table payment set (autovacuum_enabled=false);</pre> <p>Si l&#8217;auto-vacuum est passé sur la table avant que vous n&#8217;ayez eu le temps de le désactiver, malheureusement, pg_dirtyread ne vous servira plus à rien. Une fois cela fait, on initialise l&#8217;extension :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# CREATE EXTENSION pg_dirtyread; CREATE EXTENSION</pre> <p>L&#8217;extension étant maintenant créée, on peut requêter les données perdues. La syntaxe d&#8217;utilisation exige que vous rappeliez le schéma de la table en question au moment de requêter les lignes supprimées. Vous pouvez choisir de ne pas inclure toutes les colonnes, ou même d&#8217;en ajouter, comme nous le verrons à la fin de l&#8217;article. Dans notre exemple, la requête pour consulter les données effacées sera la suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') AS t(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), payment_date timestamp without time zone); payment_id | customer_id | staff_id | rental_id | amount | payment_date ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 17524 | 345 | 2 | 1550 | 0.99 | 2007-02-16 00:27:01.996577 17525 | 345 | 2 | 2766 | 4.99 | 2007-02-19 16:13:41.996577 17526 | 346 | 1 | 1994 | 5.99 | 2007-02-17 09:35:32.996577 17527 | 346 | 2 | 3372 | 2.99 | 2007-02-21 12:02:45.996577 17528 | 346 | 1 | 3421 | 2.99 | 2007-02-21 15:51:24.996577 17529 | 347 | 2 | 1711 | 8.99 | 2007-02-16 12:40:18.996577 17530 | 347 | 2 | 2274 | 0.99 | 2007-02-18 04:59:41.996577 17531 | 347 | 1 | 3026 | 4.99 | 2007-02-20 10:16:26.996577 17532 | 347 | 1 | 3092 | 8.99 | 2007-02-20 14:33:08.996577 17533 | 347 | 1 | 3326 | 7.99 | 2007-02-21 07:33:16.996577 17534 | 348 | 1 | 1654 | 2.99 | 2007-02-16 08:11:14.996577 17535 | 348 | 1 | 2041 | 8.99 | 2007-02-17 12:47:26.996577 17536 | 348 | 2 | 2499 | 0.99 | 2007-02-18 21:30:02.996577</pre> <p>On voit bien apparaître en début de liste nos lignes qui sont censées ne plus exister. Cela nous permet éventuellement d&#8217;extraire les données qu&#8217;elles contiennent pour pouvoir les réinjecter ensuite avec un INSERT. On peut également filtrer les données pour ne rechercher que celles que l&#8217;on a supprimées :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') payment(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), dropped_6 timestamp without time zone) where payment_id = 17523; payment_id | customer_id | staff_id | rental_id | amount | dropped_6 ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 (1 row) </pre> <h2>Deuxième scénario : On a supprimé une colonne entière</h2> <p>Deuxième cas typique, sûrement plus rare, celui où une colonne entière est supprimée. Pour notre exemple, je vais supprimer la colonne contenant la date de paiement :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter table payment drop column payment_date; ALTER TABLE</pre> <p>pg_dirtyread nous permet de retrouver les informations de cette colonne à condition qu&#8217;aucun VACUUM FULL ou CLUSTER n&#8217;ait été exécuté. Pour ce faire, il faut connaître la position de la colonne dans l&#8217;ordre des colonnes de la table. Pour notre exemple, la colonne de date est la dernière colonne de la table, donc en position 6. Pour retrouver nos données, on peut donc exécuter la commande suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from pg_dirtyread('payment') payment(payment_id integer, customer_id smallint, staff_id smallint, rental_id integer, amount numeric(5,2), dropped_6 timestamp without time zone); payment_id | customer_id | staff_id | rental_id | amount | dropped_6 ------------+-------------+----------+-----------+--------+---------------------------- 17523 | 345 | 1 | 1457 | 4.99 | 2007-02-15 18:34:15.996577 17524 | 345 | 2 | 1550 | 0.99 | 2007-02-16 00:27:01.996577 17525 | 345 | 2 | 2766 | 4.99 | 2007-02-19 16:13:41.996577 17526 | 346 | 1 | 1994 | 5.99 | 2007-02-17 09:35:32.996577 17527 | 346 | 2 | 3372 | 2.99 | 2007-02-21 12:02:45.996577 </pre> <p>Le cas d&#8217;une restauration d&#8217;une colonne entière supprimée est compliqué à gérer. Il faudrait la recréer, puis modifier toutes les lignes une à une pour réajouter les valeurs de cette fameuse colonne. Cela peut s&#8217;avérer un peu complexe à réaliser. Cependant, si vous ne disposez pas d&#8217;une sauvegarde pg_dump, vous n&#8217;aurez peut-être pas d&#8217;autres alternatives que cela, à part recharger entièrement la base.</p> <h2>Bonus : Récupérer les informations système des lignes supprimées</h2> <p>Avec pg_dirtyread, il est possible de récupérer les informations système des lignes qui ont été supprimées. Pour ce faire, rien de plus simple : il suffit d&#8217;indiquer dans la requête les informations que vous souhaitez récupérer. De plus, il existe une colonne qui indique si la ligne est supposément supprimée ou non, ce qui pourrait être pratique pour trier les différentes lignes selon leur état :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT * FROM pg_dirtyread('payment') AS t(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean); tableoid | ctid | xmin | xmax | cmin | cmax | dead ----------+-----------+------+------+------+------+------ 16505 | (0,21) | 835 | 941 | 0 | 0 | f 16505 | (0,22) | 835 | 941 | 0 | 0 | f 16505 | (0,23) | 835 | 941 | 0 | 0 | f 16505 | (0,24) | 835 | 941 | 0 | 0 | f 16505 | (0,25) | 835 | 941 | 0 | 0 | f 16505 | (0,26) | 835 | 941 | 0 | 0 | f 16505 | (0,27) | 835 | 941 | 0 | 0 | f 16505 | (0,28) | 835 | 941 | 0 | 0 | f 16505 | (0,29) | 835 | 941 | 0 | 0 | f 16505 | (0,30) | 835 | 941 | 0 | 0 | f </pre> <h2>Conclusion :</h2> <p>En conclusion, l&#8217;extension pg_dirtyread offre une solution précieuse pour accéder aux données supprimées dans une base de données PostgreSQL. Son utilisation peut être cruciale dans des cas d&#8217;incidents critiques tels que la récupération de données perdues accidentellement ou la résolution d&#8217;incohérences de données. Cependant, il est essentiel de rappeler que son application reste extrêmement situationnelle et que pour qu&#8217;elle puisse être efficace, l&#8217;autovacuum doit être désactivé, ce qui n&#8217;est pas forcément recommandé.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/aws-rds-les-extensions-postgresql/" rel="bookmark" title="21 janvier 2020">AWS RDS : les extensions PostgreSQL</a> (Emmanuel RAMI) [AWSPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/" rel="bookmark" title="30 janvier 2024">Pseudonymiser vos bases PostgreSQL</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 3.145 ms --></p> <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%2F%3Fp%3D10494&#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%2F%3Fp%3D10494&#038;title=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage" 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=pg_dirtyread%20o%C3%B9%20comment%20r%C3%A9parer%20facilement%20un%20delete%20sauvage&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10494" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/">pg_dirtyread où comment réparer facilement un delete sauvage</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>Imaginez le scénario : nous sommes vendredi soir, en fin de journée, et pour une raison quelconque, un développeur qui a eu une semaine fatiguante se dit qu&#8217;il serait bien de supprimer les lignes qu&#8217;il a modifiées dans la base&#8230; <a href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/">pg_dirtyread où comment réparer facilement un delete sauvage</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> @@ -286,7 +286,7 @@ Pseudonymiser vos bases PostgreSQL 2024-01-30T17:54:28Z tag:blog.capdata.fr,2024-01-30:/index.php/pseudonymisation-postgresql/ - <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%2F%3Fp%3D10377&#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%2F%3Fp%3D10377&#038;title=Pseudonymiser%20vos%20bases%20PostgreSQL" 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=Pseudonymiser%20vos%20bases%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10377" 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>Dans l&#8217;ère numérique actuelle, où la sécurité des données occupe une place centrale, la pseudonymisation émerge comme une stratégie cruciale pour renforcer la confidentialité des informations stockées dans les bases de données. Cette approche, bien que semblable à l&#8217;anonymisation, se distingue par son objectif spécifique de préserver l&#8217;utilité des données tout en masquant l&#8217;identité réelle des individus. Dans le contexte de PostgreSQL, la pseudonymisation offre un équilibre délicat entre protection des renseignements sensibles et préservation de la fonctionnalité des données.</p> <h2>Principe de la Pseudonymisation :</h2> <p>La pseudonymisation implique la substitution des données réelles par des données fictives, mais conservant leur structure originale. Contrairement à l&#8217;anonymisation, qui supprime complètement toute référence à l&#8217;identité d&#8217;un individu, la pseudonymisation permet la réversibilité du processus. Ainsi, les données restent utilisables à des fins légitimes : l&#8217;analyse statistique, ou la réalisation de tests, tout en garantissant la protection des informations confidentielles.</p> <p>L&#8217;utilité fondamentale de la pseudonymisation réside dans sa capacité à concilier deux impératifs apparemment contradictoires : la protection de la vie privée des individus et la nécessité d&#8217;accéder et de traiter des données. Dans un paysage où les fuites de données et les violations de la vie privée sont de plus en plus fréquentes, la pseudonymisation devient une réponse pragmatique aux exigences de conformité réglementaire tout en préservant la valeur analytique des données.</p> <p>Souvent confondue avec la pseudonymisation, l&#8217;anonymisation diffère par son caractère irréversible. Alors que l&#8217;anonymisation supprime toute possibilité de relier des données à une identité spécifique, la pseudonymisation offre une réversibilité calculée, permettant une utilisation future des données tout en maintenant un niveau élevé de sécurité. Cette distinction cruciale souligne l&#8217;importance de choisir la méthode la plus appropriée en fonction des besoins spécifiques de sécurité et des objectifs opérationnels.</p> <h1>Etat de la pseudonymisation actuellement sur PostgreSQL :</h1> <p>Le seul outil actuellement disponible sur le marché permettant de réaliser une pseudonymisation sur PostgreSQL est une extension. Nous l&#8217;avions déjà évoquée lors d&#8217;un précédent article : PostgreSQL Anonymizer. Pour rappel, cette extension est développée par Dalibo, et contient également de nombreuses options d&#8217;anonymisation.</p> <p>Dans cet article, nous allons rappeler l&#8217;installation de cette extension puis nous la verrons à l&#8217;œuvre dans quelques exemples de pseudonymisation de données.</p> <h3>Etape 1 : Installation</h3> <p>La machine choisie pour mon test est une Ubuntu. Il n&#8217;y a pas de package prêt à l&#8217;emploi sur ce système d&#8217;exploitation. Nous devons donc réaliser nous même la compilation de l&#8217;extension pour qu&#8217;elle puisse fonctionner. Nous avons au préalable installé une version 15 de PostgreSQL sur notre machine.</p> <p>Nous commençons par installer les outils de développement de PostgreSQL :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~# sudo apt-get install postgresql-server-dev-15 Reading package lists... Done Building dependency tree Reading state information... Done ... Setting up postgresql-server-dev-15 (13.7-1.pgdg18.04+1) ... Processing triggers for man-db (2.8.3-2ubuntu0.1) ... Processing triggers for mime-support (3.60ubuntu1) ... Processing triggers for ureadahead (0.100.0-21) ... Processing triggers for install-info (6.5.0.dfsg.1-2) ... Processing triggers for libc-bin (2.27-3ubuntu1.5) ... Processing triggers for systemd (237-3ubuntu10.53)</pre> <p>Puis on récupère depuis le git de Dalibo les sources à la dernière version disponible :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~# git clone &lt;a href=&quot;https://gitlab.com/dalibo/postgresql_anonymizer.git&quot;&gt;https://gitlab.com/dalibo/postgresql_anonymizer.git&lt;/a&gt; Cloning into 'postgresql_anonymizer'... remote: Enumerating objects: 5145, done. remote: Counting objects: 100% (487/487), done. remote: Compressing objects: 100% (271/271), done. remote: Total 5145 (delta 327), reused 277 (delta 216), pack-reused 4658 Receiving objects: 100% (5145/5145), 25.71 MiB | 19.23 MiB/s, done. Resolving deltas: 100% (3304/3304), done. </pre> <p>Une fois les sources récupérées, on se positionne dans le répertoire créé par Git ou les sources ont été déposées et on fait un make extension :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~/postgresql_anonymizer# make extension  mkdir -p anon cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/ </pre> <p>Et enfin un Make install pour installer le tout. Il est important de préciser qu&#8217;il est nécessaire que vous ayez installé gcc pour pouvoir compiler les sources de l&#8217;extension (apt install gcc) :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~/postgresql_anonymizer# sudo make install  cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/ gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing- format-attribute -Wimplicit-fallthrough=3 -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno- format-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror= format-security -fno-omit-frame-pointer -fPIC -Wno-unused-variable -I. -I./ -I/usr/include/postgresql/13/server -I/usr/include/postgresql/internal   -Wdate- time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2    -c -o anon.o anon.c gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing- format-attribute -Wimplicit-fallthrough=3 -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno- format-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror= format-security -fno-omit-frame-pointer -fPIC -Wno-unused-variable anon.o -L/usr/lib/x86_64-linux-gnu -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -L/usr/lib/llvm-6.0/lib   -Wl,--as-needed  -shared -o anon.so /usr/bin/clang-6.0 -Wno-ignored-attributes -fno-strict-aliasing -fwrapv -Wno-unused- command-line-argument -O2  -I. -I./ -I/usr/include/postgresql/13/server -I/usr/include/postgresql/internal   -Wdate- time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -flto=thin -emit-llvm -c -o anon. bc anon.c /bin/mkdir -p  '/usr/share/postgresql/15/extension' /bin/mkdir -p  '/usr/share/postgresql/15/extension/anon' /bin/mkdir -p  '/usr/lib/postgresql/15/lib' install -d /usr/lib/postgresql/15/bin install -m 0755 bin/pg_dump_anon.sh /usr/lib/postgresql/15/bin /usr/bin/install -c -m 644 .//anon.control  '/usr/share/postgresql/15/extension/' /usr/bin/install -c -m 644 .//anon/*   '/usr/share/postgresql/15/extension/anon/' /usr/bin/install -c -m 755  anon.so  '/usr/lib/postgresql/15/lib/' /bin/mkdir -p  '/usr/lib/postgresql/15/lib/bitcode/anon' /bin/mkdir -p  '/usr/lib/postgresql/15/lib/bitcode'/anon/ /usr/bin/install -c -m 644 anon. bc '/usr/lib/postgresql/15/lib/bitcode'/anon/./ cd '/usr/lib/postgresql/15/lib/bitcode' &amp;&amp; /usr/lib/llvm-6.0/bin/llvm-lto -thinlto -thinlto-action=thinlink -o anon.index. bc anon/anon. bc</pre> <p>Pour ce test, j&#8217;ai importé la base de données exemple dvdrental de PostgreSQL. Elle me permettra d&#8217;illustrer la pseudonymisation facilement et sur une quantité respectable de données. Pour importer cette base de données, rien de plus simple :</p> <p>On la récupère en la téléchargeant sur le site d&#8217;hébergement :</p> <pre class="brush: bash; title: ; notranslate">postgres@sarah:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2023-11-29 13:42:52-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2023-11-29 13:42:52 (45.5 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>On la dézippe ensuite :</p> <pre class="brush: bash; title: ; notranslate">postgres@sarah:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar</pre> <p>On obtient ainsi une archive qu&#8217;on peut utiliser avec un pg_restore pour charger la base de données. Je me suis d&#8217;abbord connectée sur l&#8217;instance pour créer une base de données que j&#8217;ai appelé dvdrental:</p> <pre class="brush: bash; title: ; notranslate">postgres@ip-172-44-2-72:~$ psql psql (15.5 (Ubuntu 15.5-1.pgdg22.04+1)) Type &quot;help&quot; for help.</pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE</pre> <p>Puis j&#8217;ai restauré la base :</p> <pre class="brush: bash; title: ; notranslate">postgres@ip-172-44-2-72:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Nous sommes donc prêts à commencer.</p> <p>Il existe un petit nombre de fonctions de pseudonymisation dans PostgreSQL anonymiser. Elles fonctionnent comme les fonctions d&#8217;anonymisation et peuvent être utilisée en masquage statique ou en masquage dynamique.</p> <p>Les fonctions disponibles sont les suivantes :</p> <ul> <li><code>anon.pseudo_first_name('seed','salt')</code>qui retourne un prénom générique</li> <li><code>anon.pseudo_last_name('seed','salt')</code> qui retourne un nom de famille générique</li> <li><code>anon.pseudo_email('seed','salt')</code> qui retourne une adresse email générique et existante</li> <li><code>anon.pseudo_city('seed','salt')</code> qui retourne le nom d&#8217;une ville existante</li> <li><code>anon.pseudo_country('seed','salt')</code> qui retourne un pays existant</li> <li><code>anon.pseudo_company('seed','salt')</code> qui retourne un nom de société générique</li> <li><code>anon.pseudo_iban('seed','salt')</code> qui retourne un IBAN valide</li> <li><code>anon.pseudo_siret('seed','salt') qui retourne un SIRET valide</code></li> </ul> <p>Il est important de définir un <em>salt</em> différent pour chaque base de données. Si un utilisateur quelconque trouve le <em>salt</em> de votre base de données, il sera ensuite capable de procéder à une attaque par force brute sur toutes les autres données afin de les révéler.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter database dvdrental set anon.salt = 'This_is_a_salt';</pre> <p>Pour prendre un exemple, nous pouvons tenter de pseudonymiser les données des clients de notre base dvdrental :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+-------------------------------------+------------+------------+-------------+-------------------------+-------- 524 | 1 | Jared | Ely | jared.ely@sakilacustomer.org | 530 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 1 | 1 | Mary | Smith | mary.smith@sakilacustomer.org | 5 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 2 | 1 | Patricia | Johnson | patricia.johnson@sakilacustomer.org | 6 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 3 | 1 | Linda | Williams | linda.williams@sakilacustomer.org | 7 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 4 | 2 | Barbara | Jones | barbara.jones@sakilacustomer.org | 8 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 (5 rows)</pre> <p>Lors d&#8217;un premier essai, en suivant la documentation officielle disponible <a href="https://postgresql-anonymizer.readthedocs.io/en/stable/">ici</a>, j&#8217;ai utilisé la fonction suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name('seed', 'salt')'; SECURITY LABEL</pre> <p>Au moment d&#8217;utiliser le masquage pour pseudonymiser mes données, je suis tombée sur l&#8217;erreur suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name IS 'MASKED WITH FUNCTION anon.pseudo_first_name('seed','salt')'; ERROR: syntax error at or near &quot;seed&quot; LINE 2: IS 'MASKED WITH FUNCTION anon.pseudo_first_name('seed','salt...</pre> <p>Il faut le savoir, car ce n&#8217;est pas forcément bien explicité dans la documentation : le simple guillemet ne permet pas d&#8217;échapper correctement les caractères. Il est donc nécessaire d&#8217;ajouter d&#8217;autres caractères d&#8217;échappement. Dans ce cas, j&#8217;ai choisi d&#8217;utiliser le symbole &#8220;$&#8221;.<br /> Ainsi, au deuxième essai, cela m&#8217;a donné :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name($$seed$$, $$salt$$)'; SECURITY LABEL</pre> <p>Ca avait l&#8217;air de fonctionner, j&#8217;ai donc lancé mon anonymisation pour vérifier :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT anon.anonymize_database(); DEBUG: Anonymize table public.customer with first_name = anon.pseudo_first_name($$seed$$) ERROR: could not determine polymorphic type because input has type unknown CONTEXT: SQL statement &quot;UPDATE public.customer SET first_name = anon.pseudo_first_name($$seed$$)&quot; PL/pgSQL function anon.anonymize_table(regclass) line 38 at EXECUTE SQL function &quot;anonymize_database&quot; statement 1 </pre> <p>Il s&#8217;avère que la documentation ne précise pas qu&#8217;il faut typer les deux champs qu&#8217;on utilise pour notre fonction, sinon PostgreSQL ne sait pas quoi en faire. Cela donne donc :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name(CAST($$seed$$ as text), cast($$salt$$ as text))'; SECURITY LABEL</pre> <p>Et on applique ensuite les différentes fonctions :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT anon.anonymize_database(); DEBUG: Anonymize table public.customer with first_name = anon.pseudo_first_name(CAST($$seed$$ as text), cast($$salt$$ as text)) anonymize_database -------------------- t (1 row)</pre> <p>On va ensuite vérifier nos données dans la table pour voir si cela a été appliqué :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+-----------------------------------+------------+------------+-------------+----------------------------+-------- 524 | 1 | Taniya | Ely | jared.ely@sakilacustomer.org | 530 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 15 | 1 | Taniya | Harris | helen.harris@sakilacustomer.org | 19 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 16 | 2 | Taniya | Martin | sandra.martin@sakilacustomer.org | 20 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 0 17 | 1 | Taniya | Thompson | donna.thompson@sakilacustomer.org | 21 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 18 | 2 | Taniya | Garcia | carol.garcia@sakilacustomer.org | 22 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 (5 rows)</pre> <p>On se rend alors compte que toutes les données pseudonymisées l&#8217;ont été avec le même résultat.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select distinct first_name from customer; first_name ------------ Taniya (1 row)</pre> <p>Il est en effet précisé dans la documentation que si on voulait obtenir des pseudo différents pour chaque ligne, il fallait ajouter un custom dataset d&#8217;un nombre supérieur de ligne au nombres d&#8217;entrées dans notre table.</p> <p>Il existe un dataset fournit par dalibo (en français uniquement), disponible à l&#8217;adresse <a href="https://gitlab.com/dalibo/postgresql_anonymizer/-/packages">suivante </a>.</p> <p>Pour pouvoir l&#8217;intégrer dans votre extension, il vous faut le télécharger sur votre serveur, le placer dans le dossier de votre choix, et utiliser la commande :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=#SELECT anon.init('/path/to/custom_csv_files/')</pre> <p>Vous pouvez également créer votre propre dataset sous la forme d&#8217;un fichier csv avec un script par exemple, pour peupler vos exemples avec vos propres données personnalisées.</p> <p>On peut tenter de réaliser la même opération mais pour une autre donnée. On voit en effet que notre adresse email contient toujours les noms de familles des personnes, ils n&#8217;ont pas été pseudonymisés. On va donc changer cela :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# security label for anon on column customer.email is 'MASKED WITH FUNCTION anon.pseudo_email(CAST($$seed$$ as text), cast($$salt$$ as text))'; SECURITY LABEL </pre> <p>Et une fois appliqué on obtient les données suivantes :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+----------------------------+------------+------------+-------------+----------------------------+-------- 524 | 1 | Taniya | Ely | rowesally@kelly-dorsey.com | 530 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 235 | 1 | Taniya | Lynch | rowesally@kelly-dorsey.com | 239 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 15 | 1 | Taniya | Harris | rowesally@kelly-dorsey.com | 19 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 16 | 2 | Taniya | Martin | rowesally@kelly-dorsey.com | 20 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 0 17 | 1 | Taniya | Thompson | rowesally@kelly-dorsey.com | 21 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 (5 rows)</pre> <h2>Les limites de la pseudonymisation Postgres :</h2> <p>Actuellement il n&#8217;existe qu&#8217;une seule extension permettant de faire de la pseudonymisation sur PostgreSQL. Et elle présente plusieurs limites :</p> <ol> <li>Les fonctions de pseudonymisation de postgresql anonymizer sont déterministes. C&#8217;est à dire que pour deux valeurs identiques, elles donneront toujours le même résultat.</li> <li>Les données pseudonymisée restent des données personnes ! Le <strong>RGPD</strong> précise très clairement que : « les données à caractère personnel qui ont fait l’objet d’une pseudonymisation […] devraient être considérées comme des informations concernant une personne physique identifiable. »</li> <li>Les coûts en performance, comme évoqué dans mon précédent article pour cette même extension, sont élevés. C&#8217;est quelque chose à considérer quand on veut utiliser cette méthode.</li> <li>Il faut pouvoir gérer correctement les clés qu&#8217;on utilises pour la pseudonymisation, c&#8217;est une charge supplémentaire.</li> <li>La documentation n&#8217;est pas forcément tout à fait bien mise à jour, et certaines choses ne sont pas évidentes à appréhender.</li> <li>L&#8217;extension est toujours en développement et en changements constants, nous ne sommes pas à l&#8217;abris de rencontrer des bugs au fil des versions. Pour écrire cet article, j&#8217;ai du contacter directement le développeur car au début de sa rédaction, les fonctions de pseudonymisation ne fonctionnaient pas.</li> </ol> <h2>Conclusion :</h2> <p>En conclusion, la pseudonymisation des données dans PostgreSQL se révèle être une stratégie de protection de la vie privée et de conformité réglementaire particulièrement robuste. En adoptant cette approche, les entreprises peuvent tirer parti des avantages significatifs tels que la préservation de la confidentialité des données tout en permettant l&#8217;analyse et le traitement des informations sensibles.</p> <p>D&#8217;un côté, la pseudonymisation offre une solution efficace pour équilibrer la nécessité d&#8217;accéder aux données avec le respect des réglementations de confidentialité. Les données pseudonymisées demeurent utiles pour les analyses internes tout en limitant le risque d&#8217;exploitation malveillante.</p> <p>Cependant, il est crucial de reconnaître que la pseudonymisation n&#8217;est pas une panacée. Elle peut présenter des défis en termes de gestion des clés de pseudonymisation, de complexité accrue dans la maintenance des bases de données, et de potentielles vulnérabilités si elle est mal mise en œuvre.</p> <p>Dans une perspective plus large, il est également pertinent de considérer l&#8217;anonymisation des données comme une alternative. Bien que l&#8217;anonymisation puisse offrir un niveau supérieur de protection, elle peut également rendre les données moins utiles pour certaines applications, limitant ainsi leur valeur pour les analyses internes.</p> <p>En définitive, le choix entre la pseudonymisation et l&#8217;anonymisation dépend des besoins spécifiques de chaque organisation, du contexte réglementaire et des compromis acceptables entre la protection de la vie privée et l&#8217;utilité des données. En élaborant une stratégie de gestion des données judicieuse, les entreprises peuvent naviguer avec succès dans le paysage complexe de la confidentialité des données dans PostgreSQL.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/postgresql-anonymizer/" rel="bookmark" title="7 juillet 2022">PostgreSQL Anonymizer</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/" rel="bookmark" title="27 mars 2024">pg_dirtyread où comment réparer facilement un delete sauvage</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/hypopg-et-les-index-hypothetiques/" rel="bookmark" title="5 août 2022">HypoPG et les index hypothétiques</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 3.129 ms --></p> <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%2F%3Fp%3D10377&#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%2F%3Fp%3D10377&#038;title=Pseudonymiser%20vos%20bases%20PostgreSQL" 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=Pseudonymiser%20vos%20bases%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10377" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/">Pseudonymiser vos bases PostgreSQL</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10377&#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%2F%3Fp%3D10377&#038;title=Pseudonymiser%20vos%20bases%20PostgreSQL" 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=Pseudonymiser%20vos%20bases%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10377" 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>Dans l&#8217;ère numérique actuelle, où la sécurité des données occupe une place centrale, la pseudonymisation émerge comme une stratégie cruciale pour renforcer la confidentialité des informations stockées dans les bases de données. Cette approche, bien que semblable à l&#8217;anonymisation, se distingue par son objectif spécifique de préserver l&#8217;utilité des données tout en masquant l&#8217;identité réelle des individus. Dans le contexte de PostgreSQL, la pseudonymisation offre un équilibre délicat entre protection des renseignements sensibles et préservation de la fonctionnalité des données.</p> <h2>Principe de la Pseudonymisation :</h2> <p>La pseudonymisation implique la substitution des données réelles par des données fictives, mais conservant leur structure originale. Contrairement à l&#8217;anonymisation, qui supprime complètement toute référence à l&#8217;identité d&#8217;un individu, la pseudonymisation permet la réversibilité du processus. Ainsi, les données restent utilisables à des fins légitimes : l&#8217;analyse statistique, ou la réalisation de tests, tout en garantissant la protection des informations confidentielles.</p> <p>L&#8217;utilité fondamentale de la pseudonymisation réside dans sa capacité à concilier deux impératifs apparemment contradictoires : la protection de la vie privée des individus et la nécessité d&#8217;accéder et de traiter des données. Dans un paysage où les fuites de données et les violations de la vie privée sont de plus en plus fréquentes, la pseudonymisation devient une réponse pragmatique aux exigences de conformité réglementaire tout en préservant la valeur analytique des données.</p> <p>Souvent confondue avec la pseudonymisation, l&#8217;anonymisation diffère par son caractère irréversible. Alors que l&#8217;anonymisation supprime toute possibilité de relier des données à une identité spécifique, la pseudonymisation offre une réversibilité calculée, permettant une utilisation future des données tout en maintenant un niveau élevé de sécurité. Cette distinction cruciale souligne l&#8217;importance de choisir la méthode la plus appropriée en fonction des besoins spécifiques de sécurité et des objectifs opérationnels.</p> <h1>Etat de la pseudonymisation actuellement sur PostgreSQL :</h1> <p>Le seul outil actuellement disponible sur le marché permettant de réaliser une pseudonymisation sur PostgreSQL est une extension. Nous l&#8217;avions déjà évoquée lors d&#8217;un précédent article : PostgreSQL Anonymizer. Pour rappel, cette extension est développée par Dalibo, et contient également de nombreuses options d&#8217;anonymisation.</p> <p>Dans cet article, nous allons rappeler l&#8217;installation de cette extension puis nous la verrons à l&#8217;œuvre dans quelques exemples de pseudonymisation de données.</p> <h3>Etape 1 : Installation</h3> <p>La machine choisie pour mon test est une Ubuntu. Il n&#8217;y a pas de package prêt à l&#8217;emploi sur ce système d&#8217;exploitation. Nous devons donc réaliser nous même la compilation de l&#8217;extension pour qu&#8217;elle puisse fonctionner. Nous avons au préalable installé une version 15 de PostgreSQL sur notre machine.</p> <p>Nous commençons par installer les outils de développement de PostgreSQL :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~# sudo apt-get install postgresql-server-dev-15 Reading package lists... Done Building dependency tree Reading state information... Done ... Setting up postgresql-server-dev-15 (13.7-1.pgdg18.04+1) ... Processing triggers for man-db (2.8.3-2ubuntu0.1) ... Processing triggers for mime-support (3.60ubuntu1) ... Processing triggers for ureadahead (0.100.0-21) ... Processing triggers for install-info (6.5.0.dfsg.1-2) ... Processing triggers for libc-bin (2.27-3ubuntu1.5) ... Processing triggers for systemd (237-3ubuntu10.53)</pre> <p>Puis on récupère depuis le git de Dalibo les sources à la dernière version disponible :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~# git clone &lt;a href=&quot;https://gitlab.com/dalibo/postgresql_anonymizer.git&quot;&gt;https://gitlab.com/dalibo/postgresql_anonymizer.git&lt;/a&gt; Cloning into 'postgresql_anonymizer'... remote: Enumerating objects: 5145, done. remote: Counting objects: 100% (487/487), done. remote: Compressing objects: 100% (271/271), done. remote: Total 5145 (delta 327), reused 277 (delta 216), pack-reused 4658 Receiving objects: 100% (5145/5145), 25.71 MiB | 19.23 MiB/s, done. Resolving deltas: 100% (3304/3304), done. </pre> <p>Une fois les sources récupérées, on se positionne dans le répertoire créé par Git ou les sources ont été déposées et on fait un make extension :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~/postgresql_anonymizer# make extension  mkdir -p anon cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/ </pre> <p>Et enfin un Make install pour installer le tout. Il est important de préciser qu&#8217;il est nécessaire que vous ayez installé gcc pour pouvoir compiler les sources de l&#8217;extension (apt install gcc) :</p> <pre class="brush: bash; title: ; notranslate">root@sarah:~/postgresql_anonymizer# sudo make install  cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/ gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing- format-attribute -Wimplicit-fallthrough=3 -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno- format-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror= format-security -fno-omit-frame-pointer -fPIC -Wno-unused-variable -I. -I./ -I/usr/include/postgresql/13/server -I/usr/include/postgresql/internal   -Wdate- time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2    -c -o anon.o anon.c gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing- format-attribute -Wimplicit-fallthrough=3 -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno- format-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror= format-security -fno-omit-frame-pointer -fPIC -Wno-unused-variable anon.o -L/usr/lib/x86_64-linux-gnu -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -L/usr/lib/llvm-6.0/lib   -Wl,--as-needed  -shared -o anon.so /usr/bin/clang-6.0 -Wno-ignored-attributes -fno-strict-aliasing -fwrapv -Wno-unused- command-line-argument -O2  -I. -I./ -I/usr/include/postgresql/13/server -I/usr/include/postgresql/internal   -Wdate- time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -flto=thin -emit-llvm -c -o anon. bc anon.c /bin/mkdir -p  '/usr/share/postgresql/15/extension' /bin/mkdir -p  '/usr/share/postgresql/15/extension/anon' /bin/mkdir -p  '/usr/lib/postgresql/15/lib' install -d /usr/lib/postgresql/15/bin install -m 0755 bin/pg_dump_anon.sh /usr/lib/postgresql/15/bin /usr/bin/install -c -m 644 .//anon.control  '/usr/share/postgresql/15/extension/' /usr/bin/install -c -m 644 .//anon/*   '/usr/share/postgresql/15/extension/anon/' /usr/bin/install -c -m 755  anon.so  '/usr/lib/postgresql/15/lib/' /bin/mkdir -p  '/usr/lib/postgresql/15/lib/bitcode/anon' /bin/mkdir -p  '/usr/lib/postgresql/15/lib/bitcode'/anon/ /usr/bin/install -c -m 644 anon. bc '/usr/lib/postgresql/15/lib/bitcode'/anon/./ cd '/usr/lib/postgresql/15/lib/bitcode' &amp;&amp; /usr/lib/llvm-6.0/bin/llvm-lto -thinlto -thinlto-action=thinlink -o anon.index. bc anon/anon. bc</pre> <p>Pour ce test, j&#8217;ai importé la base de données exemple dvdrental de PostgreSQL. Elle me permettra d&#8217;illustrer la pseudonymisation facilement et sur une quantité respectable de données. Pour importer cette base de données, rien de plus simple :</p> <p>On la récupère en la téléchargeant sur le site d&#8217;hébergement :</p> <pre class="brush: bash; title: ; notranslate">postgres@sarah:~$ wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip --2023-11-29 13:42:52-- https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip Resolving www.postgresqltutorial.com (www.postgresqltutorial.com)... 104.21.2.174, 172.67.129.129, 2606:4700:3037::6815:2ae, ... Connecting to www.postgresqltutorial.com (www.postgresqltutorial.com)|104.21.2.174|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 550906 (538K) [application/zip] Saving to: ‘dvdrental.zip’ dvdrental.zip 100%[========================================================================================================================================&gt;] 537.99K --.-KB/s in 0.01s 2023-11-29 13:42:52 (45.5 MB/s) - ‘dvdrental.zip’ saved [550906/550906] </pre> <p>On la dézippe ensuite :</p> <pre class="brush: bash; title: ; notranslate">postgres@sarah:~$ unzip dvdrental.zip Archive: dvdrental.zip inflating: dvdrental.tar</pre> <p>On obtient ainsi une archive qu&#8217;on peut utiliser avec un pg_restore pour charger la base de données. Je me suis d&#8217;abbord connectée sur l&#8217;instance pour créer une base de données que j&#8217;ai appelé dvdrental:</p> <pre class="brush: bash; title: ; notranslate">postgres@ip-172-44-2-72:~$ psql psql (15.5 (Ubuntu 15.5-1.pgdg22.04+1)) Type &quot;help&quot; for help.</pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database dvdrental; CREATE DATABASE</pre> <p>Puis j&#8217;ai restauré la base :</p> <pre class="brush: bash; title: ; notranslate">postgres@ip-172-44-2-72:~$ pg_restore -U postgres -d dvdrental dvdrental.tar</pre> <p>Nous sommes donc prêts à commencer.</p> <p>Il existe un petit nombre de fonctions de pseudonymisation dans PostgreSQL anonymiser. Elles fonctionnent comme les fonctions d&#8217;anonymisation et peuvent être utilisée en masquage statique ou en masquage dynamique.</p> <p>Les fonctions disponibles sont les suivantes :</p> <ul> <li><code>anon.pseudo_first_name('seed','salt')</code>qui retourne un prénom générique</li> <li><code>anon.pseudo_last_name('seed','salt')</code> qui retourne un nom de famille générique</li> <li><code>anon.pseudo_email('seed','salt')</code> qui retourne une adresse email générique et existante</li> <li><code>anon.pseudo_city('seed','salt')</code> qui retourne le nom d&#8217;une ville existante</li> <li><code>anon.pseudo_country('seed','salt')</code> qui retourne un pays existant</li> <li><code>anon.pseudo_company('seed','salt')</code> qui retourne un nom de société générique</li> <li><code>anon.pseudo_iban('seed','salt')</code> qui retourne un IBAN valide</li> <li><code>anon.pseudo_siret('seed','salt') qui retourne un SIRET valide</code></li> </ul> <p>Il est important de définir un <em>salt</em> différent pour chaque base de données. Si un utilisateur quelconque trouve le <em>salt</em> de votre base de données, il sera ensuite capable de procéder à une attaque par force brute sur toutes les autres données afin de les révéler.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# alter database dvdrental set anon.salt = 'This_is_a_salt';</pre> <p>Pour prendre un exemple, nous pouvons tenter de pseudonymiser les données des clients de notre base dvdrental :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+-------------------------------------+------------+------------+-------------+-------------------------+-------- 524 | 1 | Jared | Ely | jared.ely@sakilacustomer.org | 530 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 1 | 1 | Mary | Smith | mary.smith@sakilacustomer.org | 5 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 2 | 1 | Patricia | Johnson | patricia.johnson@sakilacustomer.org | 6 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 3 | 1 | Linda | Williams | linda.williams@sakilacustomer.org | 7 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 4 | 2 | Barbara | Jones | barbara.jones@sakilacustomer.org | 8 | t | 2006-02-14 | 2013-05-26 14:49:45.738 | 1 (5 rows)</pre> <p>Lors d&#8217;un premier essai, en suivant la documentation officielle disponible <a href="https://postgresql-anonymizer.readthedocs.io/en/stable/">ici</a>, j&#8217;ai utilisé la fonction suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name('seed', 'salt')'; SECURITY LABEL</pre> <p>Au moment d&#8217;utiliser le masquage pour pseudonymiser mes données, je suis tombée sur l&#8217;erreur suivante :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name IS 'MASKED WITH FUNCTION anon.pseudo_first_name('seed','salt')'; ERROR: syntax error at or near &quot;seed&quot; LINE 2: IS 'MASKED WITH FUNCTION anon.pseudo_first_name('seed','salt...</pre> <p>Il faut le savoir, car ce n&#8217;est pas forcément bien explicité dans la documentation : le simple guillemet ne permet pas d&#8217;échapper correctement les caractères. Il est donc nécessaire d&#8217;ajouter d&#8217;autres caractères d&#8217;échappement. Dans ce cas, j&#8217;ai choisi d&#8217;utiliser le symbole &#8220;$&#8221;.<br /> Ainsi, au deuxième essai, cela m&#8217;a donné :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name($$seed$$, $$salt$$)'; SECURITY LABEL</pre> <p>Ca avait l&#8217;air de fonctionner, j&#8217;ai donc lancé mon anonymisation pour vérifier :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT anon.anonymize_database(); DEBUG: Anonymize table public.customer with first_name = anon.pseudo_first_name($$seed$$) ERROR: could not determine polymorphic type because input has type unknown CONTEXT: SQL statement &quot;UPDATE public.customer SET first_name = anon.pseudo_first_name($$seed$$)&quot; PL/pgSQL function anon.anonymize_table(regclass) line 38 at EXECUTE SQL function &quot;anonymize_database&quot; statement 1 </pre> <p>Il s&#8217;avère que la documentation ne précise pas qu&#8217;il faut typer les deux champs qu&#8217;on utilise pour notre fonction, sinon PostgreSQL ne sait pas quoi en faire. Cela donne donc :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SECURITY LABEL FOR anon ON COLUMN customer.first_name is 'MASKED WITH FUNCTION anon.pseudo_first_name(CAST($$seed$$ as text), cast($$salt$$ as text))'; SECURITY LABEL</pre> <p>Et on applique ensuite les différentes fonctions :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# SELECT anon.anonymize_database(); DEBUG: Anonymize table public.customer with first_name = anon.pseudo_first_name(CAST($$seed$$ as text), cast($$salt$$ as text)) anonymize_database -------------------- t (1 row)</pre> <p>On va ensuite vérifier nos données dans la table pour voir si cela a été appliqué :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+-----------------------------------+------------+------------+-------------+----------------------------+-------- 524 | 1 | Taniya | Ely | jared.ely@sakilacustomer.org | 530 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 15 | 1 | Taniya | Harris | helen.harris@sakilacustomer.org | 19 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 16 | 2 | Taniya | Martin | sandra.martin@sakilacustomer.org | 20 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 0 17 | 1 | Taniya | Thompson | donna.thompson@sakilacustomer.org | 21 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 18 | 2 | Taniya | Garcia | carol.garcia@sakilacustomer.org | 22 | t | 2006-02-14 | 2024-01-29 09:26:48.268084 | 1 (5 rows)</pre> <p>On se rend alors compte que toutes les données pseudonymisées l&#8217;ont été avec le même résultat.</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select distinct first_name from customer; first_name ------------ Taniya (1 row)</pre> <p>Il est en effet précisé dans la documentation que si on voulait obtenir des pseudo différents pour chaque ligne, il fallait ajouter un custom dataset d&#8217;un nombre supérieur de ligne au nombres d&#8217;entrées dans notre table.</p> <p>Il existe un dataset fournit par dalibo (en français uniquement), disponible à l&#8217;adresse <a href="https://gitlab.com/dalibo/postgresql_anonymizer/-/packages">suivante </a>.</p> <p>Pour pouvoir l&#8217;intégrer dans votre extension, il vous faut le télécharger sur votre serveur, le placer dans le dossier de votre choix, et utiliser la commande :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=#SELECT anon.init('/path/to/custom_csv_files/')</pre> <p>Vous pouvez également créer votre propre dataset sous la forme d&#8217;un fichier csv avec un script par exemple, pour peupler vos exemples avec vos propres données personnalisées.</p> <p>On peut tenter de réaliser la même opération mais pour une autre donnée. On voit en effet que notre adresse email contient toujours les noms de familles des personnes, ils n&#8217;ont pas été pseudonymisés. On va donc changer cela :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# security label for anon on column customer.email is 'MASKED WITH FUNCTION anon.pseudo_email(CAST($$seed$$ as text), cast($$salt$$ as text))'; SECURITY LABEL </pre> <p>Et une fois appliqué on obtient les données suivantes :</p> <pre class="brush: sql; title: ; notranslate">dvdrental=# select * from customer limit 5; customer_id | store_id | first_name | last_name | email | address_id | activebool | create_date | last_update | active -------------+----------+------------+-----------+----------------------------+------------+------------+-------------+----------------------------+-------- 524 | 1 | Taniya | Ely | rowesally@kelly-dorsey.com | 530 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 235 | 1 | Taniya | Lynch | rowesally@kelly-dorsey.com | 239 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 15 | 1 | Taniya | Harris | rowesally@kelly-dorsey.com | 19 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 16 | 2 | Taniya | Martin | rowesally@kelly-dorsey.com | 20 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 0 17 | 1 | Taniya | Thompson | rowesally@kelly-dorsey.com | 21 | t | 2006-02-14 | 2024-01-29 09:42:03.560633 | 1 (5 rows)</pre> <h2>Les limites de la pseudonymisation Postgres :</h2> <p>Actuellement il n&#8217;existe qu&#8217;une seule extension permettant de faire de la pseudonymisation sur PostgreSQL. Et elle présente plusieurs limites :</p> <ol> <li>Les fonctions de pseudonymisation de postgresql anonymizer sont déterministes. C&#8217;est à dire que pour deux valeurs identiques, elles donneront toujours le même résultat.</li> <li>Les données pseudonymisée restent des données personnes ! Le <strong>RGPD</strong> précise très clairement que : « les données à caractère personnel qui ont fait l’objet d’une pseudonymisation […] devraient être considérées comme des informations concernant une personne physique identifiable. »</li> <li>Les coûts en performance, comme évoqué dans mon précédent article pour cette même extension, sont élevés. C&#8217;est quelque chose à considérer quand on veut utiliser cette méthode.</li> <li>Il faut pouvoir gérer correctement les clés qu&#8217;on utilises pour la pseudonymisation, c&#8217;est une charge supplémentaire.</li> <li>La documentation n&#8217;est pas forcément tout à fait bien mise à jour, et certaines choses ne sont pas évidentes à appréhender.</li> <li>L&#8217;extension est toujours en développement et en changements constants, nous ne sommes pas à l&#8217;abris de rencontrer des bugs au fil des versions. Pour écrire cet article, j&#8217;ai du contacter directement le développeur car au début de sa rédaction, les fonctions de pseudonymisation ne fonctionnaient pas.</li> </ol> <h2>Conclusion :</h2> <p>En conclusion, la pseudonymisation des données dans PostgreSQL se révèle être une stratégie de protection de la vie privée et de conformité réglementaire particulièrement robuste. En adoptant cette approche, les entreprises peuvent tirer parti des avantages significatifs tels que la préservation de la confidentialité des données tout en permettant l&#8217;analyse et le traitement des informations sensibles.</p> <p>D&#8217;un côté, la pseudonymisation offre une solution efficace pour équilibrer la nécessité d&#8217;accéder aux données avec le respect des réglementations de confidentialité. Les données pseudonymisées demeurent utiles pour les analyses internes tout en limitant le risque d&#8217;exploitation malveillante.</p> <p>Cependant, il est crucial de reconnaître que la pseudonymisation n&#8217;est pas une panacée. Elle peut présenter des défis en termes de gestion des clés de pseudonymisation, de complexité accrue dans la maintenance des bases de données, et de potentielles vulnérabilités si elle est mal mise en œuvre.</p> <p>Dans une perspective plus large, il est également pertinent de considérer l&#8217;anonymisation des données comme une alternative. Bien que l&#8217;anonymisation puisse offrir un niveau supérieur de protection, elle peut également rendre les données moins utiles pour certaines applications, limitant ainsi leur valeur pour les analyses internes.</p> <p>En définitive, le choix entre la pseudonymisation et l&#8217;anonymisation dépend des besoins spécifiques de chaque organisation, du contexte réglementaire et des compromis acceptables entre la protection de la vie privée et l&#8217;utilité des données. En élaborant une stratégie de gestion des données judicieuse, les entreprises peuvent naviguer avec succès dans le paysage complexe de la confidentialité des données dans PostgreSQL.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/postgresql-anonymizer/" rel="bookmark" title="7 juillet 2022">PostgreSQL Anonymizer</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/" rel="bookmark" title="27 mars 2024">pg_dirtyread où comment réparer facilement un delete sauvage</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/postgresql-planifier-une-tache-avec-pg_cron/" rel="bookmark" title="24 septembre 2019">PostgreSQL : planifier une tâche avec pg_cron</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/hypopg-et-les-index-hypothetiques/" rel="bookmark" title="5 août 2022">HypoPG et les index hypothétiques</a> (Sarah FAVEERE) [PostgreSQL]</li> </ul> <p><!-- Similar Posts took 2.552 ms --></p> <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%2F%3Fp%3D10377&#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%2F%3Fp%3D10377&#038;title=Pseudonymiser%20vos%20bases%20PostgreSQL" 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=Pseudonymiser%20vos%20bases%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10377" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/">Pseudonymiser vos bases PostgreSQL</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>Dans l&#8217;ère numérique actuelle, où la sécurité des données occupe une place centrale, la pseudonymisation émerge comme une stratégie cruciale pour renforcer la confidentialité des informations stockées dans les bases de données. Cette approche, bien que semblable à l&#8217;anonymisation, se&#8230; <a href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/pseudonymisation-postgresql/">Pseudonymiser vos bases PostgreSQL</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> @@ -297,7 +297,7 @@ OrioleDB : la promesse d’un No-Vacuum 2023-10-11T10:27:27Z tag:blog.capdata.fr,2023-10-11:/index.php/orioledb-la-promesse-dun-no-vacuum/ - <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%2F%3Fp%3D10248&#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%2F%3Fp%3D10248&#038;title=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum" 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=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10248" 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>La gestion efficace de l&#8217;espace disque dans PostgreSQL est une préoccupation constante pour les administrateurs de bases de données. C&#8217;est là qu&#8217;intervient le processus de &#8220;vacuum&#8221;, une fonctionnalité puissante et complexe qui peut jouer un rôle déterminant dans la performance et la stabilité de votre base de données PostgreSQL. Cependant, comme toute arme à double tranchant, le vacuum apporte à la fois des avantages considérables et des défis potentiels.</p> <p>Le principal reproche que l&#8217;on peut adresser au système de vacuum (et à l&#8217;auto-vacuum par extension) est sa consommation importante de ressources. C&#8217;est l&#8217;une des raisons majeures évoquées par les grandes entreprises qui optent pour un autre SGBD, <a href="https://www.uber.com/blog/postgres-to-mysql-migration/">comme Uber, qui a migré vers MySQL pour remplacer PostgreSQL</a>.</p> <p><a href="https://github.com/orioledb/orioledb">OrioleDB </a>nous propose une approche novatrice de la gestion de la fragmentation (bloat) et de l&#8217;espace disque de vos tables PostgreSQL. La principale promesse de ce nouveau moteur développé par la société Oriole est d&#8217;éliminer le besoin de vacuum régulier et de longues périodes de maintenance pour nos bases de données.</p> <p>Cette innovation s&#8217;articule autour de journaux d&#8217;annulation au niveau de la ligne et du bloc, ainsi que de la fusion automatique des pages. Les journaux d&#8217;annulation au niveau des lignes et des blocs offrent un contrôle plus précis, permettant une gestion plus efficace des modifications de données. La fonction de fusion automatique des pages travaille en arrière-plan pour regrouper les données fragmentées, améliorant ainsi encore davantage l&#8217;efficacité du système. Le moteur est actuellement disponible en version bêta sur le site d&#8217;OrioleDB.</p> <p>Dans cet article, je vais vous montrer comment installer OrioleDB et réaliser un test comparatif entre une instance classique et une instance OrioleDB en termes d&#8217;espace disque occupé. Je vais utiliser <a href="https://capdata.fr/dbadistance">notre agent AllDB de supervision des bases de données </a>pour générer des graphiques de la consommation d&#8217;espace disque.</p> <h2>Installation d&#8217;OrioleDB :</h2> <p>Pour pouvoir tester le nouveau moteur, il nous faut l&#8217;installer. Pour cela, deux solutions s&#8217;offrent à nous. On peut choisir de le compiler nous-mêmes à partir du code source fourni par Oriole, ou on peut choisir d&#8217;utiliser le conteneur Docker mis à notre disposition.</p> <p>Pour des raisons de rapidité de mise en place, j&#8217;ai choisi d&#8217;utiliser le conteneur fourni.</p> <p>Pour commencer, j&#8217;installe Docker sur ma machine.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~$ sudo apt-get update (...) Reading package lists... Done root@ip-172-44-2-190:~$ sudo apt-get install ca-certificates curl gnupg Reading package lists... Done (...) No VM guests are running outdated hypervisor (qemu) binaries on this host. root@ip-172-44-2-190:~$ sudo install -m 0755 -d /etc/apt/keyrings root@ip-172-44-2-190:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg root@ip-172-44-2-190:~$ sudo chmod a+r /etc/apt/keyrings/docker.gpg root@ip-172-44-2-190:~$ echo \ &quot;deb [arch=&quot;$(dpkg --print-architecture)&quot; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ &quot;$(. /etc/os-release &amp;amp;&amp;amp; echo &quot;$VERSION_CODENAME&quot;)&quot; stable&quot; | \ sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null sudo apt-get update Hit:1 http://eu-west-3.ec2.archive.ubuntu.com/ubuntu jammy InRelease Hit:2 http://eu-west-3.ec2.archive.ubuntu.com/ubuntu jammy-updates InRelease (...) No VM guests are running outdated hypervisor (qemu) binaries on this host.</pre> <p>Pour tester que mon installation fonctionne correctement, je peux démarrer l&#8217;image hello-world par défaut de Docker.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~$ sudo docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 719385e32844: Pull complete Digest: sha256:4f53e2564790c8e7856ec08e384732aa38dc43c52f02952483e3f003afbf23db Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/</pre> <p>Docker est correctement installé, nous pouvons à présent aller récupérer l&#8217;image Oriole depuis les dépôts (repositories). J&#8217;ai choisi de récupérer l&#8217;image &#8220;latest&#8221; de la version 14, avec laquelle je vais effectuer mes tests.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker pull orioledb/orioledb:latest-pg14 latest-pg14: Pulling from orioledb/orioledb 7264a8db6415: Pull complete 4e0888d8a332: Pull complete 4b0510894ae7: Pull complete dc5da57566a1: Pull complete 9611602787a1: Pull complete 8f68f9452236: Pull complete 5c92ed25d025: Pull complete 11bb162cfe7a: Pull complete 6f375d990892: Pull complete 6ac166496e68: Pull complete Digest: sha256:4d408916e929ff0bcea11fee91ca5e69b0929be1afa58b5dbbcddcce340089ef Status: Downloaded newer image for orioledb/orioledb:latest-pg14 docker.io/orioledb/orioledb:latest-pg14</pre> <p>On vérifie que notre image est bien présente, et on l&#8217;éxecute :</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker image ls --all REPOSITORY TAG IMAGE ID CREATED SIZE orioledb/orioledb latest-pg14 cd7f629f705e 10 days ago 270MB hello-world latest 9c7a54a9a43c 4 months ago 13.3kB </pre> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker run -e POSTGRES_PASSWORD=password cd7f629f705e The files belonging to this database system will be owned by user &quot;postgres&quot;. This user must also own the server process. The database cluster will be initialized with locale &quot;en_US.utf8&quot;. The default database encoding has accordingly been set to &quot;UTF8&quot;. The default text search configuration will be set to &quot;english&quot;. Data page checksums are disabled. fixing permissions on existing directory /var/lib/postgresql/data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting default time zone ... UTC creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... sh: locale: not found 2023-09-21 09:51:08.177 UTC [30] WARNING: no usable system locales were found ok syncing data to disk ... ok Success. You can now start the database server using: pg_ctl -D /var/lib/postgresql/data -l logfile start initdb: warning: enabling &quot;trust&quot; authentication for local connections You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. waiting for server to start....2023-09-21 09:51:09.294 UTC [36] LOG: OrioleDB public beta 2 started 2023-09-21 09:51:09.295 UTC [36] LOG: starting PostgreSQL 14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00 on x86_64-pc-linux-musl, compiled by Alpine clang version 15.0.7, 64-bit 2023-09-21 09:51:09.300 UTC [36] LOG: listening on Unix socket &quot;/var/run/postgresql/.s.PGSQL.5432&quot; 2023-09-21 09:51:09.316 UTC [37] LOG: database system was shut down at 2023-09-21 09:51:08 UTC 2023-09-21 09:51:09.316 UTC [38] LOG: orioledb background writer started 2023-09-21 09:51:09.323 UTC [36] LOG: database system is ready to accept connections done server started /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/* waiting for server to shut down...2023-09-21 09:51:09.350 UTC [36] LOG: received fast shutdown request .2023-09-21 09:51:09.353 UTC [36] LOG: aborting any active transactions 2023-09-21 09:51:09.354 UTC [38] LOG: orioledb bgwriter is shut down 2023-09-21 09:51:09.357 UTC [36] LOG: background worker &quot;logical replication launcher&quot; (PID 44) exited with exit code 1 2023-09-21 09:51:09.357 UTC [39] LOG: shutting down 2023-09-21 09:51:09.360 UTC [39] LOG: orioledb checkpoint 1 started 2023-09-21 09:51:09.601 UTC [39] LOG: orioledb checkpoint 1 complete 2023-09-21 09:51:09.631 UTC [36] LOG: database system is shut down done server stopped PostgreSQL init process complete; ready for start up. 2023-09-21 09:51:09.712 UTC [1] LOG: OrioleDB public beta 2 started 2023-09-21 09:51:09.764 UTC [1] LOG: starting PostgreSQL 14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00 on x86_64-pc-linux-musl, compiled by Alpine clang version 15.0.7, 64-bit 2023-09-21 09:51:09.764 UTC [1] LOG: listening on IPv4 address &quot;0.0.0.0&quot;, port 5432 2023-09-21 09:51:09.764 UTC [1] LOG: listening on IPv6 address &quot;::&quot;, port 5432 2023-09-21 09:51:09.770 UTC [1] LOG: listening on Unix socket &quot;/var/run/postgresql/.s.PGSQL.5432&quot; 2023-09-21 09:51:09.778 UTC [49] LOG: database system was shut down at 2023-09-21 09:51:09 UTC 2023-09-21 09:51:09.778 UTC [50] LOG: orioledb background writer started 2023-09-21 09:51:09.785 UTC [1] LOG: database system is ready to accept connections 2023-09-21 09:54:23.633 UTC [68] FATAL: role &quot;root&quot; does not exist 2023-09-21 09:56:09.884 UTC [51] LOG: orioledb checkpoint 2 started 2023-09-21 09:56:10.100 UTC [51] LOG: orioledb checkpoint 2 complete </pre> <p>Notre serveur est correctement démarré. Nous pouvons maintenant nous y connecter à partir d&#8217;un autre terminal et préparer le cas de test. Pour ce faire, nous allons créer une nouvelle base de données sur laquelle nous effectuerons un test de charge avec pg_bench.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11c0860b7abc cd7f629f705e &quot;docker-entrypoint.s…&quot; 2 minutes ago Up 2 minutes 5432/tcp funny_hellman root@ip-172-44-2-190:~# docker exec -it 11c0860b7abc bash 11c0860b7abc:/# su - postgres 11c0860b7abc:~$ psql psql (14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00) Type &quot;help&quot; for help.</pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database pg_bench; CREATE DATABASE postgres=# \c pg_bench You are now connected to database &quot;pg_bench&quot; as user &quot;postgres&quot;. pg_bench=# CREATE TABLE test ( pg_bench(# id integer primary key, pg_bench(# value1 float8 not null, pg_bench(# value2 float8 not null, pg_bench(# value3 float8 not null, pg_bench(# value4 float8 not null, pg_bench(# ts timestamp not null pg_bench(# ); CREATE TABLE pg_bench=# CREATE INDEX test_value1_idx ON test (value1); CREATE INDEX pg_bench=# CREATE INDEX test_value2_idx ON test (value2); CREATE INDEX pg_bench=# CREATE INDEX test_value3_idx ON test (value3); CREATE INDEX pg_bench=# CREATE INDEX test_value4_idx ON test (value4); CREATE INDEX pg_bench=# CREATE INDEX test_ts_idx ON test (ts); CREATE INDEX </pre> <p>Notre environnement de test est prêt. Afin de pouvoir comparer les performances avec une instance PostgreSQL plus traditionnelle, j&#8217;ai également installé une version legacy en local sur la machine que j&#8217;utilise. J&#8217;ai créé la même table et les mêmes index que dans mon conteneur Docker. Pour réaliser ce test, j&#8217;ai repris le script pg_bench de test fourni par Oriole.</p> <pre class="brush: bash; title: ; notranslate">11c0860b7abc:~$ cat test.sql \set id random(1, 10000000) INSERT INTO test VALUES(:id, random(), random(), random(), random(), now() - random() * random() * 1800 * interval '1 second') ON CONFLICT (id) DO UPDATE SET ts = now();</pre> <p>Ce petit script de test permet de lancer des insertions successives de données dans notre table de test. Il est conçu pour maximiser les chances de fragmentation à mesure que nous insérons des données dans notre table, car les doublons de clé primaire entraînent la mise à jour d&#8217;anciennes lignes. Cela devrait donc nous permettre de constater la différence d&#8217;espace disque entre une version standard de PostgreSQL et la version du moteur développée par Oriole.</p> <h2>Résultats des test :</h2> <p>Mes tests sont réalisés sur une machine Amazon EC2 de type &#8220;large&#8221;, équipée de 7 gigaoctets de RAM et de 2 unités de traitement central (CPU).</p> <p>Voici la capture d&#8217;écran de l&#8217;occupation de l&#8217;espace disque pour le premier test, celui concernant l&#8217;instance OrioleDB :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png"><img loading="lazy" decoding="async" src="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png" alt="" width="1261" height="345" class="aligncenter size-full wp-image-10306" srcset="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png 1261w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-300x82.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-1024x280.png 1024w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-768x210.png 768w" sizes="auto, (max-width: 1261px) 100vw, 1261px" /></a></p> <p>On peut observer que la progression de la taille de l&#8217;instance est régulière et très modérée. Pendant le test, j&#8217;ai surveillé l&#8217;utilisation de la RAM de la machine, qui atteignait un pic à 95 % et demeurait relativement stable pendant toute la durée du test. Conformément à la promesse du moteur, aucun processus d&#8217;autovacuum n&#8217;a été constaté pendant le processus.</p> <p>En comparaison, le graphique de la base de données non Oriole présente une nette différence :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png"><img loading="lazy" decoding="async" src="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png" alt="" width="1268" height="345" class="aligncenter size-full wp-image-10308" srcset="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png 1268w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-300x82.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-1024x279.png 1024w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-768x209.png 768w" sizes="auto, (max-width: 1268px) 100vw, 1268px" /></a></p> <p>On observe notamment de nombreux pics et chutes fréquents dus aux vacuums organisés régulièrement. Le tracé n&#8217;est pas linéaire.</p> <p>De plus, en surveillant les valeurs d&#8217;utilisation CPU, à la différence d&#8217;Oriole où les valeurs étaient constantes car il était toujours en train d&#8217;ajouter des données, les moments de vacuum de la base provoquent d&#8217;importantes montées de charge, suivies de chutes lorsque ces processus sont terminés sur notre instance de base.</p> <h2>Conclusion :</h2> <p>Sur le blog d&#8217;OrioleDB, les graphiques affichés sont le résultat de l&#8217;injection de 80 gigaoctets de données dans leur base de données à l&#8217;aide de pg_bench. Cependant, lorsque l&#8217;on examine les spécifications de la machine qu&#8217;ils ont utilisée pour leurs tests, on se rend compte qu&#8217;elle n&#8217;est pas à la portée de toutes les bourses. La machine utilisée comprend en effet 72 CPU et dispose d&#8217;un total de 144 Go de RAM, une configuration que très peu de personnes, principalement en raison du coût élevé de ce type de matériel, pourraient envisager pour leur infrastructure.</p> <p>Oriole fonctionne, comme nous l&#8217;avons vu sur les graphiques que j&#8217;ai commentés précédemment. Cependant, il s&#8217;agit d&#8217;un outil conçu pour les architectures de très grande envergure et les besoins importants en termes d&#8217;entrées/sorties de données. Les gains ne sont pas aussi significatifs sur des machines de plus petite taille, qui correspondent davantage à la réalité de nombreuses entreprises actuelles.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/containeriser-une-base-de-donnees-postgresql-avec-docker/" rel="bookmark" title="23 septembre 2021">Containeriser PostgreSQL avec Docker !</a> (Emmanuel RAMI) [ContainerPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/cybertec-migrator-une-alternative-a-ora2pg/" rel="bookmark" title="10 mai 2023">Cybertec Migrator : une alternative à ora2pg ?</a> (Sarah FAVEERE) [OraclePostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/comparatif-des-gestionnaires-de-vip-dans-un-cluster-patroni-episode-1-keepalived/" rel="bookmark" title="6 mars 2022">Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 1 (KEEPALIVED)</a> (David Baffaleuf) [ContainerPostgreSQL]</li> </ul> <p><!-- Similar Posts took 2.494 ms --></p> <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%2F%3Fp%3D10248&#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%2F%3Fp%3D10248&#038;title=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum" 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=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10248" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/">OrioleDB : la promesse d&#8217;un No-Vacuum</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> + <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%2F%3Fp%3D10248&#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%2F%3Fp%3D10248&#038;title=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum" 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=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10248" 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>La gestion efficace de l&#8217;espace disque dans PostgreSQL est une préoccupation constante pour les administrateurs de bases de données. C&#8217;est là qu&#8217;intervient le processus de &#8220;vacuum&#8221;, une fonctionnalité puissante et complexe qui peut jouer un rôle déterminant dans la performance et la stabilité de votre base de données PostgreSQL. Cependant, comme toute arme à double tranchant, le vacuum apporte à la fois des avantages considérables et des défis potentiels.</p> <p>Le principal reproche que l&#8217;on peut adresser au système de vacuum (et à l&#8217;auto-vacuum par extension) est sa consommation importante de ressources. C&#8217;est l&#8217;une des raisons majeures évoquées par les grandes entreprises qui optent pour un autre SGBD, <a href="https://www.uber.com/blog/postgres-to-mysql-migration/">comme Uber, qui a migré vers MySQL pour remplacer PostgreSQL</a>.</p> <p><a href="https://github.com/orioledb/orioledb">OrioleDB </a>nous propose une approche novatrice de la gestion de la fragmentation (bloat) et de l&#8217;espace disque de vos tables PostgreSQL. La principale promesse de ce nouveau moteur développé par la société Oriole est d&#8217;éliminer le besoin de vacuum régulier et de longues périodes de maintenance pour nos bases de données.</p> <p>Cette innovation s&#8217;articule autour de journaux d&#8217;annulation au niveau de la ligne et du bloc, ainsi que de la fusion automatique des pages. Les journaux d&#8217;annulation au niveau des lignes et des blocs offrent un contrôle plus précis, permettant une gestion plus efficace des modifications de données. La fonction de fusion automatique des pages travaille en arrière-plan pour regrouper les données fragmentées, améliorant ainsi encore davantage l&#8217;efficacité du système. Le moteur est actuellement disponible en version bêta sur le site d&#8217;OrioleDB.</p> <p>Dans cet article, je vais vous montrer comment installer OrioleDB et réaliser un test comparatif entre une instance classique et une instance OrioleDB en termes d&#8217;espace disque occupé. Je vais utiliser <a href="https://capdata.fr/dbadistance">notre agent AllDB de supervision des bases de données </a>pour générer des graphiques de la consommation d&#8217;espace disque.</p> <h2>Installation d&#8217;OrioleDB :</h2> <p>Pour pouvoir tester le nouveau moteur, il nous faut l&#8217;installer. Pour cela, deux solutions s&#8217;offrent à nous. On peut choisir de le compiler nous-mêmes à partir du code source fourni par Oriole, ou on peut choisir d&#8217;utiliser le conteneur Docker mis à notre disposition.</p> <p>Pour des raisons de rapidité de mise en place, j&#8217;ai choisi d&#8217;utiliser le conteneur fourni.</p> <p>Pour commencer, j&#8217;installe Docker sur ma machine.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~$ sudo apt-get update (...) Reading package lists... Done root@ip-172-44-2-190:~$ sudo apt-get install ca-certificates curl gnupg Reading package lists... Done (...) No VM guests are running outdated hypervisor (qemu) binaries on this host. root@ip-172-44-2-190:~$ sudo install -m 0755 -d /etc/apt/keyrings root@ip-172-44-2-190:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg root@ip-172-44-2-190:~$ sudo chmod a+r /etc/apt/keyrings/docker.gpg root@ip-172-44-2-190:~$ echo \ &quot;deb [arch=&quot;$(dpkg --print-architecture)&quot; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ &quot;$(. /etc/os-release &amp;amp;&amp;amp; echo &quot;$VERSION_CODENAME&quot;)&quot; stable&quot; | \ sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null sudo apt-get update Hit:1 http://eu-west-3.ec2.archive.ubuntu.com/ubuntu jammy InRelease Hit:2 http://eu-west-3.ec2.archive.ubuntu.com/ubuntu jammy-updates InRelease (...) No VM guests are running outdated hypervisor (qemu) binaries on this host.</pre> <p>Pour tester que mon installation fonctionne correctement, je peux démarrer l&#8217;image hello-world par défaut de Docker.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~$ sudo docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 719385e32844: Pull complete Digest: sha256:4f53e2564790c8e7856ec08e384732aa38dc43c52f02952483e3f003afbf23db Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/</pre> <p>Docker est correctement installé, nous pouvons à présent aller récupérer l&#8217;image Oriole depuis les dépôts (repositories). J&#8217;ai choisi de récupérer l&#8217;image &#8220;latest&#8221; de la version 14, avec laquelle je vais effectuer mes tests.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker pull orioledb/orioledb:latest-pg14 latest-pg14: Pulling from orioledb/orioledb 7264a8db6415: Pull complete 4e0888d8a332: Pull complete 4b0510894ae7: Pull complete dc5da57566a1: Pull complete 9611602787a1: Pull complete 8f68f9452236: Pull complete 5c92ed25d025: Pull complete 11bb162cfe7a: Pull complete 6f375d990892: Pull complete 6ac166496e68: Pull complete Digest: sha256:4d408916e929ff0bcea11fee91ca5e69b0929be1afa58b5dbbcddcce340089ef Status: Downloaded newer image for orioledb/orioledb:latest-pg14 docker.io/orioledb/orioledb:latest-pg14</pre> <p>On vérifie que notre image est bien présente, et on l&#8217;éxecute :</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker image ls --all REPOSITORY TAG IMAGE ID CREATED SIZE orioledb/orioledb latest-pg14 cd7f629f705e 10 days ago 270MB hello-world latest 9c7a54a9a43c 4 months ago 13.3kB </pre> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker run -e POSTGRES_PASSWORD=password cd7f629f705e The files belonging to this database system will be owned by user &quot;postgres&quot;. This user must also own the server process. The database cluster will be initialized with locale &quot;en_US.utf8&quot;. The default database encoding has accordingly been set to &quot;UTF8&quot;. The default text search configuration will be set to &quot;english&quot;. Data page checksums are disabled. fixing permissions on existing directory /var/lib/postgresql/data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting default time zone ... UTC creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... sh: locale: not found 2023-09-21 09:51:08.177 UTC [30] WARNING: no usable system locales were found ok syncing data to disk ... ok Success. You can now start the database server using: pg_ctl -D /var/lib/postgresql/data -l logfile start initdb: warning: enabling &quot;trust&quot; authentication for local connections You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. waiting for server to start....2023-09-21 09:51:09.294 UTC [36] LOG: OrioleDB public beta 2 started 2023-09-21 09:51:09.295 UTC [36] LOG: starting PostgreSQL 14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00 on x86_64-pc-linux-musl, compiled by Alpine clang version 15.0.7, 64-bit 2023-09-21 09:51:09.300 UTC [36] LOG: listening on Unix socket &quot;/var/run/postgresql/.s.PGSQL.5432&quot; 2023-09-21 09:51:09.316 UTC [37] LOG: database system was shut down at 2023-09-21 09:51:08 UTC 2023-09-21 09:51:09.316 UTC [38] LOG: orioledb background writer started 2023-09-21 09:51:09.323 UTC [36] LOG: database system is ready to accept connections done server started /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/* waiting for server to shut down...2023-09-21 09:51:09.350 UTC [36] LOG: received fast shutdown request .2023-09-21 09:51:09.353 UTC [36] LOG: aborting any active transactions 2023-09-21 09:51:09.354 UTC [38] LOG: orioledb bgwriter is shut down 2023-09-21 09:51:09.357 UTC [36] LOG: background worker &quot;logical replication launcher&quot; (PID 44) exited with exit code 1 2023-09-21 09:51:09.357 UTC [39] LOG: shutting down 2023-09-21 09:51:09.360 UTC [39] LOG: orioledb checkpoint 1 started 2023-09-21 09:51:09.601 UTC [39] LOG: orioledb checkpoint 1 complete 2023-09-21 09:51:09.631 UTC [36] LOG: database system is shut down done server stopped PostgreSQL init process complete; ready for start up. 2023-09-21 09:51:09.712 UTC [1] LOG: OrioleDB public beta 2 started 2023-09-21 09:51:09.764 UTC [1] LOG: starting PostgreSQL 14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00 on x86_64-pc-linux-musl, compiled by Alpine clang version 15.0.7, 64-bit 2023-09-21 09:51:09.764 UTC [1] LOG: listening on IPv4 address &quot;0.0.0.0&quot;, port 5432 2023-09-21 09:51:09.764 UTC [1] LOG: listening on IPv6 address &quot;::&quot;, port 5432 2023-09-21 09:51:09.770 UTC [1] LOG: listening on Unix socket &quot;/var/run/postgresql/.s.PGSQL.5432&quot; 2023-09-21 09:51:09.778 UTC [49] LOG: database system was shut down at 2023-09-21 09:51:09 UTC 2023-09-21 09:51:09.778 UTC [50] LOG: orioledb background writer started 2023-09-21 09:51:09.785 UTC [1] LOG: database system is ready to accept connections 2023-09-21 09:54:23.633 UTC [68] FATAL: role &quot;root&quot; does not exist 2023-09-21 09:56:09.884 UTC [51] LOG: orioledb checkpoint 2 started 2023-09-21 09:56:10.100 UTC [51] LOG: orioledb checkpoint 2 complete </pre> <p>Notre serveur est correctement démarré. Nous pouvons maintenant nous y connecter à partir d&#8217;un autre terminal et préparer le cas de test. Pour ce faire, nous allons créer une nouvelle base de données sur laquelle nous effectuerons un test de charge avec pg_bench.</p> <pre class="brush: bash; title: ; notranslate">root@ip-172-44-2-190:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11c0860b7abc cd7f629f705e &quot;docker-entrypoint.s…&quot; 2 minutes ago Up 2 minutes 5432/tcp funny_hellman root@ip-172-44-2-190:~# docker exec -it 11c0860b7abc bash 11c0860b7abc:/# su - postgres 11c0860b7abc:~$ psql psql (14.9 OrioleDB public beta 2 PGTAG=patches14_16 alpine:3.18+clang-15 build:2023-09-10T16:41:50+00:00) Type &quot;help&quot; for help.</pre> <pre class="brush: sql; title: ; notranslate">postgres=# create database pg_bench; CREATE DATABASE postgres=# \c pg_bench You are now connected to database &quot;pg_bench&quot; as user &quot;postgres&quot;. pg_bench=# CREATE TABLE test ( pg_bench(# id integer primary key, pg_bench(# value1 float8 not null, pg_bench(# value2 float8 not null, pg_bench(# value3 float8 not null, pg_bench(# value4 float8 not null, pg_bench(# ts timestamp not null pg_bench(# ); CREATE TABLE pg_bench=# CREATE INDEX test_value1_idx ON test (value1); CREATE INDEX pg_bench=# CREATE INDEX test_value2_idx ON test (value2); CREATE INDEX pg_bench=# CREATE INDEX test_value3_idx ON test (value3); CREATE INDEX pg_bench=# CREATE INDEX test_value4_idx ON test (value4); CREATE INDEX pg_bench=# CREATE INDEX test_ts_idx ON test (ts); CREATE INDEX </pre> <p>Notre environnement de test est prêt. Afin de pouvoir comparer les performances avec une instance PostgreSQL plus traditionnelle, j&#8217;ai également installé une version legacy en local sur la machine que j&#8217;utilise. J&#8217;ai créé la même table et les mêmes index que dans mon conteneur Docker. Pour réaliser ce test, j&#8217;ai repris le script pg_bench de test fourni par Oriole.</p> <pre class="brush: bash; title: ; notranslate">11c0860b7abc:~$ cat test.sql \set id random(1, 10000000) INSERT INTO test VALUES(:id, random(), random(), random(), random(), now() - random() * random() * 1800 * interval '1 second') ON CONFLICT (id) DO UPDATE SET ts = now();</pre> <p>Ce petit script de test permet de lancer des insertions successives de données dans notre table de test. Il est conçu pour maximiser les chances de fragmentation à mesure que nous insérons des données dans notre table, car les doublons de clé primaire entraînent la mise à jour d&#8217;anciennes lignes. Cela devrait donc nous permettre de constater la différence d&#8217;espace disque entre une version standard de PostgreSQL et la version du moteur développée par Oriole.</p> <h2>Résultats des test :</h2> <p>Mes tests sont réalisés sur une machine Amazon EC2 de type &#8220;large&#8221;, équipée de 7 gigaoctets de RAM et de 2 unités de traitement central (CPU).</p> <p>Voici la capture d&#8217;écran de l&#8217;occupation de l&#8217;espace disque pour le premier test, celui concernant l&#8217;instance OrioleDB :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png"><img loading="lazy" decoding="async" src="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png" alt="" width="1261" height="345" class="aligncenter size-full wp-image-10306" srcset="https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB.png 1261w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-300x82.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-1024x280.png 1024w, https://blog.capdata.fr/wp-content/uploads/2023/10/OrioleDB-768x210.png 768w" sizes="auto, (max-width: 1261px) 100vw, 1261px" /></a></p> <p>On peut observer que la progression de la taille de l&#8217;instance est régulière et très modérée. Pendant le test, j&#8217;ai surveillé l&#8217;utilisation de la RAM de la machine, qui atteignait un pic à 95 % et demeurait relativement stable pendant toute la durée du test. Conformément à la promesse du moteur, aucun processus d&#8217;autovacuum n&#8217;a été constaté pendant le processus.</p> <p>En comparaison, le graphique de la base de données non Oriole présente une nette différence :</p> <p><a href="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png"><img loading="lazy" decoding="async" src="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png" alt="" width="1268" height="345" class="aligncenter size-full wp-image-10308" srcset="https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB.png 1268w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-300x82.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-1024x279.png 1024w, https://blog.capdata.fr/wp-content/uploads/2023/10/nonOrioleDB-768x209.png 768w" sizes="auto, (max-width: 1268px) 100vw, 1268px" /></a></p> <p>On observe notamment de nombreux pics et chutes fréquents dus aux vacuums organisés régulièrement. Le tracé n&#8217;est pas linéaire.</p> <p>De plus, en surveillant les valeurs d&#8217;utilisation CPU, à la différence d&#8217;Oriole où les valeurs étaient constantes car il était toujours en train d&#8217;ajouter des données, les moments de vacuum de la base provoquent d&#8217;importantes montées de charge, suivies de chutes lorsque ces processus sont terminés sur notre instance de base.</p> <h2>Conclusion :</h2> <p>Sur le blog d&#8217;OrioleDB, les graphiques affichés sont le résultat de l&#8217;injection de 80 gigaoctets de données dans leur base de données à l&#8217;aide de pg_bench. Cependant, lorsque l&#8217;on examine les spécifications de la machine qu&#8217;ils ont utilisée pour leurs tests, on se rend compte qu&#8217;elle n&#8217;est pas à la portée de toutes les bourses. La machine utilisée comprend en effet 72 CPU et dispose d&#8217;un total de 144 Go de RAM, une configuration que très peu de personnes, principalement en raison du coût élevé de ce type de matériel, pourraient envisager pour leur infrastructure.</p> <p>Oriole fonctionne, comme nous l&#8217;avons vu sur les graphiques que j&#8217;ai commentés précédemment. Cependant, il s&#8217;agit d&#8217;un outil conçu pour les architectures de très grande envergure et les besoins importants en termes d&#8217;entrées/sorties de données. Les gains ne sont pas aussi significatifs sur des machines de plus petite taille, qui correspondent davantage à la réalité de nombreuses entreprises actuelles.<strong>Continuez votre lecture sur le blog :</strong></p> <ul class="similar-posts"> <li><a href="https://blog.capdata.fr/index.php/containeriser-une-base-de-donnees-postgresql-avec-docker/" rel="bookmark" title="23 septembre 2021">Containeriser PostgreSQL avec Docker !</a> (Emmanuel RAMI) [ContainerPostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pyrseas-et-postgresql-comparer-facilement-des-schema-de-base-de-donnees/" rel="bookmark" title="3 janvier 2023">Pyrseas et Postgresql : Comparer facilement des schémas de base de données</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/cybertec-migrator-une-alternative-a-ora2pg/" rel="bookmark" title="10 mai 2023">Cybertec Migrator : une alternative à ora2pg ?</a> (Sarah FAVEERE) [OraclePostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/" rel="bookmark" title="3 avril 2024">pg_recursively_delete : Simplifier les suppressions récursives</a> (Sarah FAVEERE) [PostgreSQL]</li> <li><a href="https://blog.capdata.fr/index.php/comparatif-des-gestionnaires-de-vip-dans-un-cluster-patroni-episode-1-keepalived/" rel="bookmark" title="6 mars 2022">Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 1 (KEEPALIVED)</a> (David Baffaleuf) [ContainerPostgreSQL]</li> </ul> <p><!-- Similar Posts took 2.231 ms --></p> <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%2F%3Fp%3D10248&#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%2F%3Fp%3D10248&#038;title=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum" 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=OrioleDB%20%3A%20la%20promesse%20d%E2%80%99un%20No-Vacuum&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10248" 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>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/">OrioleDB : la promesse d&#8217;un No-Vacuum</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p> <p>La gestion efficace de l&#8217;espace disque dans PostgreSQL est une préoccupation constante pour les administrateurs de bases de données. C&#8217;est là qu&#8217;intervient le processus de &#8220;vacuum&#8221;, une fonctionnalité puissante et complexe qui peut jouer un rôle déterminant dans la performance&#8230; <a href="https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/" class="more-link">Continuer la lecture <span class="meta-nav">&#8594;</span></a></p> <p>L’article <a rel="nofollow" href="https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/">OrioleDB : la promesse d&#8217;un No-Vacuum</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>