<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Capdata TECH BLOG</title>
	<atom:link href="https://blog.capdata.fr/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.capdata.fr/</link>
	<description>Le blog technique sur les bases de données de CAP DATA Consulting</description>
	<lastBuildDate>Wed, 19 Nov 2025 17:23:48 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.capdata.fr/wp-content/uploads/2019/09/favicon2.ico</url>
	<title>Capdata TECH BLOG</title>
	<link>https://blog.capdata.fr/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>PostgreSQL 18 : des IO asynchrones performantes !</title>
		<link>https://blog.capdata.fr/index.php/postgresql-18-des-io-asynchrones-performantes/</link>
					<comments>https://blog.capdata.fr/index.php/postgresql-18-des-io-asynchrones-performantes/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Wed, 19 Nov 2025 09:10:27 +0000</pubDate>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[I/O]]></category>
		<category><![CDATA[performance]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10742</guid>

					<description><![CDATA[<p>&#160; Le 25 septembre dernier, PostgreSQL sortait sa toute dernière version, PostgreSQL 18. Cette version apporte de nombreux correctifs mais aussi des nouveautés et des améliorations sur les performances globales. Une nouveauté est particulièrement pertinente pour cette version. Il s&#8217;agit&#8230; <a href="https://blog.capdata.fr/index.php/postgresql-18-des-io-asynchrones-performantes/" 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-18-des-io-asynchrones-performantes/">PostgreSQL 18 : des IO asynchrones performantes !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10742&#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%3D10742&#038;title=PostgreSQL%2018%20%3A%20des%20IO%20asynchrones%20performantes%20%21" 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%2018%20%3A%20des%20IO%20asynchrones%20performantes%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10742" 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-medium wp-image-10743" src="https://blog.capdata.fr/wp-content/uploads/2025/11/7346573-300x300.png" alt="" width="300" height="300" srcset="https://blog.capdata.fr/wp-content/uploads/2025/11/7346573-300x300.png 300w, https://blog.capdata.fr/wp-content/uploads/2025/11/7346573-150x150.png 150w, https://blog.capdata.fr/wp-content/uploads/2025/11/7346573-144x144.png 144w, https://blog.capdata.fr/wp-content/uploads/2025/11/7346573-50x50.png 50w, https://blog.capdata.fr/wp-content/uploads/2025/11/7346573.png 512w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>&nbsp;</p>
<p>Le 25 septembre dernier, PostgreSQL sortait sa toute dernière version, PostgreSQL 18.<br />
Cette version apporte de nombreux correctifs mais aussi des nouveautés et des améliorations sur les performances globales.</p>
<p>Une nouveauté est particulièrement pertinente pour cette version. Il s&#8217;agit des &#8220;asynchronous IO&#8221; (AIO).</p>
<h2>Présentation</h2>
<p>Une lecture en mode synchrone se fait lorsque PostgreSQL demande au kernel l&#8217;accès à une page disque. PostgreSQL attend alors sa mise à disposition pour être traitée et montée en mémoire partagée.<br />
L&#8217;OS écoute la requête de la part de PostgreSQL et ne peut anticiper les éventuelles demandes.<br />
Dans ce contexte, PostgreSQL est très dépendant des performances disques avec d&#8217;éventuels &#8220;bottlenecks&#8221;.</p>
<p>Avec le mode asynchrone IO, l&#8217;OS est capable de traiter en parallèle les demandes faites par PostgreSQL et peut donc facilement travailler sur plusieurs requêtes en lecture.</p>
<p>&nbsp;</p>
<p>Synchronous IO                                                                                                            Asynchronous IO</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10744" src="https://blog.capdata.fr/wp-content/uploads/2025/11/synch_IO.jpg" alt="" width="474" height="222" srcset="https://blog.capdata.fr/wp-content/uploads/2025/11/synch_IO.jpg 827w, https://blog.capdata.fr/wp-content/uploads/2025/11/synch_IO-300x140.jpg 300w, https://blog.capdata.fr/wp-content/uploads/2025/11/synch_IO-768x359.jpg 768w" sizes="auto, (max-width: 474px) 100vw, 474px" />   <img loading="lazy" decoding="async" class="alignnone wp-image-10745" src="https://blog.capdata.fr/wp-content/uploads/2025/11/asynch_IO.jpg" alt="" width="459" height="187" srcset="https://blog.capdata.fr/wp-content/uploads/2025/11/asynch_IO.jpg 818w, https://blog.capdata.fr/wp-content/uploads/2025/11/asynch_IO-300x122.jpg 300w, https://blog.capdata.fr/wp-content/uploads/2025/11/asynch_IO-768x314.jpg 768w" sizes="auto, (max-width: 459px) 100vw, 459px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Un premier travail avait été fait avec les versions récentes de PostgreSQL afin de faire du &#8220;prefetch&#8221; de données via la méthode système &#8220;<a href="http://manpagesfr.free.fr/man/man2/posix_fadvise.2.html">posix_fadvise</a>&#8220;, ceci permettait d&#8217;anticiper les déclarations d&#8217;accès à certaines données ou pages de fichiers en déclarant un offset de départ et une longueur défini. Ceci évite des allers-retours sur le même fichier traitant des pages contiguës.<br />
Malheureusement, avec PostgreSQL, ce mécanisme ne permet pas de monter les pages dans le &#8220;<strong>shared_buffer</strong>&#8220;, mais s&#8217;appuie sur le cache disque.</p>
<p>Avec les asynchronous IO, PostgreSQL est particulièrement performant lors des lectures séquentielles (seq scans, bitmap heap scan).<br />
Un parallèle peut être fait avec le mécanisme d&#8217;offloading fait par Oracle Exadata et les &#8220;smart scans&#8221; montés directement vers la PGA.</p>
<p>&nbsp;</p>
<h2>Paramétrage</h2>
<p>&nbsp;</p>
<p>Avec la version 18, PostgreSQL met l&#8217;accent sur les lectures asynchrones avec de nouveaux paramètres.</p>
<p><b>io_method</b> qui peut prendre 3 valeurs différentes</p>
<ul>
<li><span style="color: #3366ff;">sync</span> : exécute les IO en mode synchrone comme pour les versions PostgreSQL antérieures</li>
<li><span style="color: #3366ff;">worker</span> : valeur par défaut. Les lectures se font via des processus en parallèles lancés par la requête parente (3 par défaut). Ces processus font des appels auprès du kernel et montent les données dans le &#8220;shared_buffers&#8221; pour être traitées.</li>
<li><span style="color: #3366ff;">io_uring</span> : utilisation de la méthode &#8220;<a href="https://en.wikipedia.org/wiki/Io_uring">io_uring</a>&#8221; qui consiste, depuis la version 5.1 du kernel Linux, à utiliser des &#8220;shared ring buffers&#8221;. Ce procédé utilise des &#8220;queue rings&#8221; entre PostgreSQL et le kernel , avec &#8220;completion queue&#8221; pour la partie kernel et &#8220;submission queue&#8221; géré pour les demandes PostgreSQL.<br />
Je vous invite à lire <a href="https://www.phoronix.com/news/Linux-io_uring-Fast-Efficient">cet article</a> pour plus de précisions sur ce mécanisme.</li>
</ul>
<p>&nbsp;</p>
<p><strong>io_worker</strong> représentant le nombre de process en parallèle qui vont traiter les demandes de mise en cache des pages.<br />
Par défaut, le nombre est de 3, mais nous pouvons monter au-delà.</p>
<p><strong>effective_io_concurrency</strong> est déjà présent sur les anciennes versions de PostgreSQL. Cependant, dans le cas des IO asychrones (io_method=worker ou io_methode=io_uring), les IO se font en concurrence directement dans PostgreSQL.</p>
<p><strong>io_combine_limit</strong> est la taille maximale d&#8217;IO.</p>
<p>&nbsp;</p>
<h4><span style="color: #ff6600;">Système</span></h4>
<p>&nbsp;</p>
<p>Vérifier que votre serveur Linux est compatible avec la méthode &#8220;io_uring&#8221;. Pour cela exécuter cette commande, qui doit vous renvoyer 0</p>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">$ cat /proc/sys/kernel/io_uring_disabled
0</pre>
<p>Sinon, forcer la valeur à 0, sous &#8220;root&#8221;.</p>
<pre class="brush: bash; title: ; notranslate"># echo 0 &gt; /proc/sys/kernel/io_uring_disabled</pre>
<p>&nbsp;</p>
<p>La validation peut se faire également sur la configuration du kernel au démarrage</p>
<pre class="brush: bash; title: ; notranslate"> # cat /boot/config-6.12.48+deb13-cloud-amd64 | grep -i io_uring
CONFIG_IO_URING=y</pre>
<p>&nbsp;</p>
<h4><span style="color: #ff6600;">PostgreSQL</span></h4>
<p>&nbsp;</p>
<p>Pour utiliser le mode &#8220;<strong>io_method=<span style="color: #3366ff;">io_uring</span></strong>&#8220;, il faudra compiler PostgreSQL 18 avec ce mode -&gt; <strong>with-liburing</strong>.</p>
<p>Il faut donc récupérer les sources depuis le site officiel <a href="https://www.postgresql.org/ftp/source/v18.1/">PostgreSQL</a></p>
<p>Puis configurer avec l&#8217;option suivante :</p>
<pre class="brush: bash; title: ; notranslate"># ./configure --with-liburing
# make
# make install
</pre>
<p>&nbsp;</p>
<h2>Benchmark</h2>
<p>&nbsp;</p>
<p>Pour nos tests, nous utilisons l&#8217;outil &#8220;pgbench&#8221;.<br />
Nous allons créer un jeu de test avec un facteur de 500 sur le nombre de lignes créées par défaut dans les tables. Notre plus grosse table &#8220;pgbench_accounts&#8221; devrait donc dépasser les 50M de lignes.</p>
<p>Attention, chaque test se fait avec données &#8220;à froid&#8221;. Le cache est vidé à chaque interrogation. C&#8217;est à la première exécution que le test est le plus représentatif car PostgreSQL n&#8217;a pas de pages dans son cache et doit donc solliciter le kernel.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">$ pgbench -i pgbench -s 500
dropping old tables...
NOTICE: table &quot;pgbench_accounts&quot; does not exist, skipping
NOTICE: table &quot;pgbench_branches&quot; does not exist, skipping
NOTICE: table &quot;pgbench_history&quot; does not exist, skipping
NOTICE: table &quot;pgbench_tellers&quot; does not exist, skipping
creating tables...
generating data (client-side)...
vacuuming...
creating primary keys...
done in 42.40 s (drop tables 0.00 s, create tables 0.01 s, client-side generate 25.13 s, vacuum 3.35 s, primary keys 13.92 s).</pre>
<p>&nbsp;</p>
<p>La suite consiste à lancer un &#8220;SELECT COUNT&#8221; sur la table &#8220;pgbench_accounts&#8221; et relever les temps d&#8217;exécution entre les différentes versions de PostgreSQL et les différentes méthodes de lectures asynchrones.</p>
<p>&nbsp;</p>
<h4>PostgreSQL 17</h4>
<pre class="brush: sql; title: ; notranslate">$  select count(abalance) from &quot;pgbench_accounts&quot;;
count
----------
50000000
(1 row)

Time: 48937.120 ms (00:48.937)</pre>
<p>&nbsp;</p>
<p>Nous mettons un peu plus de 49 secondes pour compter les 50M de lignes de la table.</p>
<p>le plan d&#8217;exécution est le suivant</p>
<pre class="brush: sql; title: ; notranslate">$  explain (analyze, buffers) select count(abalance) from &quot;pgbench_accounts&quot;;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=1081089.88..1081089.89 rows=1 width=8) (actual time=69053.114..69053.219 rows=1 loops=1)
Buffers: shared hit=2518 read=817155
- Gather (cost=1081089.67..1081089.88 rows=2 width=8) (actual time=69051.505..69053.209 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
Buffers: shared hit=2518 read=817155
- Partial Aggregate (cost=1080089.67..1080089.68 rows=1 width=8) (actual time=69021.569..69021.571 rows=1 loops=3)
Buffers: shared hit=2518 read=817155
- Parallel Seq Scan on pgbench_accounts (cost=0.00..1028006.33 rows=20833333 width=4) (actual time=4.259..41669.680 rows=16666667 loops=3)
Buffers: shared hit=2518 read=817155
Planning Time: 0.056 ms
Execution Time: 69053.251 ms</pre>
<p>&nbsp;</p>
<p>Le &#8220;parallel seq scan&#8221; sur &#8220;pgbench_accounts&#8221; est fait avec 2 workers en parallèle.</p>
<p>&nbsp;</p>
<h4>PostgreSQL 18</h4>
<p>&nbsp;</p>
<p>Avec la valeur &#8220;<strong>io_methode=sync</strong>&#8220;, nous retrouvons à peu près le même temps, même si notre serveur sur PostgreSQL 18 est peu plus puissant et dispose de plus de RAM.Le &#8220;shared_buffer&#8221; a été taillé en conséquence.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">pgbench=# select count(abalance) from &quot;pgbench_accounts&quot;;
count
----------
10000000
(1 row)

Time: 49704.009 ms (00:49.704)</pre>
<p>&nbsp;</p>
<p>le plan est à peu près le même avec nos 2 workers effectuant du &#8220;parallel seq scan&#8221; sur &#8220;pgbench_accounts&#8221;.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">  - Parallel Seq Scan on public.pgbench_accounts (cost=0.00..205601.67 rows=4166667 width=4) (actual time=0.838..42937.512 rows=3333333.33 loops=3)
Output: aid, bid, abalance, filler
Buffers: shared read=163935
Buffers: shared read=54826</pre>
<p>&nbsp;</p>
<p>Avec &#8220;<strong>io_method=worker</strong>&#8221; = 3, là, nous gagnons déjà quelques secondes en terme de temps d&#8217;exécution puisque notre requête est proche des 40 sec.</p>
<p>Puis avec &#8220;<strong>io_method=io_uring</strong>&#8220;, c&#8217;est là que nous sommes le plus performant puisque nous descendons à moins de 25 secondes.</p>
<p>&nbsp;</p>
<p>Nous obtenons les résultats suivants</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10753 size-full" src="https://blog.capdata.fr/wp-content/uploads/2025/11/temps-2.jpg" alt="" width="629" height="368" srcset="https://blog.capdata.fr/wp-content/uploads/2025/11/temps-2.jpg 629w, https://blog.capdata.fr/wp-content/uploads/2025/11/temps-2-300x176.jpg 300w" sizes="auto, (max-width: 629px) 100vw, 629px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>A noter que pendant des opérations de lectures asynchrones, nous pouvons suivre l&#8217;évolution de celles ci via la vue &#8216;<strong>pg_aios</strong>&#8221; mise à disposition par PostgreSQL.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">$  select * from pg_aios;
pid | io_id | io_generation | state | operation | off | length | target | handle_data_len | raw_result | result | target_desc | f_sync | f_localmem | f_buffered
-------+-------+---------------+-----------+-----------+-----------+--------+--------+-----------------+------------+---------+--------------------------------------------------+--------+------------+------------
41329 | 192 | 9233 | SUBMITTED | readv | 570818560 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200752..200767 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 193 | 9290 | SUBMITTED | readv | 570556416 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200720..200735 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 194 | 9239 | SUBMITTED | readv | 570949632 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200768..200783 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 195 | 9229 | SUBMITTED | readv | 570687488 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200736..200751 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 197 | 9225 | SUBMITTED | readv | 571473920 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200832..200847 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 198 | 6372 | SUBMITTED | readv | 569638912 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200608..200623 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 200 | 6420 | SUBMITTED | readv | 571080704 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200784..200799 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 201 | 9247 | SUBMITTED | readv | 571211776 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200800..200815 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 202 | 9248 | SUBMITTED | readv | 571604992 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200848..200863 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 203 | 9221 | SUBMITTED | readv | 569769984 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200624..200639 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 207 | 9223 | SUBMITTED | readv | 569507840 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200592..200607 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 208 | 9226 | SUBMITTED | readv | 571736064 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200864..200879 in file &quot;base/16440/16456&quot; | f | f | t
41329 | 210 | 282085 | SUBMITTED | readv | 571342848 | 131072 | smgr | 16 | NULL | UNKNOWN | blocks 200816..200831 in file &quot;base/16440/16456&quot; | f | f | t
(13 rows)</pre>
<p>&nbsp;</p>
<p>Cette vue est alimentée via la fonction &#8216;<strong>pg_get_aios&#8221;</strong>.</p>
<p>Nous pouvons voir l&#8217;offset de la page sur laquelle la lecture pointe ainsi que la longueur de l&#8217;opération IO en cours.</p>
<h2>Conclusion</h2>
<p>Pour profiter de l&#8217;efficacité des lectures asynchrones, ne pas oublier de configurer la valeur de &#8220;<strong>io_method</strong>&#8221; à &#8220;<strong>worker</strong>&#8221; ou &#8220;<strong>io_uring </strong></p>
<p>A volumes équivalents, les plans d&#8217;exécutions nous permettent de voir que l&#8217;on traite un nombre de &#8220;shared read&#8221; similaire entre PostgreSQL 18 qu&#8217;avec PostgreSQL 17 mais avec un temps plus faible pour PostgreSQL 18.</p>
<p>Attention cependant, les gains sont effectifs sur les opérations de type &#8220;seq scan&#8221; ou &#8220;bitmap heap scan&#8221;. Pas de gain possible sur des opérations d&#8217;écritures.</p>
<p>En d&#8217;autres termes, la configuration Asynchrones IO sera parfaitement adaptée pour des requêtes décisionnelles avec datawarehouse volumineux.</p>
<p>Autre point à savoir, il a été relevé également des risques en terme de sécurité avec le mode <strong>io_uring</strong> configuré dans le kernel.<br />
Certains sites comme <a href="https://www.upwind.io/feed/io_uring-linux-performance-boost-or-security-headache">celui-ci</a> font états de potentiels processus de type &#8220;malwares&#8221; qui pourraient s&#8217;attaquer au kernel et mettre en péril la sécurité du serveur.</p>
<p>Sur l&#8217;<a href="https://en.wikipedia.org/wiki/Io_uring">article Wikipédia</a> dédié à &#8220;<strong>io_uring</strong>&#8220;, il est d&#8217;ailleurs noté -&gt;</p>
<p><em>&#8220;In June 2023, Google&#8217;s security team reported that 60% of the <a title="Exploit (computer security)" href="https://en.wikipedia.org/wiki/Exploit_(computer_security)">exploits</a> submitted to their <a title="Bug bounty program" href="https://en.wikipedia.org/wiki/Bug_bounty_program">bug bounty program</a> in 2022 were exploits of the Linux kernel&#8217;s io_uring vulnerabilities. As a result, <code>io_uring</code> was disabled for apps in <a title="Android (operating system)" href="https://en.wikipedia.org/wiki/Android_(operating_system)">Android</a>, and disabled entirely in <a title="ChromeOS" href="https://en.wikipedia.org/wiki/ChromeOS">ChromeOS</a> as well as Google servers.<sup id="cite_ref-12" class="reference"><a href="https://en.wikipedia.org/wiki/Io_uring#cite_note-12"><span class="cite-bracket">[</span>11<span class="cite-bracket">]</span></a></sup> <a title="Docker (software)" href="https://en.wikipedia.org/wiki/Docker_(software)">Docker</a> also consequently disabled io_uring from their default <a title="Seccomp" href="https://en.wikipedia.org/wiki/Seccomp">seccomp</a> profile&#8221;</em></p>
<p>&nbsp;</p>
<p>Bonne fin de journée !<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<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/pruning-de-partitions-sous-postgresql/" rel="bookmark" title="7 décembre 2020">&#8220;Pruning&#8221; de partitions sous PostgreSQL ou comment bien élaguer !</a> (Capdata team) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/" rel="bookmark" title="26 février 2025">PostgreSQL : optimiser vos opérations vacuum et analyze !</a> (Emmanuel RAMI) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/nouveautes-pg_stat_statements-avec-postgresql-15/" rel="bookmark" title="16 mars 2023">Nouveautés pg_stat_statements avec PostgreSQL 15</a> (David Baffaleuf) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/postgresql-basics-lire-un-plan-dexecution/" rel="bookmark" title="13 mai 2025">PostgreSQL Basics : lire un plan d&#8217;exécution comme un·e pro (ou presque)</a> (Sarah FAVEERE) [PostgreSQL]</li>
</ul>
<p><!-- Similar Posts took 3.587 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%3D10742&#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%3D10742&#038;title=PostgreSQL%2018%20%3A%20des%20IO%20asynchrones%20performantes%20%21" 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%2018%20%3A%20des%20IO%20asynchrones%20performantes%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10742" 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-18-des-io-asynchrones-performantes/">PostgreSQL 18 : des IO asynchrones performantes !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/postgresql-18-des-io-asynchrones-performantes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PostgreSQL Basics : lire un plan d&#8217;exécution comme un·e pro (ou presque)</title>
		<link>https://blog.capdata.fr/index.php/postgresql-basics-lire-un-plan-dexecution/</link>
					<comments>https://blog.capdata.fr/index.php/postgresql-basics-lire-un-plan-dexecution/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Tue, 13 May 2025 15:20:05 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[explain]]></category>
		<category><![CDATA[Plan d'exécution]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10697</guid>

					<description><![CDATA[<p>Introduction : Quand une requête PostgreSQL est considérée comme lente ou que ses performances se dégradent soudainement, il y a un réflexe à toujours avoir : utiliser les plans d&#8217;exécution. Je n’apprends certainement rien à la majorité des personnes qui&#8230; <a href="https://blog.capdata.fr/index.php/postgresql-basics-lire-un-plan-dexecution/" 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-basics-lire-un-plan-dexecution/">PostgreSQL Basics : lire un plan d&#8217;exécution comme un·e pro (ou presque)</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10697&#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%3D10697&#038;title=PostgreSQL%20Basics%20%3A%20lire%20un%20plan%20d%E2%80%99ex%C3%A9cution%20comme%20un%C2%B7e%20pro%20%28ou%20presque%29" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="linkedin" title="Share on Linkedin" class="synved-share-image synved-social-image synved-social-image-share" width="24" height="24" style="display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://blog.capdata.fr/wp-content/plugins/social-media-feather/synved-social/image/social/regular/48x48/linkedin.png" /></a><a class="synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox" data-provider="mail" rel="nofollow" title="Share by email" href="mailto:?subject=PostgreSQL%20Basics%20%3A%20lire%20un%20plan%20d%E2%80%99ex%C3%A9cution%20comme%20un%C2%B7e%20pro%20%28ou%20presque%29&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10697" 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><h1>Introduction :</h1>
<p>Quand une requête PostgreSQL est considérée comme lente ou que ses performances se dégradent soudainement, il y a un réflexe à toujours avoir : utiliser les plans d&#8217;exécution. Je n’apprends certainement rien à la majorité des personnes qui liront cet article, mais si cela permet de garder vigilants les débutants et de leur enseigner quelques ficelles, alors cet article vaut le coup.</p>
<p>Si vous avez déjà été dans le cas de figure où un plan d&#8217;exécution est trop gros pour que vous sachiez par quel bout le prendre, ou si vous avez l&#8217;impression qu&#8217;on vous parle chinois quand on évoque les index scan ou les &#8220;cost&#8221; d&#8217;une requête, alors vous êtes au bon endroit.</p>
<p>Dans cet article, premier d&#8217;une série sur les bases de PostgreSQL, nous allons démystifier l&#8217;optimisation des requêtes en utilisant EXPLAIN comme point de départ, et les plans d&#8217;exécution générés comme fil conducteur.</p>
<p>L’objectif : vous aider à identifier rapidement les informations clés, repérer les goulots d’étranglement, et gagner en autonomie pour diagnostiquer les performances de vos requêtes.</p>
<p>C’est parti !</p>
<h2>EXPLAIN ANALYZE : c’est quoi exactement et pourquoi l’utiliser ?</h2>
<p>Pour commencer, il faut savoir qu&#8217;un moteur de base de données, que ce soit PostgreSQL ou un autre ne &#8220;lit&#8221; pas une requête comme un humain pourrait la lire, de gauche à droite. Il élabore plutôt un plan d&#8217;exécution : il créé un ensemble d&#8217;étape qui se veulent le plus optimisées possible pour aller chercher les données que vous lui demander de la façon la plus efficace possible.</p>
<p>Il y a un moyen simple de voir ce plan d&#8217;exécution, c&#8217;est d&#8217;utiliser la commande EXPLAIN ANALYZE. Elle nous permet en plus de comprendre de quelle façon le moteur à réellement exécuté notre ordre SQL (et pas seulement comment il pensait le faire). C&#8217;est un peu comme lire un journal de bord de l&#8217;exécution de la requête.</p>
<h3>Prenons un exemple simple :</h3>
<p>Imaginons que nous avons une table users, constituée comme tel :</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE users (
id integer PRIMARY KEY,
name vachar(30),
age integer,
email varchar(80) );</pre>
<p>Et que nous souhaitons utiliser la requête suivante dans notre application :</p>
<pre class="brush: sql; title: ; notranslate">SELECT * FROM users WHERE email = 'foo@example.com';</pre>
<p>Sur cette table, nous n&#8217;avons pas d&#8217;index. Voici donc ce à quoi pourrait ressembler notre plan d&#8217;éxecution si nous utilisons la commande :</p>
<pre class="brush: sql; title: ; notranslate">EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'foo@example.com';

Seq Scan on users (cost=0.00..35.50 rows=1 width=100)
Filter: (email = 'foo@example.com')
Rows Removed by Filter: 999
Actual time=0.010..0.420 rows=1 loops=1
</pre>
<h3>Les termes du plan d&#8217;exécution :</h3>
<p>A première vue, il n&#8217;est pas évident de comprendre tout les termes que l&#8217;on voit dans le plan d&#8217;exécution généré.</p>
<p>Voici un récapitulatif des principaux :</p>
<ul>
<li><strong>Seq Scan / Index Scan :</strong> Le type de parcours utilisé (séquentiel ou via un index)</li>
<li><strong>cost=… :</strong> Estimation du &#8220;coût&#8221; total de l’opération, selon PostgreSQL. Le coût n&#8217;est pas exprimé dans une unité particulière, c&#8217;est juste un indicatif. Plus il est élevé, plus l&#8217;opération est &#8220;coûteuse&#8221;, notre but étant d&#8217;éviter les coûts énormes pour que tout soit plus simple.</li>
<li><strong>rows=… :</strong> Estimation du nombre de lignes que l&#8217;étape va retourner</li>
<li><strong>actual time=… :</strong> Le temps réel que cette étape a pris (en millisecondes)</li>
<li><strong>Rows Removed by Filter :</strong> Nombre de lignes lues mais éliminées par un filtre</li>
<li><strong>loops=1 :</strong> Nombre de fois que cette étape a été exécutée (ex. dans une boucle)</li>
</ul>
<h3>Puis un exemple plus compliqué :</h3>
<p>Attention, tout les plans d&#8217;exécution de requête ne sont pas aussi simples que celui que je viens de vous présenter. Bien souvent ils se présentent sous la forme de plusieurs nœuds indentés  qu&#8217;il faut lire dans le bon ordre. Imaginons une nouvelle table orders qui répertorie les commandes passées par un user :</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE orders (
id_orders integer PRIMARY KEY,
created_at timestamp,
total numeric,
status text,
user_id integer references users(id));</pre>
<p>Nous pouvons alors imaginer la requête suivante :</p>
<pre class="brush: sql; title: ; notranslate">SELECT u.name, o.total, o.created_at
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed'
ORDER BY o.created_at DESC
LIMIT 10;</pre>
<p>Qui remonte les dix premières commandes,  avec le nom de l&#8217;acheteur, le total de la commande et sa date, pour les 10 premières commandes dans l&#8217;ordre des dates dont le statut est &#8220;completed&#8221;.</p>
<p>Si on execute un explain analyze sur cette requête, on obtient un plan d&#8217;execution de cet ordre :</p>
<pre class="brush: sql; title: ; notranslate"> Limit (cost=123.45..123.48 rows=10 width=40) (actual time=1.234..1.239 rows=10 loops=1)
   -&amp;gt; Sort (cost=123.45..130.00 rows=2620 width=40) (actual time=1.234..1.236 rows=10 loops=1)
      Sort Key: o.created_at DESC
      Sort Method: top-N heapsort Memory: 25kB
       -&amp;gt; Nested Loop (cost=0.42..100.00 rows=2620 width=40) (actual time=0.045..0.980 rows=200 loops=1)
            -&amp;gt; Index Scan using idx_orders_created_at on orders o (cost=0.29..45.00 rows=2620 width=24) (actual time=0.030..0.340 rows=200 loops=1)
                 Filter: (status = 'completed')
                 Rows Removed by Filter: 50
            -&amp;gt; Index Scan using users_pkey on users u (cost=0.13..0.20 rows=1 width=16) (actual time=0.003..0.004 rows=1 loops=200)
                 Index Cond: (id = o.user_id)</pre>
<p>Si on devait visualiser ce plan d&#8217;exécution de manière graphique pour qu&#8217;il soit plus simple à lire, ça donnerait ça :</p>
<p><a href="https://blog.capdata.fr/wp-content/uploads/2025/04/chrome_zyhdtutiuX.png"><img loading="lazy" decoding="async" class="alignnone wp-image-10701" src="https://blog.capdata.fr/wp-content/uploads/2025/04/chrome_zyhdtutiuX-300x170.png" alt="" width="508" height="288" srcset="https://blog.capdata.fr/wp-content/uploads/2025/04/chrome_zyhdtutiuX-300x170.png 300w, https://blog.capdata.fr/wp-content/uploads/2025/04/chrome_zyhdtutiuX-768x434.png 768w, https://blog.capdata.fr/wp-content/uploads/2025/04/chrome_zyhdtutiuX.png 853w" sizes="auto, (max-width: 508px) 100vw, 508px" /></a></p>
<h3>Comment lire un plan d&#8217;exécution :</h3>
<p>Pour lire un plan d&#8217;exécution, on part toujours du nœud le plus indenté, puis on remonte vers les nœuds supérieur. Dans notre cas, on partirais des deux index scan pour remonter ensuite sur le Nested Loop, puis sur le Sort, et enfin sur le Limit.<br />
La raison est simple : le plan d’exécution reflète l’ordre réel d’exécution de la requête par PostgreSQL.<br />
Chaque étape du plan consomme les résultats produits par les étapes précédentes. Autrement dit : PostgreSQL commence par les opérations de lecture (accès aux tables, scans d’index&#8230;), puis applique les jointures, les filtres, les tris, etc. en remontant vers le haut du plan.<br />
Le nœud le plus &#8220;haut&#8221; du plan (celui avec le moins d’indentation) correspond à l’opération finale, celle qui retourne les résultats à l’utilisateur. Les blocs en dessous (plus indentés) sont les dépendances nécessaires pour y arriver.<br />
C’est donc une logique de pipeline de traitement :</p>
<ol>
<li><strong>Lire les données</strong></li>
<li><strong>Les filtrer</strong></li>
<li><strong>Les combiner</strong></li>
<li><strong>Les trier / agréger</strong></li>
<li><strong>Les retourner</strong></li>
</ol>
<p>Lire un plan d’exécution &#8220;du plus profond vers le haut&#8221;, c’est suivre le chemin de vie d’une ligne de résultat depuis le disque jusqu’à votre terminal.</p>
<h3>Petite encyclopédie des nœuds les plus courants dans un plan d’exécution</h3>
<p>Voici une sélection des nœuds que vous croiserez régulièrement dans les plans d’exécution PostgreSQL, avec une explication simple et directe pour chacun :</p>
<ul>
<li class="" data-start="251" data-end="420">
<p class="" data-start="253" data-end="420"><strong data-start="253" data-end="265">Seq Scan</strong><br data-start="265" data-end="268" />Lecture séquentielle de toute la table.<br data-start="309" data-end="312" /><em data-start="317" data-end="331">À surveiller</em> : Normal sur petites tables, mais sur les grosses, cela peut indiquer un index manquant.</p>
</li>
<li class="" data-start="422" data-end="607">
<p class="" data-start="424" data-end="607"><strong data-start="424" data-end="438">Index Scan</strong><br data-start="438" data-end="441" />Parcours d’un index pour chercher les lignes correspondantes.<br data-start="504" data-end="507" /><em data-start="512" data-end="526">À surveiller</em> : Rapide si l’index est bien choisi. Peut devenir lent avec beaucoup de <em data-start="599" data-end="606">loops</em>.</p>
</li>
<li class="" data-start="609" data-end="804">
<p class="" data-start="611" data-end="804"><strong data-start="611" data-end="630">Index Only Scan</strong><br data-start="630" data-end="633" />Comme un Index Scan, mais sans lire la table si toutes les colonnes nécessaires sont déjà dans l’index.<br data-start="740" data-end="743" /><em data-start="748" data-end="762">À surveiller</em> : Ultra-performant ! À viser si possible.</p>
</li>
<li class="" data-start="806" data-end="1031">
<p class="" data-start="808" data-end="1031"><strong data-start="808" data-end="829">Bitmap Index Scan</strong> + <strong data-start="832" data-end="852">Bitmap Heap Scan</strong><br data-start="852" data-end="855" />PostgreSQL construit une &#8220;carte&#8221; des lignes à lire, puis les récupère en une seule passe.<br data-start="946" data-end="949" /><em data-start="954" data-end="968">À surveiller</em> : Très performant pour des filtres avec beaucoup de résultats.</p>
</li>
<li class="" data-start="1033" data-end="1244">
<p class="" data-start="1035" data-end="1244"><strong data-start="1035" data-end="1050">Nested Loop</strong><br data-start="1050" data-end="1053" />Pour chaque ligne de la première table, PostgreSQL cherche dans la seconde.<br data-start="1130" data-end="1133" /><em data-start="1138" data-end="1152">À surveiller</em> : Bien sur de petits volumes, mais peut exploser sur de grandes tables (effet quadratique).</p>
</li>
<li class="" data-start="1246" data-end="1445">
<p class="" data-start="1248" data-end="1445"><strong data-start="1248" data-end="1261">Hash Join</strong><br data-start="1261" data-end="1264" />PostgreSQL construit une table de hachage en mémoire pour faire la jointure.<br data-start="1342" data-end="1345" /><em data-start="1350" data-end="1364">À surveiller</em> : Performant si la mémoire le permet. Attention à la taille des jeux de données.</p>
</li>
<li class="" data-start="1447" data-end="1621">
<p class="" data-start="1449" data-end="1621"><strong data-start="1449" data-end="1463">Merge Join</strong><br data-start="1463" data-end="1466" />Jointure optimisée entre deux sources déjà triées.<br data-start="1518" data-end="1521" /><em data-start="1526" data-end="1540">À surveiller</em> : Excellent en perfs si les colonnes jointes sont indexées ou triées à l’avance.</p>
</li>
<li class="" data-start="1623" data-end="1794">
<p class="" data-start="1625" data-end="1794"><strong data-start="1625" data-end="1638">Aggregate</strong><br data-start="1638" data-end="1641" />Calcule une agrégation (COUNT, SUM, AVG, &#8230;).<br data-start="1696" data-end="1699" data-is-only-node="" /><em data-start="1704" data-end="1718">À surveiller</em> : Peut être coûteux si l’agrégation se fait sur de gros volumes sans index.</p>
</li>
<li class="" data-start="1796" data-end="1959">
<p class="" data-start="1798" data-end="1959"><strong data-start="1798" data-end="1806">Sort</strong><br data-start="1806" data-end="1809" />Trie les données selon une ou plusieurs colonnes.<br data-start="1860" data-end="1863" /><em data-start="1868" data-end="1882">À surveiller</em> : Peut consommer beaucoup de mémoire ; un bon index peut éviter cette étape.</p>
</li>
<li class="" data-start="1961" data-end="2136">
<p class="" data-start="1963" data-end="2136"><strong data-start="1963" data-end="1972">Limit</strong><br data-start="1972" data-end="1975" />Tronque le résultat à N lignes.<br data-start="2010" data-end="2013" /><em data-start="2018" data-end="2032">À surveiller</em> : Très utile combiné avec ORDER BY, car PostgreSQL peut s’arrêter dès qu’il a assez de lignes triées.</p>
</li>
<li class="" data-start="2138" data-end="2327">
<p class="" data-start="2140" data-end="2327"><strong data-start="2140" data-end="2152">CTE Scan</strong><br data-start="2152" data-end="2155" />Utilisé quand vous avez une clause WITH (CTE – Common Table Expression).<br data-start="2231" data-end="2234" /><em data-start="2239" data-end="2253">À surveiller</em> : Si le CTE n’est pas matérialisé, il peut être recalculé à chaque appel.</p>
</li>
</ul>
<h2>Options utiles de EXPLAIN / EXPLAIN ANALYZE</h2>
<p>La commande EXPLAIN (et sa variante EXPLAIN ANALYZE) peut être enrichie avec plusieurs options facultatives pour mieux comprendre ce que PostgreSQL fait avec vos requêtes. Voici une présentation des principales.</p>
<h4>ANALYZE : exécuter la requête pour de vrai</h4>
<p>Cette option (souvent appelée “EXPLAIN ANALYZE”) <strong>exécute réellement la requête</strong> et mesure les temps d&#8217;exécution.</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'foo@example.com'; </pre>
<p>Sans cette option, PostgreSQL ne fait qu’estimer le plan, il ne l’exécute pas réellement.</p>
<h4>VERBOSE: plus de détails sur les colonnes et les expressions</h4>
<p>Affiche le nom exact des colonnes internes et les expressions utilisées dans chaque étape du plan.</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, VERBOSE) SELECT name FROM users WHERE age &amp;gt; 30; </pre>
<p>Très utile quand on travaille avec des fonctions, des agrégats ou des vues complexes.</p>
<h4>BUFFERS : détail des lectures mémoire et disque</h4>
<p>Montre combien de blocs de données ont été lus en mémoire (cache partagé) et depuis le disque.</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE status = 'completed'; </pre>
<p>Idéal pour identifier si une requête est ralentie par des accès disques trop nombreux.</p>
<h4>WAL : suivi des écritures dans le journal de transactions</h4>
<p>Affiche l’impact de la requête sur le WAL (Write-Ahead Logging), c’est-à-dire les écritures nécessaires à la durabilité.</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, WAL) INSERT INTO logs SELECT * FROM events; </pre>
<p>Surtout utile pour comprendre le coût caché des requêtes d’écriture.</p>
<h4>COSTS : afficher ou masquer les coûts estimés</h4>
<p>Permet de désactiver les lignes cost=&#8230; si on veut se concentrer uniquement sur les temps réels (actual time).</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, COSTS OFF) SELECT * FROM users; </pre>
<p>Pratique pour alléger la lecture d’un plan quand on n’a pas besoin des estimations.</p>
<h4>SETTINGS : voir les paramètres ayant influencé le plan</h4>
<p>Affiche les paramètres de configuration de PostgreSQL qui ont eu un impact sur la génération du plan.</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, SETTINGS) SELECT * FROM users; </pre>
<p>Très utile pour le debug avancé, ou si certains paramètres sont modifiés via SET.</p>
<h4>SUMMARY : afficher ou non les temps globaux</h4>
<p>Contrôle l’affichage du résumé final (Planning Time, Execution Time).</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, SUMMARY OFF) SELECT * FROM users; </pre>
<p>Par défaut activé, mais vous pouvez le désactiver si vous ne souhaitez pas ces infos à la fin du plan.</p>
<h4>TIMING : activer ou désactiver la mesure des temps internes</h4>
<p>PostgreSQL mesure le actual time pour chaque nœud du plan. Cette option permet de désactiver ces mesures (utile pour les très petites requêtes ou les benchmarks massifs).</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM users; </pre>
<p>Désactive les mesures fines, ce qui peut légèrement améliorer les performances du plan d’analyse lui-même.</p>
<h4>FORMAT : changer la sortie (TEXT, JSON, YAML)</h4>
<p>Permet d’obtenir un plan dans un format structuré (parfait pour les outils externes comme explain.dalibo.com).</p>
<pre class="brush: sql; title: ; notranslate"> EXPLAIN (ANALYZE, FORMAT JSON) SELECT * FROM users; </pre>
<p>Pratique pour générer un plan visuel, l’analyser en script, ou l’intégrer dans des outils de perf.</p>
<h2>OK super, mais une fois qu&#8217;on sait ça, on en fait quoi ?</h2>
<p data-start="260" data-end="525">On pourrait passer des heures à décortiquer les différentes lignes d&#8217;un plan d&#8217;exécution sans pour autant avancer plus que ça. L&#8217;important est maintenant de savoir quoi en faire. Parce que c&#8217;est bien beau de l&#8217;afficher, encore faut-il savoir quoi y chercher.</p>
<p data-start="260" data-end="525">Voici une petite liste non exhaustive des indices à repérer dans un plan d&#8217;exécution qui pourraient vous mener à une raison pour la lenteur de votre requête :</p>
<h3><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Une lecture séquentielle sur une grosse table :</h3>
<p class="" data-start="659" data-end="678"><strong data-start="659" data-end="674">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">Seq Scan on users  (rows=100000)<br />
</code></div>
</div>
<p><strong data-start="725" data-end="755">Pourquoi c’est un souci</strong><br data-start="755" data-end="758" />PostgreSQL lit toute la table ligne par ligne. C’est très lent et parfaitement inutile, surtout si vous ne voulez que quelques lignes de cette table.</p>
<p><strong data-start="861" data-end="887">Comment le voir</strong></p>
<ul data-start="890" data-end="992">
<li class="" data-start="890" data-end="914">On note la présence d&#8217;un nœud SEQ SCAN dans notre plan d&#8217;exécution</li>
<li class="" data-start="915" data-end="992">Nombre de lignes parcourues très élevé (puce rows ou présence de row removed by filter)</li>
</ul>
<p><strong data-start="994" data-end="1010">Solutions</strong></p>
<ul data-start="1011" data-end="1184">
<li class="" data-start="1011" data-end="1085">Ajouter un index sur la colonne filtrée peut souvent aider dans ce genre de cas.</li>
<li class="" data-start="1086" data-end="1184">Réécrire la requête pour qu’elle utilise une clause mieux optimisée (exclusion, inclusion&#8230;)</li>
</ul>
<h3><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Trop de Loop</h3>
<p class="" data-start="1242" data-end="1261"><strong data-start="1242" data-end="1257">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">Index Scan using users_pkey on users  (loops=10000)<br />
</code></div>
</div>
<p class="" data-start="1327" data-end="1489"><strong data-start="1327" data-end="1357">Pourquoi c’est un souci</strong><br data-start="1357" data-end="1360" />Une opération lente est répétée pour chaque ligne de l’opération extérieure, souvent dans une jointure en boucle (Nested Loop).</p>
<p class="" data-start="1491" data-end="1519"><strong data-start="1491" data-end="1517">Comment le voir</strong></p>
<ul data-start="1520" data-end="1575">
<li data-start="1520" data-end="1535">Nombre de loop très élevés</li>
<li class="" data-start="1520" data-end="1535">Souvent précédé d&#8217;un Nested Loop plus haut dans le plan d&#8217;exécution</li>
</ul>
<p class="" data-start="1577" data-end="1593"><strong data-start="1577" data-end="1593">Solutions</strong></p>
<ul data-start="1594" data-end="1764">
<li class="" data-start="1594" data-end="1657">
<p class="" data-start="1596" data-end="1657">Changer le type de jointure pour quelque chose de plus simple a traiter (hash join ou merge join)</p>
</li>
<li class="" data-start="1658" data-end="1715">
<p class="" data-start="1660" data-end="1715">Réécrire la requête pour faire moins de &#8220;aller-retours&#8221;</p>
</li>
<li class="" data-start="1716" data-end="1764">
<p class="" data-start="1718" data-end="1764">Ajouter des index pour faciliter les jointures</p>
</li>
</ul>
<h3 data-start="1718" data-end="1764"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Des estimations loin de la réalité</h3>
<p class="" data-start="1826" data-end="1845"><strong data-start="1826" data-end="1841">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">rows=10   actual rows=10000<br />
</code></div>
</div>
<p class="" data-start="1887" data-end="2017"><strong data-start="1887" data-end="1917">Pourquoi c’est un souci</strong><br data-start="1917" data-end="1920" />PostgreSQL s’est trompé dans son estimation, ce qui l’a peut-être mené à choisir un mauvais plan.</p>
<p class="" data-start="2019" data-end="2179"><strong data-start="2019" data-end="2045">Comment le voir</strong><br data-start="2045" data-end="2048" />Comparez le nombre de rows (estimation) avec les actual rows. Si l’écart est très important, les statistiques sont probablement obsolètes.</p>
<p class="" data-start="2181" data-end="2197"><strong data-start="2181" data-end="2197">Solutions</strong></p>
<ul data-start="2198" data-end="2421">
<li class="" data-start="2198" data-end="2259">
<p class="" data-start="2200" data-end="2259">Rafraichir les statistiques dans ce cas ne peut pas faire de mal : Analyze ou vaccuum analyze si nécessaire.</p>
</li>
<li class="" data-start="2260" data-end="2352">
<p class="" data-start="2262" data-end="2352">Ajuster les statistiques</p>
</li>
<li class="" data-start="2353" data-end="2421">
<p class="" data-start="2355" data-end="2421">Éviter les expressions trop complexes qui biaisent les estimations</p>
</li>
</ul>
<h3 data-start="2355" data-end="2421"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Un tri qui consomme trop</h3>
<p class="" data-start="2494" data-end="2513"><strong data-start="2494" data-end="2509">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">Sort  (Sort Method: quicksort / external merge, Memory: 100MB)<br />
</code></div>
</div>
<p class="" data-start="2590" data-end="2723"><strong data-start="2590" data-end="2620">Pourquoi c’est un souci</strong><br data-start="2620" data-end="2623" />Le tri est trop gros, PostgreSQL n’arrive plus à le faire en RAM → il passe sur disque → c’est lent.</p>
<p class="" data-start="2725" data-end="2753"><strong data-start="2725" data-end="2751">Comment le voir</strong></p>
<ul data-start="2754" data-end="2879">
<li class="" data-start="2754" data-end="2813">
<p class="" data-start="2756" data-end="2813">Nœud SORT avec un SORT METHOD lent (EXTERNAL MERGE)</p>
</li>
<li class="" data-start="2814" data-end="2843">
<p class="" data-start="2816" data-end="2843">Consommation mémoire élevée</p>
</li>
<li class="" data-start="2844" data-end="2879">
<p class="" data-start="2846" data-end="2879">Étape qui prend beaucoup de temps</p>
</li>
</ul>
<p class="" data-start="2881" data-end="2897"><strong data-start="2881" data-end="2897">Solutions</strong></p>
<ul data-start="2898" data-end="3077">
<li class="" data-start="2898" data-end="2962">
<p class="" data-start="2900" data-end="2962">Ajouter un index sur les colonnes utilisées dans le ORDER BY</p>
</li>
<li class="" data-start="2963" data-end="3027">
<p class="" data-start="2965" data-end="3027">Réduire la quantité de lignes à trier avec un LIMIT en amont</p>
</li>
<li class="" data-start="3028" data-end="3077">
<p class="" data-start="3030" data-end="3077">Éviter les sous-requêtes non filtrées avant tri</p>
</li>
</ul>
<h3 data-start="3030" data-end="3077"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Une clause Limit inefficace</h3>
<p class="" data-start="3164" data-end="3183"><strong data-start="3164" data-end="3179">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">Sort → Limit<br />
</code></div>
</div>
<p class="" data-start="3210" data-end="3355"><strong data-start="3210" data-end="3240">Pourquoi c’est un souci</strong><br data-start="3240" data-end="3243" />PostgreSQL trie <em data-start="3259" data-end="3266">toute</em> la table avant d’en extraire 10 lignes. S’il y a 1 million de lignes, c’est pas optimal.</p>
<p class="" data-start="3357" data-end="3385"><strong data-start="3357" data-end="3383">Comment le voir</strong></p>
<ul data-start="3386" data-end="3479">
<li class="" data-start="3386" data-end="3422">
<p class="" data-start="3388" data-end="3422">Le SORT est au-dessus du LIMIT</p>
</li>
<li class="" data-start="3423" data-end="3479">
<p class="" data-start="3425" data-end="3479">Le tri prend beaucoup de temps même pour un LIMIT 10</p>
</li>
</ul>
<p class="" data-start="3481" data-end="3497"><strong data-start="3481" data-end="3497">Solutions</strong></p>
<ul data-start="3498" data-end="3678">
<li class="" data-start="3498" data-end="3588">
<p class="" data-start="3500" data-end="3588">Utiliser un index qui permet de lire déjà trié (ORDER BY created_at DESC → index DESC)</p>
</li>
<li class="" data-start="3589" data-end="3678">
<p class="" data-start="3591" data-end="3678">Repenser la requête pour éviter le tri global (ex : préfiltrage ou pagination efficace)</p>
</li>
</ul>
<h3 data-start="3591" data-end="3678"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50e.png" alt="🔎" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Des filtres appliqués trop tard</h3>
<p class="" data-start="3744" data-end="3763"><strong data-start="3744" data-end="3759">Symptôme</strong> :</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-text">Rows Removed by Filter: 999000<br />
</code></div>
</div>
<p class="" data-start="3808" data-end="3948"><strong data-start="3808" data-end="3838">Pourquoi c’est un souci</strong><br data-start="3838" data-end="3841" />Le filtre est appliqué après avoir lu la majorité des lignes → PostgreSQL fait beaucoup de travail inutile.</p>
<p class="" data-start="3950" data-end="3978"><strong data-start="3950" data-end="3976">Comment le voir</strong></p>
<ul data-start="3979" data-end="4071">
<li class="" data-start="3979" data-end="4023">
<p class="" data-start="3981" data-end="4023">Présence de FILTER (&#8230;) en bas de plan</p>
</li>
<li class="" data-start="4024" data-end="4071">
<p class="" data-start="4026" data-end="4071">Très grand nombre de ROWS REMOVED BY FILTER</p>
</li>
</ul>
<p class="" data-start="4073" data-end="4089"><strong data-start="4073" data-end="4089">Solutions</strong></p>
<ul data-start="4090" data-end="4311">
<li class="" data-start="4090" data-end="4120">
<p class="" data-start="4092" data-end="4120">Indexer la colonne du filtre</p>
</li>
<li class="" data-start="4121" data-end="4203">
<p class="" data-start="4123" data-end="4203">Réécrire la requête pour que le filtre soit pris en compte plus tôt dans le plan</p>
</li>
<li class="" data-start="4204" data-end="4311">
<p class="" data-start="4206" data-end="4311">Éviter les fonctions non indexables dans le WHERE (ex : LOWER(email) → préférer un index fonctionnel)</p>
</li>
</ul>
<p>&nbsp;</p>
<h1>Conclusion</h1>
<p class="" data-start="313" data-end="605">Comme souvent avec PostgreSQL, il est difficile — voire impossible — de tout couvrir en un seul article. Chaque plan d&#8217;exécution est unique, chaque requête a ses subtilités, et chaque base de données a ses petites surprises. Il n’existe pas de recette miracle qui marcherait à tous les coups.</p>
<p class="" data-start="607" data-end="813">Mais avec les bons réflexes, quelques outils et un peu de méthode, on peut rapidement progresser : repérer les symptômes, lire les signes, poser les bonnes questions… et surtout, tester, encore et toujours.</p>
<p class="" data-start="607" data-end="813">EXPLAIN ANALYZE n’est pas réservé aux DBA ou aux experts en perfs. C’est un compagnon de route pour toute personne qui écrit des requêtes, et qui veut comprendre ce qui se passe sous le capot.</p>
<p class="" data-start="1011" data-end="1213">Et si vous vous sentez encore un peu perdu·e face à un plan trop verbeux : pas de panique. Avec l’habitude, ça devient un langage qu’on apprend à lire presque instinctivement. Et ça commence maintenant.</p>
<p><strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/pruning-de-partitions-sous-postgresql/" rel="bookmark" title="7 décembre 2020">&#8220;Pruning&#8221; de partitions sous PostgreSQL ou comment bien élaguer !</a> (Capdata team) [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/nouveautes-pg_stat_statements-avec-postgresql-15/" rel="bookmark" title="16 mars 2023">Nouveautés pg_stat_statements avec PostgreSQL 15</a> (David Baffaleuf) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/postgresql-18-des-io-asynchrones-performantes/" rel="bookmark" title="19 novembre 2025">PostgreSQL 18 : des IO asynchrones performantes !</a> (Emmanuel RAMI) [Non classéPostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/" rel="bookmark" title="26 février 2025">PostgreSQL : optimiser vos opérations vacuum et analyze !</a> (Emmanuel RAMI) [PostgreSQL]</li>
</ul>
<p><!-- Similar Posts took 2.844 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%3D10697&#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%3D10697&#038;title=PostgreSQL%20Basics%20%3A%20lire%20un%20plan%20d%E2%80%99ex%C3%A9cution%20comme%20un%C2%B7e%20pro%20%28ou%20presque%29" style="font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="linkedin" title="Share on Linkedin" class="synved-share-image synved-social-image synved-social-image-share" width="24" height="24" style="display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://blog.capdata.fr/wp-content/plugins/social-media-feather/synved-social/image/social/regular/48x48/linkedin.png" /></a><a class="synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox" data-provider="mail" rel="nofollow" title="Share by email" href="mailto:?subject=PostgreSQL%20Basics%20%3A%20lire%20un%20plan%20d%E2%80%99ex%C3%A9cution%20comme%20un%C2%B7e%20pro%20%28ou%20presque%29&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10697" 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/postgresql-basics-lire-un-plan-dexecution/">PostgreSQL Basics : lire un plan d&#8217;exécution comme un·e pro (ou presque)</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/postgresql-basics-lire-un-plan-dexecution/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PostgreSQL : optimiser vos opérations vacuum et analyze !</title>
		<link>https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/</link>
					<comments>https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Wed, 26 Feb 2025 11:00:21 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[analyze]]></category>
		<category><![CDATA[ring buffer]]></category>
		<category><![CDATA[vacuum]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10670</guid>

					<description><![CDATA[<p>Hello pour commencer cette année 2025 , voici un petit article PostgreSQL ou l&#8217;on vous présente comment optimiser les opérations de maintenance que sont les VACUUM et les ANALYZE. Ces 2 opérations sont essentielles pour conserver des performances optimales pour&#8230; <a href="https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/" 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-optimiser-vos-operations-vacuum-et-analyze/">PostgreSQL : optimiser vos opérations vacuum et analyze !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10670&#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%3D10670&#038;title=PostgreSQL%20%3A%20optimiser%20vos%20op%C3%A9rations%20vacuum%20et%20analyze%20%21" 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=PostgreSQL%20%3A%20optimiser%20vos%20op%C3%A9rations%20vacuum%20et%20analyze%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10670" 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><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10677" src="https://blog.capdata.fr/wp-content/uploads/2025/02/vacuum-300x200.png" alt="" width="300" height="200" srcset="https://blog.capdata.fr/wp-content/uploads/2025/02/vacuum-300x200.png 300w, https://blog.capdata.fr/wp-content/uploads/2025/02/vacuum-768x513.png 768w, https://blog.capdata.fr/wp-content/uploads/2025/02/vacuum.png 800w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>Hello</p>
<p>pour commencer cette année 2025 , voici un petit article PostgreSQL ou l&#8217;on vous présente comment optimiser les opérations de maintenance que sont les VACUUM et les ANALYZE.</p>
<p>Ces 2 opérations sont essentielles pour conserver des performances optimales pour notre instance et garantir au planner de construire des plans d&#8217;exécutions optimisés.</p>
<p>les opérations VACUUM et/ou ANALYZE peuvent être longues et sources de nombreuses écritures dans les WALs sur des tables volumineuses.<br />
C&#8217;est pourquoi, et ce depuis la version <strong>PostgreSQL 16</strong>, il est possible de modifier le comportement de ces opérations en affectant une taille de buffer. Il s&#8217;agit du &#8220;<a href="https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-VACUUM-BUFFER-USAGE-LIMIT">buffer_usage_limit</a>&#8220;.</p>
<p>&nbsp;</p>
<h2>Principe de fonctionnement.</h2>
<p>Ce procédé s&#8217;appuie sur le principe de &#8220;ring buffer&#8221; configuré pour PostgreSQL.</p>
<p>Attention, à ne pas confondre, évidement, avec les &#8220;rings buffer&#8221; de SQL Server !!</p>
<p>Pour rappel, PostgreSQL utilise cette stratégie de &#8220;ring buffer&#8221; afin de dédier un espace mémoire pour les opérations lourdes , telles, la lecture séquentielle sur une table volumineuse, un CREATE TABLE AS SELECT, un COPY&#8230;. mais aussi un VACUUM !</p>
<p>En fait, cet espace est utilisé pour éviter de &#8220;flusher&#8221; sur disque de manière trop brutale les pages en mémoire montées dans le &#8220;<strong>shared buffer</strong>&#8220;. Cela pénaliserait en grande partie toute opération concurrente à notre traitement actif puisqu&#8217;elle n&#8217;aurait plus d&#8217;espace pour mettre ses propres pages en mémoire.</p>
<p>Jusqu&#8217;à la version PostgreSQL 16, cet espace mémoire était défini à<strong> 256Ko</strong>. Ainsi, au cours d&#8217;une lecture séquentielle, chaque page  de <strong>8Ko</strong> par défaut, est montée en mémoire dans cet espace si le nombre de pages totales à traiter pour la table, dépasse 1/4 du paramètre &#8220;<strong>shared_buffer</strong>&#8220;.</p>
<p>Il en est de même pour une opération VACUUM ou ANALYZE qui utilise également ce ring buffer et permet d&#8217;optimiser cette opération.</p>
<p>&nbsp;</p>
<h3>Nouveautés PostgreSQL 16 et PostgreSQL 17</h3>
<p>&nbsp;</p>
<p>Depuis la version PostgreSQL 16, il est possible de configurer la taille du buffer de façon unitaire. Par exemple, lors d&#8217;un VACUUM, il est tout à fait possible de choisir une valeur pour &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8220;.</p>
<p>Depuis le version PostgreSQL 17, la valeur par défaut affectée à &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8221; est de <strong>2Mo</strong>.</p>
<p>Il vous est possible de paramétrer la valeur de <strong>128Ko</strong> jusqu&#8217;à <strong>16Go</strong>. Attention, cependant, cette valeur ne peux excéder <strong>1/8</strong> du paramètre &#8220;<strong>shared_buffer</strong>&#8220;.<br />
Si vous faites le calcul, pour un serveur comportant <strong>32Go de RAM</strong>, vous ne pourrez obtenir, au plus, <strong>1Go</strong> pour votre ring buffer.</p>
<p>&nbsp;</p>
<h5>Cas d&#8217;utilisation pour un VACUUM</h5>
<p>&nbsp;</p>
<p>Sur une instance PostgreSQL 13, nous lançons un VACUUM simple sur une table de 2,5Go. Nous utilisons une base exemple créée via &#8220;pgbench&#8221;.</p>
<p>Nous avons utilisé les options &#8220;<strong>DISABLE_PAGE_SKIPPING</strong>&#8221; pour analyser, dans un premier temps, tous les blocs de notre table et ne pas sur baser sur les informations de la visibility_map.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5433) [pgbenchmark] primaire $  vacuum (verbose,DISABLE_PAGE_SKIPPING) public.pgbench_accounts;
INFO: 00000: aggressively vacuuming &quot;public.pgbench_accounts&quot;
LOCATION: lazy_scan_heap, vacuumlazy.c:797
INFO: 00000: &quot;pgbench_accounts&quot;: found 0 removable, 20000000 nonremovable row versions in 327869 out of 327869 pages
DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 196215
There were 0 unused item identifiers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 1.34 s, system: 0.67 s, elapsed: 18.90 s.
LOCATION: lazy_scan_heap, vacuumlazy.c:1759
VACUUM
Time: 18920.292 ms (00:18.920)</pre>
<p>&nbsp;</p>
<p>Le temps passé pour cette opération est d&#8217;un peu plus de 18 secondes en temps CPU pour analyser les 327869 blocs de notre table. Soit une taille de 2.5Go.</p>
<p>Nous effectuons la même opération sur cette même table, mais sur un moteur <strong>PostgreSQL 17</strong>. Nous positionnons le paramètre <strong>BUFFER_USAGE_LIMIT</strong>  à <strong>8Mo</strong>.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (verbose,DISABLE_PAGE_SKIPPING,BUFFER_USAGE_LIMIT '8MB') public.pgbench_accounts;
INFO: 00000: aggressively vacuuming &quot;pgbenchmark.public.pgbench_accounts&quot;
LOCATION: heap_vacuum_rel, vacuumlazy.c:475
INFO: 00000: finished vacuuming &quot;pgbenchmark.public.pgbench_accounts&quot;: index scans: 0
pages: 0 removed, 327869 remain, 327869 scanned (100.00% of total)
tuples: 0 removed, 20000000 remain, 0 are dead but not yet removable
removable cutoff: 208163, which was 0 XIDs old when operation ended
new relfrozenxid: 208163, which is 5 XIDs ahead of previous value
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed
avg read rate: 134.208 MB/s, avg write rate: 0.034 MB/s
buffer usage: 330350 hits, 325508 misses, 83 dirtied
WAL usage: 84 records, 83 full page images, 684232 bytes
system usage: CPU: user: 1.41 s, system: 0.64 s, elapsed: 18.94 s
LOCATION: heap_vacuum_rel, vacuumlazy.c:763
VACUUM
Time: 18955.006 ms (00:18.955)</pre>
<p>&nbsp;</p>
<p>C&#8217;est  à peu de chose près, dans le même temps d&#8217;exécution. soir 18 secondes.</p>
<p>La suite consiste à redémarrer l&#8217;instance PostgreSQL 17 et constater les temps d&#8217;exécution pour chaque occurrence de lancement.<br />
Nous exécutons donc, les mêmes ordres VACUUM, mais sans l&#8217;option &#8220;<strong>DISABLE_PAGE_SKIPPING</strong>&#8221;</p>
<p>Sur la version PostgreSQL 13, nous voyons qu&#8217;à la première exécution, juste après redémarrage, nous sommes à 32 millisecondes. Et à chaque exécution suivante, nous ne descendons pas en dessous de 15 millisecondes&#8230;.</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5433) [pgbenchmark] primaire $  vacuum public.pgbench_accounts;
VACUUM
Time: 32.149 ms

(postgres@[local]:5433) [pgbenchmark] primaire $  vacuum public.pgbench_accounts;
VACUUM
Time: 15.001 ms

(postgres@[local]:5433) [pgbenchmark] primaire $  vacuum public.pgbench_accounts;
VACUUM
Time: 15.295 ms</pre>
<p>&nbsp;</p>
<p>En version PostgreSQL 17, nous faisons également un &#8220;flush&#8221; des pages dans le buffer cache à chaque exécution, tout en modifiant la valeur de &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8220;.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (BUFFER_USAGE_LIMIT '128kB') public.pgbench_accounts;
VACUUM
Time: 18.098 ms

(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (BUFFER_USAGE_LIMIT '8MB') public.pgbench_accounts;
VACUUM
Time: 6.461 ms

(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (BUFFER_USAGE_LIMIT '16MB') public.pgbench_accounts;
VACUUM
Time: 4.333 ms</pre>
<p>&nbsp;</p>
<p>Le constat est simple, plus nous augmentons le &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8220;, et plus le temps d&#8217;exécution du VACUUM diminue.</p>
<p>Nous comprendrons donc que sur une table de plus de 100Go, le gain peut être assez important.</p>
<p>&nbsp;</p>
<h5>Cas d&#8217;utilisation pour un ANALYZE</h5>
<p>&nbsp;</p>
<p>Pour l&#8217;instance PostgreSQL 13, nous exécutons le calcul de statistiques sur cette même table</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5433) [pgbenchmark] primaire $  vacuum (analyze,verbose) public.pgbench_accounts;
INFO: 00000: vacuuming &quot;public.pgbench_accounts&quot;
LOCATION: lazy_scan_heap, vacuumlazy.c:802
INFO: 00000: &quot;pgbench_accounts&quot;: found 0 removable, 52 nonremovable row versions in 1 out of 327869 pages
DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 196278
There were 0 unused item identifiers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
LOCATION: lazy_scan_heap, vacuumlazy.c:1759
INFO: 00000: analyzing &quot;public.pgbench_accounts&quot;
LOCATION: do_analyze_rel, analyze.c:336
INFO: 00000: &quot;pgbench_accounts&quot;: scanned 30000 of 327869 pages, containing 1830000 live rows and 0 dead rows; 30000 rows in sample, 20000009 estimated total rows
LOCATION: acquire_sample_rows, analyze.c:1190
VACUUM
Time: 29526.404 ms (00:29.526)</pre>
<p>&nbsp;</p>
<p>Nous sommes autour de 29 secondes pour analyser 30000 pages sur les 32769 que composent cette table.<br />
Le sample est choisi en fonction de la valeur de &#8220;<strong>default_statistics_target</strong>&#8220;, par défaut à 100, avec 30000 lignes analysées par défaut.</p>
<p>&nbsp;</p>
<p>Sur la version PostgreSQL 17, les résultats sont les suivants</p>
<pre class="brush: sql; title: ; notranslate">(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (analyze,verbose,BUFFER_USAGE_LIMIT '128kB') public.pgbench_accounts;
INFO: 00000: vacuuming &quot;pgbenchmark.public.pgbench_accounts&quot;
LOCATION: heap_vacuum_rel, vacuumlazy.c:480
INFO: 00000: finished vacuuming &quot;pgbenchmark.public.pgbench_accounts&quot;: index scans: 0
pages: 0 removed, 327869 remain, 1 scanned (0.00% of total)
tuples: 0 removed, 20000000 remain, 0 are dead but not yet removable
removable cutoff: 208163, which was 0 XIDs old when operation ended
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed
avg read rate: 46.211 MB/s, avg write rate: 0.000 MB/s
buffer usage: 37 hits, 100 misses, 0 dirtied
WAL usage: 0 records, 0 full page images, 0 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.01 s
LOCATION: heap_vacuum_rel, vacuumlazy.c:763
INFO: 00000: analyzing &quot;public.pgbench_accounts&quot;
LOCATION: do_analyze_rel, analyze.c:321
INFO: 00000: &quot;pgbench_accounts&quot;: scanned 30000 of 327869 pages, containing 1830000 live rows and 0 dead rows; 30000 rows in sample, 20000009 estimated total rows
LOCATION: acquire_sample_rows, analyze.c:1301
VACUUM
Time: 8151.201 ms (00:08.151)

(postgres@[local]:5437) [pgbenchmark] primaire $  vacuum (analyze,BUFFER_USAGE_LIMIT '8MB') public.pgbench_accounts;
VACUUM
Time: 7282.546 ms (00:07.283)</pre>
<p>&nbsp;</p>
<p>Les différences de gains sont moins impressionnantes que sur un simple VACUUM à chaque changement de &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8220;, mais on voit qu&#8217;en version PostgreSQL 17, nous sommes tout de même 4 fois plus rapide qu&#8217;en version PostgreSQL 13.</p>
<p>&nbsp;</p>
<h4>Remarques</h4>
<p>Gardez à l&#8217;esprit que la valeur de &#8220;<strong>BUFFER_USAGE_LIMIT&#8221;</strong> est plafonnée à <strong>1/8 </strong>de<strong> &#8220;shared_buffer&#8221;</strong>. Inutile donc de mettre à 1024Mo, si vous ne possédez que 8Go de RAM.</p>
<p>Attention si vous mettez une valeur trop grande, les transactions concurrentes effectuant des lectures séquentielles seront pénalisées par les opérations VACUUM. D&#8217;ailleurs, il est possible de mettre &#8220;<strong>BUFFER_USAGE_LIMIT</strong>&#8221; à 0, mais ceci n&#8217;est pas conseillé lors d&#8217;une activité transactionnelle en cours.</p>
<p>Pour aller plus loin dans l&#8217;optimisation d&#8217;une opération de vacuum, vous pouvez également passer le paramètre &#8220;<strong>INDEX_CLEANUP</strong>&#8221; à <strong>off</strong>. Ceci aura pour effet de ne pas s&#8217;occuper de traiter les entrées des index qui pointent sur les lignes mortes de la table.<br />
Un &#8220;<strong>REINDEX</strong>&#8221; sera alors nécessaire à la fin du VACUUM sur les index de la table.</p>
<p>De plus, il est possible de positionner l&#8217;option &#8220;<strong>SKIP_DATABASE_STATS</strong>&#8221; afin d&#8217;indiquer à l&#8217;ordre VACUUM de ne pas rechercher l&#8217;ID de transaction le plus ancien pour l&#8217;ensemble des tables de la base et de geler celui-ci (datfrozenid).</p>
<p>Les opérations VACUUM sur les grosses tables seront bien entendu optimisées mais attention aux plages de maintenance choisies !!</p>
<p>&nbsp;</p>
<p>Bonne journée à vous.</p>
<p>Emmanuel Rami</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-18-des-io-asynchrones-performantes/" rel="bookmark" title="19 novembre 2025">PostgreSQL 18 : des IO asynchrones performantes !</a> (Emmanuel RAMI) [Non classé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/pruning-de-partitions-sous-postgresql/" rel="bookmark" title="7 décembre 2020">&#8220;Pruning&#8221; de partitions sous PostgreSQL ou comment bien élaguer !</a> (Capdata team) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/requetes-consommatrices-sous-postgresql-episode-1/" rel="bookmark" title="24 mai 2016">Requêtes consommatrices sous PostgreSQL (épisode 1)</a> (David Baffaleuf) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/nouveautes-mysql-8-0-les-histogrammes/" rel="bookmark" title="25 juin 2019">Nouveautés MySQL 8.0 : Les Histogrammes</a> (Capdata team) [MySQL]</li>
</ul>
<p><!-- Similar Posts took 3.237 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%3D10670&#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%3D10670&#038;title=PostgreSQL%20%3A%20optimiser%20vos%20op%C3%A9rations%20vacuum%20et%20analyze%20%21" 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=PostgreSQL%20%3A%20optimiser%20vos%20op%C3%A9rations%20vacuum%20et%20analyze%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10670" 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/postgresql-optimiser-vos-operations-vacuum-et-analyze/">PostgreSQL : optimiser vos opérations vacuum et analyze !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/postgresql-optimiser-vos-operations-vacuum-et-analyze/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>La montée de version en zero-downtime : merci la réplication !</title>
		<link>https://blog.capdata.fr/index.php/la-montee-de-version-en-zero-downtime-merci-la-replication/</link>
					<comments>https://blog.capdata.fr/index.php/la-montee-de-version-en-zero-downtime-merci-la-replication/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Thu, 19 Dec 2024 10:28:41 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[réplication logique]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10633</guid>

					<description><![CDATA[<p>Introduction : Dans le monde des bases de données, garantir une disponibilité continue est une exigence incontournable, surtout pour les systèmes critiques où chaque minute d&#8217;arrêt peut entraîner des pertes significatives. Lorsqu’il s’agit de migrer une base de données vers&#8230; <a href="https://blog.capdata.fr/index.php/la-montee-de-version-en-zero-downtime-merci-la-replication/" 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/la-montee-de-version-en-zero-downtime-merci-la-replication/">La montée de version en zero-downtime : merci la réplication !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10633&#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%3D10633&#038;title=La%20mont%C3%A9e%20de%20version%20en%20zero-downtime%20%3A%20merci%20la%20r%C3%A9plication%20%21" 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=La%20mont%C3%A9e%20de%20version%20en%20zero-downtime%20%3A%20merci%20la%20r%C3%A9plication%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10633" 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><h1>Introduction :</h1>
<p>Dans le monde des bases de données, garantir une disponibilité continue est une exigence incontournable, surtout pour les systèmes critiques où chaque minute d&#8217;arrêt peut entraîner des pertes significatives. Lorsqu’il s’agit de migrer une base de données vers une nouvelle version, ce défi prend une toute autre dimension. Comment mettre à jour votre système sans interrompre les services, tout en préservant l’intégrité des données ?</p>
<p>PostgreSQL offre une solution élégante : la réplication logique. Cet outil permet de transférer des données de manière fluide entre différentes versions de PostgreSQL, tout en maintenant la base de données source opérationnelle. Dans cet article, nous allons explorer étape par étape comment utiliser cette fonctionnalité pour réaliser une montée de version sans temps d&#8217;arrêt, du déploiement initial à la bascule finale vers la nouvelle version.</p>
<p>Que vous soyez en train de planifier une migration ou simplement curieux de découvrir les possibilités offertes par PostgreSQL, suivez ce guide pratique qui vous permettra de transformer un défi complexe en une opération maîtrisée et efficace.</p>
<h1>Le test :</h1>
<ol>
<li>
<h3>Préparation</h3>
</li>
</ol>
<p>Pour tester cette nouvelle méthode, nous aurons besoin de deux instances PostgreSQL. Pour cet article j&#8217;ai choisit de démontrer la technique en migrant d&#8217;une version 14 à une version 17 de PostgreSQL.</p>
<p>Je commence donc par installer les versions sur deux machines différentes pouvant communiquer entre elles (c&#8217;est important) :</p>
<p>Sur les deux machines nous pouvons exécuter les commandes suivantes :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-246:~# sudo apt update sudo apt upgrade -y

...

root@ip-192-1-1-246:~# sudo apt -y install gnupg2 wget vim

...

root@ip-192-1-1-246:~# 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@ip-192-1-1-246:~# curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg

root@ip-192-1-1-246:~# sudo apt -y update
Get:1 file:/etc/apt/mirrors/debian.list Mirrorlist [38 B]
Get:2 file:/etc/apt/mirrors/debian-security.list Mirrorlist [47 B]
Hit:3 https://cdn-aws.deb.debian.org/debian bookworm InRelease
Hit:4 https://cdn-aws.deb.debian.org/debian bookworm-updates InRelease
Hit:5 https://cdn-aws.deb.debian.org/debian bookworm-backports InRelease
Hit:6 https://cdn-aws.deb.debian.org/debian-security bookworm-security InRelease
Get:7 http://apt.postgresql.org/pub/repos/apt bookworm-pgdg InRelease [129 kB]
Get:8 http://apt.postgresql.org/pub/repos/apt bookworm-pgdg/main amd64 Packages [359 kB]
Fetched 489 kB in 1s (348 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
</pre>
<p>Puis sur notre première machine :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-246:~# sudo apt install postgresql-14
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
libcommon-sense-perl libgdbm-compat4 libio-pty-perl libipc-run-perl
libjson-perl libjson-xs-perl libllvm16 libperl5.36 libpq5 libsensors-config
libsensors5 libtypes-serialiser-perl libxslt1.1 libz3-4 logrotate perl
perl-modules-5.36 postgresql-client-14 postgresql-client-common
postgresql-common ssl-cert sysstat

...

root@ip-192-1-1-246:~# systemctl status postgresql@14-main.service
● postgresql@14-main.service - PostgreSQL Cluster 14-main
Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled-runtime;&amp;gt;
Active: active (running) since Wed 2024-12-04 09:43:55 UTC; 2min 55s ago
Process: 15248 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect &amp;gt;
Main PID: 15253 (postgres)
Tasks: 7 (limit: 4633)
Memory: 17.3M
CPU: 239ms
CGroup: /system.slice/system-postgresql.slice/postgresql@14-main.service
├─15253 /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresq&amp;gt;
├─15255 &quot;postgres: 14/main: checkpointer &quot;
├─15256 &quot;postgres: 14/main: background writer &quot;
├─15257 &quot;postgres: 14/main: walwriter &quot;
├─15258 &quot;postgres: 14/main: autovacuum launcher &quot;
├─15259 &quot;postgres: 14/main: stats collector &quot;
└─15260 &quot;postgres: 14/main: logical replication launcher &quot;

Dec 04 09:43:53 ip-192-1-1-246 systemd[1]: Starting postgresql@14-main.service&amp;gt;
Dec 04 09:43:55 ip-192-1-1-246 systemd[1]: Started postgresql@14-main.service &amp;gt;
</pre>
<p>Puis sur la deuxième machine :</p>
<pre class="brush: bash; title: ; notranslate">
admin@ip-192-1-1-89:~$ sudo apt install postgresql-17
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
libcommon-sense-perl libgdbm-compat4 libio-pty-perl libipc-run-perl
libjson-perl libjson-xs-perl libllvm16 libperl5.36 libpq5 libsensors-config
libsensors5 libtypes-serialiser-perl libxslt1.1 libz3-4 logrotate perl
perl-modules-5.36 postgresql-client-17 postgresql-client-common
postgresql-common ssl-cert sysstat

admin@ip-192-1-1-89:~$ systemctl status postgresql@17-main.service
● postgresql@17-main.service - PostgreSQL Cluster 17-main
Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled-runtime; &amp;gt;
Active: active (running) since Wed 2024-12-04 09:52:33 UTC; 2min 13s ago
Process: 15235 ExecStart=/usr/bin/pg_ctlcluster --skip-systemctl-redirect 1&amp;gt;
Main PID: 15240 (postgres)
Tasks: 6 (limit: 4633)
Memory: 20.5M
CPU: 332ms
CGroup: /system.slice/system-postgresql.slice/postgresql@17-main.service
├─15240 /usr/lib/postgresql/17/bin/postgres -D /var/lib/postgresql&amp;gt;
├─15241 &quot;postgres: 17/main: checkpointer &quot;
├─15242 &quot;postgres: 17/main: background writer &quot;
├─15244 &quot;postgres: 17/main: walwriter &quot;
├─15245 &quot;postgres: 17/main: autovacuum launcher &quot;
└─15246 &quot;postgres: 17/main: logical replication launcher &quot;

Dec 04 09:52:31 ip-192-1-1-89 systemd[1]: Starting postgresql@17-main.service -&amp;gt;
Dec 04 09:52:33 ip-192-1-1-89 systemd[1]: Started postgresql@17-main.service -&amp;gt;
</pre>
<p>Nos deux instances sont maintenant installées. Sur notre première base de données, nous allons créer une base, avec deux tables, et quelques lignes.</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-246:/etc/postgresql/14/main$ psql
psql (14.15 (Debian 14.15-1.pgdg120+1))
Type &quot;help&quot; for help.
</pre>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">
postgres=# CREATE DATABASE mydb;
CREATE DATABASE
postgres=# \c mydb
You are now connected to database &quot;mydb&quot; as user &quot;postgres&quot;.
mydb=# CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE
mydb=# CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(id),
amount NUMERIC(10,2) NOT NULL,
order_date TIMESTAMP DEFAULT NOW()
);
CREATE TABLE
mydb=# INSERT INTO customers (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');
INSERT 0 3
mydb=# INSERT INTO orders (customer_id, amount) VALUES
(1, 50.75),
(2, 20.00),
(1, 75.00);
INSERT 0 3
</pre>
<h3>2. Configurer la base de données source</h3>
<p>Sur notre première machine, nous allons modifier les paramètres du fichier de configuration de PostgreSQL pour permettre de pouvoir créer la réplication :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-246:~# su - postgres
postgres@ip-192-1-1-246:~$ cd /etc/postgresql/14/main
postgres@ip-192-1-1-246:/etc/postgresql/14/main$ vi postgresql.conf
</pre>
<p>Il s&#8217;agit de modifier les paramètres suivants :</p>
<blockquote><p>wal_level = logical<br />
max_replication_slots = 4<br />
max_wal_senders = 4</p></blockquote>
<p>Nous modifierons ensuite le pg_hba pour rajouter l&#8217;autorisation de connexion entre les deux machines :</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-246:/etc/postgresql/14/main$ vi pg_hba.conf
</pre>
<p>Il suffira de rajouter une ligne :</p>
<blockquote><p>host replication <span class="hljs-attribute">all</span> &lt;destination_ip&gt; scram-sha-256</p>
<p>host replication all &lt;source_ip&gt; scram-sha-256</p>
<p>host all replication &lt;destination_ip&gt; scram-sha-256</p>
<p>host all replication &lt;source-ip&gt; scram-sha-256</p></blockquote>
<p>Il ne faut pas oublier de redémarrer le serveur PostgreSQL une fois ces modifications effectuées :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-246:~# systemctl stop postgresql@14-main.service
root@ip-192-1-1-246:~# systemctl start postgresql@14-main.service
</pre>
<h3>3. Configurer la base de donnée de destination</h3>
<p>Après avoir configuré notre base de donnée depuis laquelle nous allons faire notre migration, il nous faut a présent configurer celle qui va recevoir la nouvelle base de donnée migrée.</p>
<p>Pour cela, nous allons répéter les étapes de configuration de la base de donnée source, en les adaptant sur notre base de donnée de destination : modifier le postgresql.conf, puis le pg_hba.conf, redémarrer ensuite la base de données</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-89:~$ cd /etc/postgresql/17/main/
postgres@ip-192-1-1-89:/etc/postgresql/17/main$ vi postgresql.conf
</pre>
<blockquote><p>wal_level = logical<br />
max_replication_slots = 4<br />
max_wal_senders = 4</p></blockquote>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-246:/etc/postgresql/14/main$ vi pg_hba.conf
</pre>
<blockquote><p>host replication <span class="hljs-attribute">all</span> &lt;destination_ip&gt; scram-sha-256</p>
<p>host replication all &lt;source_ip&gt; scram-sha-256</p>
<p>host all replication &lt;destination_ip&gt; scram-sha-256</p>
<p>host all replication &lt;source-ip&gt; scram-sha-256</p></blockquote>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-246:~# systemctl stop postgresql@14-main.service
root@ip-192-1-1-246:~# systemctl start postgresql@14-main.service
</pre>
<p>Il ne faudra pas oublier de créer la base de donnée ainsi que toutes les structures de tables et autres objets dans notre base cible pour qu&#8217;elle puisse recevoir les données. Pour avoir les scripts de création de la base de données, vous pouvez faire un pg_dump avec l&#8217;option</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-89:~$ psql
psql (17.2 (Debian 17.2-1.pgdg120+1))
Type &quot;help&quot; for help.
</pre>
<pre class="brush: sql; title: ; notranslate">
postgres=# CREATE DATABASE mydb;
CREATE DATABASE
</pre>
<p>N&#8217;oubliez pas de donner tout les droits à votre utilisateur de replication pour qu&#8217;il puisse lire, écrire&#8230; Sur votre base de données repliquée, sur la source, comme sur la destination :</p>
<pre class="brush: sql; title: ; notranslate">
postgres=# GRANT ALL PRIVILEGES ON DATABASE &quot;mydb&quot; to replication;
GRANT

mydb=# GRANT ALL PRIVILEGES ON all tables in schema public to replication;
GRANT
</pre>
<h3>4. Mise en place de la réplication logique</h3>
<p>Maintenant que nos deux environnement sont bien en place, nous sommes prêts à mettre en route le processus de réplication logique pour commencer à transférer les données. Les étapes du dessous ont demandé une première intervention hors horaire de prod, notamment pour redémarrer le service postgreSQL, mais le but d&#8217;une migration avec réplication logique, c&#8217;est de pouvoir ensuite n&#8217;avoir rien à toucher jusqu&#8217;au moment de basculer les applicatifs d&#8217;une ip a une autre.</p>
<p>Sur notre machine source, on créé la publication qui va nous servir à transférer nos tables :</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-246:~$ psql
psql (14.15 (Debian 14.15-1.pgdg120+1))
Type &quot;help&quot; for help.
</pre>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">
postgres=# \c mydb
You are now connected to database &quot;mydb&quot; as user &quot;postgres&quot;.
mydb=# CREATE PUBLICATION my_pub FOR ALL TABLES;
CREATE PUBLICATION
</pre>
<p>On va ensuite créé la souscription sur la base de données cible de notre migration :</p>
<pre class="brush: sql; title: ; notranslate">
mydb=# create subscription my_sub connection 'host=192.1.1.246 port=5432 dbname=mydb user=replication password=replication'publication my_pub;
NOTICE: created replication slot &quot;my_sub&quot; on publisher
CREATE SUBSCRIPTION
</pre>
<p>Maintenant que la subscription est en place, on peut vérifier qu&#8217;elle fonctionne. Pendant ce temps, la vrai production, sur la version 14, peut continuer à fonctionner, elle sera automatiquement repliquée sur la nouvelle version 17.</p>
<p>On peut vérifier ou en est notre replication avec la commande <span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">FROM</span> pg_stat_subscription;</p>
<pre class="brush: sql; title: ; notranslate">
mydb=# SELECT * FROM pg_stat_subscription;
-[ RECORD 1 ]---------+------------------------------
subid | 16422
subname | my_sub
worker_type | apply
pid | 16076
leader_pid |
relid |
received_lsn | 0/1733988
last_msg_send_time | 2024-12-04 14:23:59.873074+00
last_msg_receipt_time | 2024-12-04 14:23:59.872357+00
latest_end_lsn | 0/1733988
latest_end_time | 2024-12-04 14:23:59.873074+00
</pre>
<h3>5. Test de replication, bascule, et nettoyage</h3>
<p>Une fois que la synchronisation de votre replication logique est terminée, ce qui peut prendre un certain temps si vous avez beaucoup de données, vous pouvez constater de vous même sur les lignes que vous ajoutez, modifiez ou supprimez sur votre instance source sont repliquées sur l&#8217;instance de destination.</p>
<p>Par exemple, ajoutons un nouveau customer sur notre base source :</p>
<pre class="brush: bash; title: ; notranslate">
postgres@ip-192-1-1-246:~$ psql
psql (14.15 (Debian 14.15-1.pgdg120+1))
Type &quot;help&quot; for help.
</pre>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">
postgres=# \c mydb
You are now connected to database &quot;mydb&quot; as user &quot;postgres&quot;.
mydb=# INSERT INTO customers (name, email) VALUES ('Diana', 'diana@example.com');
INSERT 0 1
</pre>
<p>Si nous allons requêter sur notre instance de destination :</p>
<pre class="brush: sql; title: ; notranslate">
mydb=# select * from customers where name='Diana';
id | name | email | created_at
----+-------+-------------------+----------------------------
4 | Diana | diana@example.com | 2024-12-04 14:31:05.708031
(1 row)
</pre>
<p>Quand vous vous êtes bien assuré que tout fonctionne, vous pouvez alors rediriger les drivers odbc de vos applications vers le nouveau serveur et non plus l&#8217;ancien.</p>
<p>Une fois que cela est fait, vous pouvez alors supprimer le lien de replication, puisque l&#8217;ancienne instance ne sera plus alimentée, et même supprimer l&#8217;ancienne version si vous n&#8217;en avez plus l&#8217;utilité.</p>
<p>Sur la destination, notre nouveau serveur de prod :</p>
<pre class="brush: sql; title: ; notranslate">
DROP SUBSCRIPTION my_sub;
</pre>
<p>Sur la source, ancien serveur qui va être supprimé :</p>
<pre class="brush: sql; title: ; notranslate">
DROP PUBLICATION my_pub;
</pre>
<h1>Conclusion</h1>
<p>La réplication logique se distingue comme l’une des meilleures solutions pour minimiser le temps d’arrêt lors d’une migration de version PostgreSQL. En permettant une synchronisation continue des données entre deux instances, elle garantit une transition en douceur sans jamais interrompre les services en cours. Cela en fait un choix idéal pour les environnements critiques où la disponibilité est primordiale.</p>
<h3>Avantages :</h3>
<p><strong>Zéro downtime :</strong> la source reste opérationnelle pendant toute la migration.<br />
<strong>Flexibilité :</strong> possibilité de migrer vers une infrastructure différente (nouveau matériel, cloud, etc.).<br />
<strong>Granularité :</strong> la réplication logique peut se limiter à certaines tables si nécessaire.</p>
<h3>Inconvénients :</h3>
<p><strong>Complexité initiale :</strong> la configuration et les tests nécessitent une bonne maîtrise des paramètres de PostgreSQL.<br />
<strong>Impact sur les performances :</strong> la charge de réplication peut légèrement affecter les performances de la base source, surtout avec un grand volume de données.<br />
<strong>Non pris en charge pour certains types de données :</strong> les types spécifiques ou les extensions non standards ne sont pas toujours compatibles avec la réplication logique.</p>
<p>Si la réplication logique est souvent la méthode privilégiée pour des mises à jour critiques, elle n’est pas la seule option. Des alternatives comme les outils de sauvegarde et restauration ou la réplication physique peuvent répondre à d’autres besoins spécifiques, notamment pour des bases de données très volumineuses ou des scénarios nécessitant une réplication complète du système.</p>
<p>Dans tous les cas, le choix de la méthode dépendra de votre contexte, de vos contraintes techniques et de vos objectifs métier. Prenez le temps d’évaluer les différentes options pour garantir une migration réussie et sans surprise.<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/replication-logique-avec-postgresql/" rel="bookmark" title="23 janvier 2020">Réplication logique avec PostgreSQL</a> (Capdata team) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/migration-postgresql-via-slony-i-ou-comment-reduire-le-temps-de-coupure/" rel="bookmark" title="27 janvier 2020">Migration PostgreSQL via SLONY-I ou comment réduire le temps de coupure</a> (Capdata team) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/migrer-dun-cluster-galera-mariadb-10-3-vers-mariadb-10-5-avec-la-replication-logique/" rel="bookmark" title="25 février 2022">Migrer d&#8217;un cluster Galera MariaDB 10.3 vers MariaDB 10.5 avec la réplication logique</a> (David Baffaleuf) [ContainerMySQLNon classé]</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>
<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>
</ul>
<p><!-- Similar Posts took 2.261 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%3D10633&#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%3D10633&#038;title=La%20mont%C3%A9e%20de%20version%20en%20zero-downtime%20%3A%20merci%20la%20r%C3%A9plication%20%21" 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=La%20mont%C3%A9e%20de%20version%20en%20zero-downtime%20%3A%20merci%20la%20r%C3%A9plication%20%21&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10633" 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/la-montee-de-version-en-zero-downtime-merci-la-replication/">La montée de version en zero-downtime : merci la réplication !</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/la-montee-de-version-en-zero-downtime-merci-la-replication/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>pg_vector : l&#8217;IA et PostgreSQL</title>
		<link>https://blog.capdata.fr/index.php/pg_vector-lia-et-postgresql/</link>
					<comments>https://blog.capdata.fr/index.php/pg_vector-lia-et-postgresql/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Tue, 03 Dec 2024 07:22:34 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[ia]]></category>
		<category><![CDATA[rag]]></category>
		<category><![CDATA[vectorDB]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10620</guid>

					<description><![CDATA[<p>1. Introduction : L&#8217;intelligence artificielle et le rôle des bases de données L&#8217;intelligence artificielle (IA) connaît une popularité croissante, des assistants virtuels aux voitures autonomes, en passant par les recommandations de films et de produits. Mais pour que ces technologies&#8230; <a href="https://blog.capdata.fr/index.php/pg_vector-lia-et-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/pg_vector-lia-et-postgresql/">pg_vector : l&#8217;IA et PostgreSQL</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10620&#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%3D10620&#038;title=pg_vector%20%3A%20l%E2%80%99IA%20et%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=pg_vector%20%3A%20l%E2%80%99IA%20et%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10620" 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><h2>1. Introduction : L&#8217;intelligence artificielle et le rôle des bases de données</h2>
<p>L&#8217;intelligence artificielle (IA) connaît une popularité croissante, des assistants virtuels aux voitures autonomes, en passant par les recommandations de films et de produits. Mais pour que ces technologies fonctionnent, elles ont besoin de données, souvent en grande quantité. C’est là qu’interviennent les bases de données : elles stockent, gèrent et permettent d&#8217;accéder à ces données de manière efficace.</p>
<p>Les bases de données, comme PostgreSQL, ont donc un rôle clé dans l’IA. Mais l&#8217;IA ne traite pas toujours des informations simples comme des noms ou des chiffres ; souvent, elle doit manipuler des informations complexes, comme des représentations numériques d&#8217;images, de sons, ou de textes. Pour gérer ces données spécifiques, il faut des outils adaptés, et c&#8217;est là que l&#8217;extension pg_vector de PostgreSQL entre en jeu.</p>
<h2>2. Les vecteurs en informatique et dans pg_vector</h2>
<p>Dans le cadre de l’informatique, un vecteur est simplement une liste de nombres. Ces nombres peuvent représenter n’importe quoi : les caractéristiques d’un produit, les mots d’un texte ou même une image. Par exemple, pour un document texte, chaque mot peut être transformé en une série de nombres qui capture son sens dans un certain contexte.</p>
<p>L’extension pg_vector permet à PostgreSQL de stocker et de manipuler ces vecteurs. Elle offre un moyen simple de les utiliser directement dans une base de données. Imaginons que nous avons des centaines de documents et que nous souhaitions rechercher les plus similaires à un texte donné : en stockant les représentations numériques (ou embeddings) de ces documents sous forme de vecteurs, nous pouvons facilement comparer leur similarité grâce à pg_vector.</p>
<h2>3. Le lien entre l&#8217;IA et les vecteurs</h2>
<p>L&#8217;intelligence artificielle repose sur la capacité à comprendre et traiter des informations complexes. Par exemple, quand une IA doit reconnaître une image, elle ne &#8220;voit&#8221; pas comme nous. Au lieu de cela, l&#8217;image est transformée en une série de nombres, un vecteur, qui représente ses caractéristiques (couleurs, formes, etc.).</p>
<p>Le même principe s’applique au texte. Les modèles de traitement du langage, comme ceux utilisés par les moteurs de recherche ou les chatbots, transforment chaque mot ou phrase en vecteur. Ces vecteurs capturent le sens des mots et permettent à l&#8217;IA de manipuler des informations complexes sans &#8220;comprendre&#8221; le langage humain.</p>
<p>C&#8217;est ici que les embeddings entrent en jeu. Un embedding est un vecteur qui représente des données sous une forme que l’IA peut utiliser. Par exemple, dans un système de recommandation, chaque produit est converti en un embedding, et les produits les plus proches de celui que nous venons de consulter (en termes de vecteur) nous seront recommandés. Grâce à pg_vector, ces embeddings peuvent être stockés et comparés directement dans une base de données.</p>
<h2>4. Pourquoi est-ce utile ?</h2>
<p>L&#8217;extension pg_vector est très utile pour des applications qui nécessitent la recherche par similarité. Par exemple, dans un moteur de recherche, si nous voulons trouver les documents les plus proches d&#8217;un texte donné, pg_vector permet de comparer les vecteurs (ou embeddings) de chaque document pour voir lesquels sont les plus similaires.</p>
<p>Autre exemple, dans une plateforme de streaming musical, chaque chanson peut être convertie en vecteur qui représente ses caractéristiques (comme le tempo, la tonalité, etc.). Grâce à pg_vector, on peut facilement recommander des chansons similaires à celles que nous écoutons.</p>
<p>L’avantage de pg_vector, c’est qu’il permet de gérer ces vecteurs directement dans la base de données, ce qui évite de passer par des systèmes externes plus complexes. Cela simplifie le développement et améliore la performance, car tout est géré au même endroit.</p>
<h2>5. Le test</h2>
<p>Pour démontrer le fonctionnement de l&#8217;extension, rien de tel qu&#8217;un petit test pour éprouver les fonctionnalités qu&#8217;elle propose. Le test sera plutôt simple et succinct pour être accessible. Le prérequi est d&#8217;avoir une version PostgreSQL 14 ou plus récente d&#8217;installée.</p>
<h3>Etape 1 :</h3>
<p>On commence par installer l&#8217;extension pg_vector. Pour cela, nous allons avoir besoin d&#8217;un certain nombre d&#8217;outils pour le faire fonctionner. Une partie de ces outils sont disponible dans la distribution dev de PostgreSQL</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-201:~# sudo apt install postgresql-server-dev-14
</pre>
<p>Nous aurons également besoin de gcc et make :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-201:~# apt install make
root@ip-192-1-1-201:~# apt-get install gcc
</pre>
<p>On effectue ensuite un git clone du projet :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-201:~# git clone https://github.com/pgvector/pgvector.git
</pre>
<p>Et une fois que c&#8217;est fait, on l&#8217;installe avec make :</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-201:~# cd pgvector
root@ip-192-1-1-201:~# make &amp;amp;&amp;amp; sudo make install
</pre>
<h3>Etape 2 :</h3>
<p>On se connecte à PostgreSQL pour créer l&#8217;extension. Au passage, on créé aussi une base de données pour faire nos test.</p>
<pre class="brush: bash; title: ; notranslate">
root@ip-192-1-1-201:~# su - postgres
postgres@ip-192-1-1-201:~$ psql
psql (14.13 (Ubuntu 14.13-0ubuntu0.22.04.1))
Type &quot;help&quot; for help.
</pre>
<pre class="brush: sql; title: ; notranslate">
postgres=# create database test_vector;
CREATE DATABASE
postgres=# \c test_vector
You are now connected to database &quot;test_vector&quot; as user &quot;postgres&quot;.
test_vector=# CREATE EXTENSION vector;
CREATE EXTENSION
</pre>
<p>Et dans la foulée, on crée une table qui contient les vecteurs sur lesquels nous allons faire les test</p>
<pre class="brush: sql; title: ; notranslate">
test_vector=# CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT,
embedding vector(3) -- vecteur de dimension 3 pour cet exemple
);
CREATE TABLE
</pre>
<p>Insertion des données d&#8217;exemple :</p>
<pre class="brush: sql; title: ; notranslate">
test_vector=# INSERT INTO documents (title, embedding) VALUES
('Document 1', '[0.1, 0.2, 0.3]'),
('Document 2', '[0.4, 0.5, 0.6]'),
('Document 3', '[0.9, 0.8, 0.7]');
INSERT 0 3
</pre>
<h3>Etape 3 :</h3>
<p>Nous avons deux types de choses à tester pour montrer l&#8217;efficacité de notre extension. En effet, pour rechercher un vecteur, deux modes s&#8217;offrent à nous :</p>
<ol>
<li>
<h4>La distance cosinus</h4>
</li>
</ol>
<p>La distance cosinus mesure non pas combien deux vecteurs sont éloignés, mais l&#8217;angle entre eux. C’est un peu comme comparer la direction dans laquelle pointent deux vecteurs plutôt que la distance réelle entre eux.</p>
<p>Imaginons que nous sommes en train de lancer deux flèches. La distance cosinus nous dira si les deux flèches pointent dans la même direction (sont similaires) ou si elles pointent dans des directions très différentes (sont moins similaires).</p>
<p>Dans le cadre de l’IA, cette mesure est souvent utilisée pour comparer des embeddings (représentations numériques complexes), car elle se concentre sur la relation entre les éléments, indépendamment de leur taille exacte.</p>
<p>Exemple simple :</p>
<p>Prenons les deux films :</p>
<ul>
<li>Film A : <code>[1, 5, 50, 120]</code></li>
<li>Film B : <code>[2, 4, 45, 110]</code></li>
</ul>
<p>La distance cosinus ne va pas se soucier de la différence de valeur entre chaque composant, mais va regarder si les deux films ont des proportions similaires. Autrement dit, est-ce que leur &#8220;profil&#8221; général est proche ou éloigné ?</p>
<p>Pour tester cette distance, dans notre pg vector, on utilise la méthode suivante :</p>
<pre class="brush: sql; title: ; notranslate">
test_vector=# SELECT title, embedding, embedding &amp;lt;=&amp;gt; '[0.2, 0.1, 0.3]' AS distance
FROM documents
ORDER BY embedding &amp;lt;=&amp;gt; '[0.2, 0.1, 0.3]' ASC
LIMIT 3;
title | embedding | distance
------------+---------------+---------------------
Document 2 | [0.4,0.5,0.6] | 0.05582537807240784
Document 1 | [0.1,0.2,0.3] | 0.07142855242198809
Document 3 | [0.9,0.8,0.7] | 0.09815280896106982
(3 rows)
</pre>
<p>Le symbole &lt;=&gt; représente une distance cosinus.</p>
<h4>2. La distance Euclidienne</h4>
<p>Imaginons que nous sommes sur une carte avec deux points : le point A et le point B. La distance euclidienne, c&#8217;est la façon la plus intuitive de mesurer la distance entre ces deux points, comme si nous tracions une ligne droite entre eux. Pour parler en terme simple, c’est la &#8220;distance à vol d&#8217;oiseau&#8221;.</p>
<p>Dans le cadre des vecteurs, la distance euclidienne mesure la différence entre deux vecteurs, un peu comme si chaque vecteur était un point sur une carte en plusieurs dimensions. Plus cette distance est petite, plus les deux vecteurs (et donc les objets qu’ils représentent) sont similaires.</p>
<p>Exemple simple :</p>
<p>Imaginons deux films représentés par les vecteurs suivants :</p>
<ul>
<li><strong>Film A</strong> : <code>[1, 5, 50, 120]</code></li>
<li><strong>Film B</strong> : <code>[2, 4, 45, 110]</code></li>
</ul>
<p>La distance euclidienne va calculer la différence entre chaque nombre des deux vecteurs et déterminer à quel point ces films sont proches en termes de caractéristiques (genre, nombre d’acteurs, budget, etc.).</p>
<p>Dans pg_vector on peut le tester ainsi :</p>
<pre class="brush: sql; title: ; notranslate">
test_vector=# SELECT title, embedding, embedding &amp;lt;-&amp;gt; '[0.2, 0.1, 0.3]' AS distance
FROM documents
ORDER BY embedding &amp;lt;-&amp;gt; '[0.2, 0.1, 0.3]' ASC
LIMIT 3;
title | embedding | distance
------------+---------------+--------------------
Document 1 | [0.1,0.2,0.3] | 0.1414213612422477
Document 2 | [0.4,0.5,0.6] | 0.5385165006363984
Document 3 | [0.9,0.8,0.7] | 1.0677078185041473
(3 rows)
</pre>
<p>Elle est représentée par le cigle &lt;-&gt; dans pg_vector.</p>
<h4>3. Quand choisir l&#8217;une ou l&#8217;autre des distances ?</h4>
<ul>
<li>La distance euclidienne est utile quand tu veux mesurer la différence globale entre deux objets. Elle est facile à comprendre et à utiliser pour des comparaisons directes.</li>
<li>La distance cosinus est utile quand tu veux savoir si deux objets sont globalement similaires dans leur profil, indépendamment de leur taille ou de leur échelle. Elle est souvent utilisée pour comparer des documents textuels ou des données complexes en IA</li>
</ul>
<h2>Conclusion</h2>
<p>L&#8217;extension pg_vector apporte une fonctionnalité puissante à PostgreSQL, permettant de manipuler des données complexes sous forme de vecteurs. Que ce soit pour des systèmes de recommandation, des moteurs de recherche ou toute autre application liée à l’intelligence artificielle, elle offre un moyen simple et efficace d&#8217;intégrer l&#8217;IA dans les bases de données. Et tout cela, sans avoir besoin de comprendre des mathématiques avancées : il suffit de savoir que ces vecteurs permettent de traiter des informations complexes de manière très efficace.<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/transparent-data-encryption-pour-postgresql/" rel="bookmark" title="13 mai 2022">Transparent Data Encryption pour PostgreSQL</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>
</ul>
<p><!-- Similar Posts took 1.964 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%3D10620&#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%3D10620&#038;title=pg_vector%20%3A%20l%E2%80%99IA%20et%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=pg_vector%20%3A%20l%E2%80%99IA%20et%20PostgreSQL&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10620" 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_vector-lia-et-postgresql/">pg_vector : l&#8217;IA et PostgreSQL</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/pg_vector-lia-et-postgresql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup</title>
		<link>https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/</link>
					<comments>https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Tue, 16 Jul 2024 11:24:05 +0000</pubDate>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[incrémental]]></category>
		<category><![CDATA[Sauvegardes]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10584</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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 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%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 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=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 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><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/17.0.2/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 3.092 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 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%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 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=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 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/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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/postgresql-17-sauvegardes-incrementales/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PGO : la suite</title>
		<link>https://blog.capdata.fr/index.php/pgo-la-suite/</link>
					<comments>https://blog.capdata.fr/index.php/pgo-la-suite/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Wed, 29 May 2024 08:58:17 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[#devops]]></category>
		<category><![CDATA[#InfraAsCode]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10562</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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-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/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/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.451 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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/pgo-la-suite/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>pg_recursively_delete : Simplifier les suppressions récursives</title>
		<link>https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/</link>
					<comments>https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Wed, 03 Apr 2024 13:11:08 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10505</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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/la-montee-de-version-en-zero-downtime-merci-la-replication/" rel="bookmark" title="19 décembre 2024">La montée de version en zero-downtime : merci la réplication !</a> (Sarah FAVEERE) [PostgreSQL]</li>
</ul>
<p><!-- Similar Posts took 2.069 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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/pg_recursively_delete-simplifier-les-suppressions-recursives/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>pg_dirtyread où comment réparer facilement un delete sauvage</title>
		<link>https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/</link>
					<comments>https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Wed, 27 Mar 2024 14:24:14 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[pg_dirtyread]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10494</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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 2.217 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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/pg_dirtyread-ou-comment-reparer-facilement-un-delete-sauvage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</title>
		<link>https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/</link>
					<comments>https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Wed, 20 Mar 2024 13:00:13 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10515</guid>

					<description><![CDATA[<p>&#160; &#160; Suite au premier article publié ce mois ci concernant &#8220;native network encryption&#8221; , je vous propose, pour ce sujet chiffrement et sécurité, de découvrir le fonctionnement de Transparent Data Encryption (TDE) pour Oracle. Le sujet TDE a plusieurs&#8230; <a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/" 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/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/">Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10515&#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%3D10515&#038;title=Le%20chiffrement%20Oracle%20%3A%20Transparent%20Data%20Encryption%20sur%20Oracle%2019c" 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=Le%20chiffrement%20Oracle%20%3A%20Transparent%20Data%20Encryption%20sur%20Oracle%2019c&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10515" 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>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10517 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/03/key_tde.png" alt="" width="577" height="329" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/key_tde.png 577w, https://blog.capdata.fr/wp-content/uploads/2024/03/key_tde-300x171.png 300w" sizes="auto, (max-width: 577px) 100vw, 577px" /></p>
<p>&nbsp;</p>
<p>Suite au premier article publié ce mois ci concernant &#8220;<a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/">native network encryption</a>&#8221; , je vous propose, pour ce sujet chiffrement et sécurité, de découvrir le fonctionnement de Transparent Data Encryption (TDE) pour Oracle.</p>
<p>Le sujet TDE a plusieurs fois été abordé au sein de notre blog, pour la partie PostgreSQL sur <a href="https://blog.capdata.fr/index.php/transparent-data-encryption-pour-postgresql/">cet article</a>, mais également sur une instance de bases de données SQL Server sur <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/">ce lien</a>.</p>
<p>Je vous invite donc à lire, ou relire, les présentations faites pour ces SGBD en question.</p>
<p>Pour le moment, intéressons nous à ce que propose Oracle.</p>
<p>&nbsp;</p>
<h2>Présentation</h2>
<p>&nbsp;</p>
<p>Oracle TDE permet de chiffrer des données sensibles dans une base de données Oracle (multitenant ou pas) de façon la plus transparente possible pour l’application. En outre, l’application n’a pas besoin d’embarquer de stratégie de chiffrement puisque celle-ci est intégralement gérée coté serveur Oracle.</p>
<p>Les données, dans les colonnes, sont chiffrées une fois inscrites dans les datafiles. Il ne sera donc pas possible de les récupérer (via ALTER SYSTEM DUMP DATAFILE) sans avoir la clé.<br />
Les syntaxes SQL utilisées par l’application restent inchangées puisque celles-ci sont directement envoyées depuis l’application cliente et chiffrées sur le serveur base de données locale (encryption at rest).</p>
<p>&nbsp;</p>
<p>Oracle TDE utilise, pour son processus, un mécanisme de clés (master key et clé de cryptage) afin de valider l’ouverture du &#8220;wallet&#8221;, et du chiffrement de la donnée.</p>
<p>À partir d’Oracle 10gr2, Oracle TDE a la possibilité de traiter l’algorithme de chiffrement sur une colonne de table (un champs adresse ou RIB par exemple).<br />
C’est à partir de la version Oracle 11gr1 qu’Oracle TDE peut travailler sur le chiffrement d’un tablespace entier. Ceci a l’avantage de traiter davantage d’objets en même temps.</p>
<p>Mais avant d&#8217;aller plus loin sur le fonctionnement de TDE, et comme nous sommes sur Oracle, il convient d&#8217;aller vérifier, point de vu licensing, ce que nous devons choisir comme version, pour configurer cette fonctionnalité et être en &#8220;règle&#8221; avec Oracle.</p>
<p>Cette fois ci, nous serons bien obligés de nous tourner vers la version Enterprise Edition Oracle, avec en plus, l&#8217;option payante &#8220;Advanced Security&#8221;.</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10518 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/03/licence.png" alt="" width="839" height="500" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/licence.png 839w, https://blog.capdata.fr/wp-content/uploads/2024/03/licence-300x179.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/03/licence-768x458.png 768w" sizes="auto, (max-width: 839px) 100vw, 839px" /></p>
<p>Extrait du site Oracle -&gt; <a href="https://docs.oracle.com/en/database/oracle/oracle-database/21/dblic/Licensing-Information.html#GUID-AB56CEE3-955E-4E56-8B44-6075E889C283">lien</a></p>
<p>Pour une version Oracle 19c, nous choisirons donc de prendre une licence Enterprise Edition. A noter que l&#8217;option &#8220;Advanced Security&#8221; est incluse dans la version Personnal Edition On prem, alors qu&#8217;elle est en &#8220;extra coût&#8221; pour une version Enterprise Edition On prem.</p>
<p>&nbsp;</p>
<h3>Les algorithmes de chiffrement</h3>
<p>&nbsp;</p>
<p>Voici la liste des algorithmes utilisés par Oracle pour TDE, et ce, avec une instance de bases de données Oracle 19c</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10519 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/03/algo_tde.png" alt="" width="836" height="584" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/algo_tde.png 836w, https://blog.capdata.fr/wp-content/uploads/2024/03/algo_tde-300x210.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/03/algo_tde-768x536.png 768w, https://blog.capdata.fr/wp-content/uploads/2024/03/algo_tde-250x175.png 250w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<p>&nbsp;</p>
<p>Oracle TDE travaille par défaut, avec l’algorithme Advanced Encryption Standard sur 192 bits, soit AES192 si vous souhaitez chiffrer une colonne. Pour un tablespace, c&#8217;est le même algorithme, mais en 128 bits.</p>
<p>D&#8217;autres algorithmes comme ARIA ou GOST pourront être choisis également.</p>
<p>Pour le chiffrement sur colonne, Oracle TDE utilisera par défaut SALT pour renforcer la sécurité des données. Le procédé suivant est réalisé ; une chaine sera ajoutée de façon aléatoire dans la donnée avant d’être chiffrée. Cela empêchera de trouver facilement la valeur d’un caractère à partir d’un motif de cryptage.</p>
<p>C&#8217;est lors de l&#8217;appel à la clause &#8220;ENCRYPT&#8221; que vous pouvez choisir un autre algorithme pour la colonne ou le tablespace.</p>
<p>&nbsp;</p>
<h2>Le fonctionnement</h2>
<p>&nbsp;</p>
<p>Oracle TDE se charge du chiffrement d’une donnée dans une colonne de table applicative en utilisant  un fonctionnement de type ESM (External Security Module) ce qui permet de générer des clés de chiffrement qui servent lors des opérations de cryptage et decryptage.</p>
<p>Ces clés de chiffrement sont stockées en interne dans la base de données. Une clé de chiffrement peut gérer une ou plusieurs colonnes de tables applicatives.</p>
<p>Ces mêmes clés de chiffrement sont stockées en interne dans une colonne du dictionnaire de données (vues v$encryption…) .<br />
C’est la clé que l’on appelle « <strong>master key</strong> » qui permet de crypter/decrypter la colonne du dictionnaire de données afin d’utiliser ces clés de chiffrement de données aux personnes autorisées.</p>
<p>On peut représenter le mécanisme via le graphique suivant afin de comprendre le cheminement du processus Oracle TDE :</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10521 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/03/tde_process_colonne.png" alt="" width="689" height="477" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/tde_process_colonne.png 689w, https://blog.capdata.fr/wp-content/uploads/2024/03/tde_process_colonne-300x208.png 300w" sizes="auto, (max-width: 689px) 100vw, 689px" /></p>
<p>Dans tous les cas, il nous faut disposer d’un rôle <strong>DBA</strong> (ou bien du rôle <strong>SYSKM</strong> depuis la version Oracle 12c), afin de manipuler cette « <strong>master key</strong> ».<br />
Cette clé est stockée hors de la base de données, c’est un répertoire de l’OS (ou ASM depuis la version Oracle 12c) qui portera cette clé.<br />
Afin de la sécuriser encore plus, on utilisera un wallet.</p>
<p>&nbsp;</p>
<h2>Prérequis à la mise en place d’un wallet (keystore)</h2>
<p>&nbsp;</p>
<p>Pour mettre en place Oracle TDE,  nous devons créer un wallet.<br />
Celui-ci sera créer par défaut sous « $ORACLE_BASE/admin/$ORACLE_SID/wallet » ou bien dans un autre répertoire que nous pourrons spécifier.</p>
<p>Pour notre instance CAPDATADB</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-141 admin]$ ls -lrt /opt/app/oracle/admin/CAPDATADB/
total 20
drwxr-x---. 2 oracle dba 4096 Jun 3 2021 scripts
drwxr-x---. 2 oracle dba 44 Jun 3 2021 xdb_wallet
drwxr-x---. 2 oracle dba 20 Jun 3 2021 dpdump
drwxr-x---. 2 oracle dba 34 Jun 3 2021 pfile
drwxr-x---. 2 oracle dba 12288 Mar 13 16:08 adump
drwxr-xr-x. 2 oracle dba 6 Mar 13 16:10 wallet</pre>
<p>&nbsp;</p>
<p>Depuis la version Oracle 12c, il est maintenant possible de stocker le wallet (keystore) sur ASM.</p>
<p>La master key communique alors en base directement afin de pouvoir utiliser la clé de cryptage / décryptage.<br />
On peut schématiser de la façon suivante (attention c&#8217;est un vue obsolète depuis Oracle 19c) :</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10525 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/03/master_key.png" alt="" width="612" height="570" srcset="https://blog.capdata.fr/wp-content/uploads/2024/03/master_key.png 612w, https://blog.capdata.fr/wp-content/uploads/2024/03/master_key-300x279.png 300w" sizes="auto, (max-width: 612px) 100vw, 612px" /></p>
<p>&nbsp;</p>
<p>Afin de pouvoir créer et administrer un wallet, on parle maintenant de keystore en 12c, nous utilisons 2 méthodes;<br />
&#8211; soit l’utilisation de l’outil Oracle Wallet Manager fourni par Oracle,<br />
&#8211; soit passer les commandes directement en base via les syntaxes « ADMINISTER KEY MANAGEMENT ».</p>
<p>Depuis la version 19c, il y a eu quelques modifications quant à la prise en charge du wallet Oracle.<br />
La variable &#8220;<strong>ENCRYPTION_WALLET_LOCATION</strong>&#8221; n&#8217;est plus utilisée pour définir l&#8217;emplacement du répertoire dédié au wallet.</p>
<p>Nous disposons de 2 nouvelles variables qui sont</p>
<p>&#8211; &#8220;<strong>WALLET_ROOT</strong>&#8220;, répertoire par défaut dédié au wallet Oracle.<br />
&#8211; &#8220;<strong>TDE_CONFIGURATION</strong>&#8220;, le type d&#8217;emplacement pour TDE. Ce paramètre s&#8217;appuie sur le paramètre &#8220;<strong>KEYSTORE_CONFIGURATION</strong>&#8220;.</p>
<p>Il est possible de définir ces variables, soit dans le SPFILE.ORA de l&#8217;instance, soit au niveau du SQLNET.ORA du ORACLE_HOME.</p>
<p>Pour notre cas d&#8217;étude, nous choisissons de placer ces paramètres directement dans le spfile.<br />
En effet, cela a pour avantage de pouvoir activer TDE uniquement pour notre base, et non pas les autres bases du même ORACLE_HOME avec l&#8217;option SQLNET.ORA.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ALTER SYSTEM SET WALLET_ROOT = '/opt/app/oracle/admin/CAPDATADB/wallet' scope=spfile;

System altered.</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Redémarrer l&#8217;instance pour une prise en compte immédiate du paramètre &#8220;<strong>WALLET_ROOT</strong>&#8216;.</p>
<p>Une fois la base redémarrer, configurer la variable &#8220;<strong>TDE_CONFIGURATION</strong>&#8221; qui nous sert à spécifier le type de stockage de la clé.<br />
Dans notre exemple, ce sera un fichier.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ALTER SYSTEM SET TDE_CONFIGURATION=&quot;KEYSTORE_CONFIGURATION=FILE&quot; scope=both;

System altered.</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Vérifier via &#8220;show parameters&#8221; que ces 2 variables sont prises en compte</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; show parameters wallet_root

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
wallet_root                          string      /opt/app/oracle/admin/CAPDATADB/wallet


SQL&gt; show parameters tde_configuration

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
tde_configuration                    string      KEYSTORE_CONFIGURATION=FILE</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>Création d’un keystore</h3>
<p>&nbsp;</p>
<p>Pour la création de la ‘<strong>master key</strong>’, Oracle TDE  s’appuie sur le format Public Key Cryptography Standards (PKCS), soit un fichier PKCS#12 sous une extension *.p12.<br />
On se connecte au serveur de bases de données sous notre environnement Oracle 19c Enterprise Edition.</p>
<p>Le répertoire choisi pour le stockage de la &#8216;<strong>master key&#8217;</strong> est ainsi géré automatiquement avec d&#8217;une part &#8220;<strong>WALLET_ROOT</strong>&#8221; et d&#8217;autre part le  &#8220;<strong>TDE_CONFIGURATION</strong>&#8220;.</p>
<p>Nous pouvons le vérifier sur cette base en interrogeant la vue &#8220;v$encryption_wallet&#8221;.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select WRL_TYPE,WRL_PARAMETER,STATUS,WALLET_TYPE,WALLET_ORDER,FULLY_BACKED_UP from v$encryption_wallet;

WRL_TYPE             WRL_PARAMETER                                      STATUS                         WALLET_TYPE          WALLET_OR  FULLY_BAC
-------------------- -------------------------------------------------- ------------------------------ -------------------- ---------  ---------
FILE                 /opt/app/oracle/admin/CAPDATADB/wallet/tde/        NOT_AVAILABLE                  UNKNOWN              SINGLE     UNDEFINED</pre>
<p>&nbsp;</p>
<h3>Gérer le keystore</h3>
<p>&nbsp;</p>
<p>On crée le keystore en utilisant comme password celui de SYS par exemple</p>
<pre class="brush: sql; title: ; notranslate"> SQL&gt; ADMINISTER KEY MANAGEMENT CREATE KEYSTORE IDENTIFIED BY **********;

keystore altered.</pre>
<p>&nbsp;</p>
<p>On peut voir immédiatement, dans le répertoire représenté par la variable &#8220;<strong>WALLET_ROOT</strong>&#8220;, un nouveau répertoire nommé &#8220;tde&#8221; .</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-141 admin]$ ls -l /opt/app/oracle/admin/CAPDATADB/wallet
total 0
drwxr-x---. 2 oracle dba 25 Mar 13 17:35 tde</pre>
<p>&nbsp;</p>
<p>Dans ce répertoire &#8220;tde&#8221;, nous avons bien un fichier *.p12  créé.</p>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-141 admin]$ ls -lrt /opt/app/oracle/admin/CAPDATADB/wallet/tde
total 4
-rw-------. 1 oracle dba 2555 Mar 13 17:35 ewallet.p12</pre>
<p>&nbsp;</p>
<p>A partir de la, on peut déclarer notre master key dans la base de données.<br />
La première étape consiste à ouvrir ce nouveau keystore avant de créer la master key.</p>
<p>Celui-ci est pour le moment marquée CLOSED</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select WRL_TYPE,WRL_PARAMETER,STATUS,WALLET_TYPE from v$encryption_wallet;

WRL_TYPE             WRL_PARAMETER                                      STATUS                         WALLET_TYPE
-------------------- -------------------------------------------------- ------------------------------ --------------------
FILE                 /opt/app/oracle/admin/CAPDATADB/wallet/tde/        CLOSED                         UNKNOWN</pre>
<p>&nbsp;</p>
<p>Nous l&#8217;ouvrons via la commande</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY *******;

keystore altered.</pre>
<p>&nbsp;</p>
<p>Le wallet est ouvert, mais notre clé n’est toujours pas présente, « <strong>OPEN_NO_MASTER_KEY </strong>»</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select WRL_TYPE,WRL_PARAMETER,STATUS,WALLET_TYPE from v$encryption_wallet;

WRL_TYPE             WRL_PARAMETER                                      STATUS                         WALLET_TYPE
-------------------- -------------------------------------------------- ------------------------------ --------------------
FILE                 /opt/app/oracle/admin/CAPDATADB/wallet/tde/        OPEN_NO_MASTER_KEY             PASSWORD</pre>
<p>&nbsp;</p>
<p>Cette première étape est primordiale pour la mise en place de TDE dans une base de données.</p>
<p>Afin d’éviter d’avoir à faire cette manipulation à chaque redémarrage de la base, nous avons le choix d&#8217;utiliser le mode AUTO LOGIN pour le keystore.</p>
<p>TDE utilisera le « <strong>Single Sign-On</strong> » afin de valider l’ouverture du keystore pour cette base de données.<br />
En outre, un fichier comportant l’extension .SSO sera alors créé à cet effet dans le répertoire</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE FROM KEYSTORE IDENTIFIED BY *********;

keystore altered.</pre>
<p>&nbsp;</p>
<p>Vérifier la présence du fichier sso dans le dossier &#8220;tde&#8221;</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-141 admin]$ ls -lrt /opt/app/oracle/admin/CAPDATADB/wallet/tde
total 8
-rw-------. 1 oracle dba 2555 Mar 13 17:35 ewallet.p12
-rw-------. 1 oracle dba 2600 Mar 13 17:46 cwallet.sso</pre>
<p>&nbsp;</p>
<h3>La master key</h3>
<p>&nbsp;</p>
<p>La master key est stockée dans le keystore préalablement créé.<br />
Une fois ouvert, le keystore peut accueillir la nouvelle &#8216;<strong>master key</strong>&#8216;.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY ***** WITH BACKUP;

keystore altered.</pre>
<p>&nbsp;</p>
<p>Pour valider la creation de la master key, on contrôle les vues « <strong>v$encryption_wallet</strong> » et « <strong>v$encryption_keys</strong> ».</p>
<p>La première vue indiquera le status OPEN du keystore, ainsi que le type SINGLE SIGN ON pour ce key store.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select WRL_TYPE,WRL_PARAMETER,STATUS,WALLET_TYPE,WALLET_ORDER,FULLY_BACKED_UP from v$encryption_wallet;

WRL_TYPE             WRL_PARAMETER                                       STATUS                         WALLET_TYPE          WALLET_OR        FULLY_BAC
-------------------- --------------------------------------------------  ------------------------------ -------------------- ---------------- ----------------
FILE                 /opt/app/oracle/admin/CAPDATADB/wallet/tde/         OPEN                           PASSWORD             SINGLE           NO</pre>
<p>&nbsp;</p>
<p>La seconde requête donne les infos sur la master key, son ID, son type et surtout sa date d’activation. On voit ici que la master key est donc active depuis le 13 mars 2024 à 17h49:</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select KEY_ID,KEYSTORE_TYPE,CREATOR_DBNAME,ACTIVATION_TIME,KEY_USE,ORIGIN from v$encryption_keys;


KEY_ID KEYSTORE_TYPE
------------------------------------------------------------------------------ -----------------
CREATOR_DBNAME
--------------------------------------------------------------------------------------------------------------------------------
ACTIVATION_TIME                                                             KEY_USE    ORIGIN
--------------------------------------------------------------------------- ---------- -----------------------------------------
AVFKfh8jPk/Rv1rHHavj4rsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA SOFTWARE KEYSTORE
CAPDATADB
13-MAR-24 05.49.58.737772 PM +00:00                                         TDE        LOCAL</pre>
<p>&nbsp;</p>
<p>A noter également que la master key est géré en locale (champs ORIGIN=LOCAL), nous verrons par la suite dans quelle mesure ce champs peut avoir une autre valeur.</p>
<p>&nbsp;</p>
<h2>Cas d&#8217;utilisation de TDE</h2>
<p>&nbsp;</p>
<h4>Chiffrement d&#8217;une colonne</h4>
<p>&nbsp;</p>
<p>Depuis Oracle 10gr2, nous pouvons utiliser Oracle TDE pour chiffrer une colonne (en choisissant l’algo de chiffrement si besoin).</p>
<p>On crée une table pour tester le chiffrement. Nous utilisons l&#8217;algorithme AES256.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect manu
SQL&gt; create table infos_employes (prenom varchar2(40),  nom varchar2(40),
  adresse varchar2(40) encrypt using 'AES256',
  code_postal number(6) encrypt using 'AES256'); 

Table created.</pre>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; insert into infos_employes values ('Emmanuel','Rami','19 rue Crebillon Nantes','44000');

1 row created.</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Il s&#8217;agit ensuite de vérifier les colonnes chiffrées avec leurs algorithmes utilisés. Le check d’intégrité (checksum) est effectué via SHA-1.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from dba_encrypted_columns;

OWNER           TABLE_NAME           COLUMN_NAME          ENCRYPTION_ALG                SAL INTEGRITY_AL
--------------- -------------------- -------------------- ----------------------------- --- ------------
MANU            INFOS_EMPLOYES       ADRESSE              AES 256 bits key              YES SHA-1
MANU            INFOS_EMPLOYES       CODE_POSTAL          AES 256 bits key              YES SHA-1</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Il est possible de modifier une table en ajoutant un chiffrement à une colonne,  ajouter une colonne chiffrée ou même déchiffrée une colonne existante.<br />
On peut également changer l’algo de chiffrement sur une colonne.</p>
<p>&nbsp;</p>
<h4>Chiffrement d&#8217;un tablespace</h4>
<p>&nbsp;</p>
<p>Depuis la version 11gr1, il est possible d’utiliser TDE sur un tablespace entier. Nous utilisons toujours le même algorihme.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; create tablespace CAPDATA_TBS datafile '/data/oradata/CAPDATADB/capdata_tbs01.dbf' size 100M
  encryption using 'AES256'
  default storage (ENCRYPT);

Tablespace created.</pre>
<p>&nbsp;</p>
<p>La vue &#8220;v$encrypted_tablespace&#8221; nous confirme la prise en charge de ce tablespace</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select T.name,E.ENCRYPTIONALG,E.ENCRYPTEDTS,E.MASTERKEYID,E.BLOCKS_ENCRYPTED,E.BLOCKS_DECRYPTED,E.STATUS 
         from v$tablespace t inner join v$encrypted_tablespaces e on (t.ts# = e.tS#);

NAME                           ENCRYPT ENC MASTERKEYID                      BLOCKS_ENCRYPTED BLOCKS_DECRYPTED STATUS
------------------------------ ------- --- -------------------------------- ---------------- ---------------- ----------
CAPDATA_TBS                    AES256  YES 514A7E1F233E4FD1BF5AC71DABE3E2BB 126              0                NORMAL</pre>
<p>&nbsp;</p>
<p>Comme nous sommes sur un keystore en mode AUTO_LOGIN, un redémarrage de base ne va pas nécessiter une saisie du password du keystore pour l&#8217;ouverture.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL&gt; startup
ORACLE instance started.

Total System Global Area 2147481648 bytes
Fixed Size 8898608 bytes
Variable Size 436207616 bytes
Database Buffers 1694498816 bytes
Redo Buffers 7876608 bytes
Database mounted.
Database opened.
SQL&gt; select * from manu.infos_employes;

PRENOM                                   NOM                                      ADRESSE                                  CODE_POSTAL
---------------------------------------- ---------------------------------------- ---------------------------------------- -----------
Emmanuel                                 Rami                                     19 rue Crebillon Nantes                  44000
</pre>
<p>&nbsp;</p>
<h3>Validation du chiffrement et tests de sécurité</h3>
<p>&nbsp;</p>
<p>Prenons l&#8217;exemple d&#8217;une table non chiffrée, que nous appelons &#8220;infos_societe&#8221;, dans laquelle nous ajoutons un ligne exemple.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; create table infos_societes (nom varchar2(40),
  raison varchar2(40),
  adresse varchar2(40),
  code_postal number(6) ); 

Table created.

SQL&gt; insert into infos_societes values ('Capdata','SA','9 rue de la porte de Buc Versailles','78000');

1 row created.</pre>
<p>&nbsp;</p>
<p>Ici, nous n&#8217;utilisons pas de chiffrement. Il nous est donc possible potentiellement d&#8217;aller lire la donnée directement dans le block Oracle via un &#8220;dump&#8221;.</p>
<p>Tout d&#8217;abord, on repère le ROW_ID de notre ligne insérée</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select rowid from manu.infos_societes where NOM='Capdata';

ROWID
------------------
AAASEqAAHAAAAF0AAA</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Puis on cherche le numéro de block dans lequel notre ligne est écrite.</p>
<pre></pre>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select DBMS_ROWID.ROWID_BLOCK_NUMBER('AAASEqAAHAAAAF0AAA') &quot;Block number&quot; from DUAL;

Block number
------------
372</pre>
<pre></pre>
<p>Ainsi que le FILE_ID du tablespace</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select SEGMENT_NAME,SEGMENT_TYPE,TABLESPACE_NAME,EXTENT_ID,FILE_ID from dba_extents where SEGMENT_NAME='INFOS_SOCIETES';

SEGMENT_NAME         SEGMENT_TYPE       TABLESPACE_NAME                EXTENT_ID  FILE_ID
-------------------- ------------------ ------------------------------ ---------- ----------
INFOS_SOCIETES       TABLE              USERS                          0          7</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Il nous reste donc à effectuer le dump du block en question</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate"> SQL&gt; alter system dump datafile 7 block 372;

System altered.</pre>
<p>&nbsp;</p>
<p>Puis vérifier la trace généré</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">[oracle@ip-172-44-2-141 trace]$ ls -rtl
.....
-rw-r-----. 1 oracle dba 964 Mar 14 16:56 CAPDATADB_ora_3063.trm
-rw-r-----. 1 oracle dba 13073 Mar 14 16:56 CAPDATADB_ora_3063.trc</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Et si l&#8217;édite la trace, nous avons les informations lisibles des données de la ligne</p>
<p>&nbsp;</p>
<pre>[bach][oracle@ip-172-44-2-141 trace]$ vi CAPDATADB_ora_3063.trc
.......
Block dump from disk:
buffer tsn: 4 rdba: 0x01c00174 (7/372)
scn: 0x2d6b05 seq: 0x01 flg: 0x06 tail: 0x6b050601
frmt: 0x02 chkval: 0x6336 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007F5EAFC45000 to 0x00007F5EAFC47000
7F5EAFC45000 0000A206 01C00174 002D6B05 06010000 [....t....k-.....]
7F5EAFC45010 00006336 00000001 0001212A 002D6B04 [6c......*!...k-.]
7F5EAFC45020 00008000 00320002 01C00170 001A0007 [......2.p.......]
7F5EAFC45030 000003DA 010007DD 003700CA 00002001 [..........7.. ..]
7F5EAFC45040 002D6B05 00000000 00000000 00000000 [.k-.............]
7F5EAFC45050 00000000 00000000 00000000 00000000 [................]
7F5EAFC45060 00000000 00010100 0014FFFF 1F4E1F62 [............b.N.]
7F5EAFC45070 00001F4E 1F620001 00000000 00000000 [N.....b.........]
7F5EAFC45080 00000000 00000000 00000000 00000000 [................]
Repeat 499 times
7F5EAFC46FC0 00000000 012C0000 61430704 74616470 [......,...Capdat]
7F5EAFC46FD0 41530261 72203923 64206575 616C2065 [a.SA#9 rue de la]
7F5EAFC46FE0 726F7020 64206574 75422065 65562063 [ porte de Buc Ve]
7F5EAFC46FF0 69617372 73656C6C 5108C303 6B050601 [rsailles...Q...k]
Block header dump: 0x01c00174
end_of_block_dump
End dump data blocks tsn: 4 file#: 7 minblk 372 maxblk 372[/bash]

</pre>
<p>&nbsp;</p>
<p>Reprenons maintenant notre table avec le chiffrement activé à savoir la table &#8220;infos_employes&#8221;</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate"> SQL&gt; select rowid from manu.infos_employes where NOM='Rami';

ROWID
------------------
AAASEcAAHAAAAFsAAA</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Rappelons que les colonnes adresse et code postal sont chiffrées.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select DBMS_ROWID.ROWID_BLOCK_NUMBER('AAASEcAAHAAAAFsAAA') &quot;Block number&quot; from DUAL;

Block number
------------
364</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Le tablespace est le même que la table &#8220;infos_societes&#8221;. Nous pouvons lancer le dump du block de données.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; alter system dump datafile 7 block 364;

System altered.</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Et lors d&#8217;une tentative de lecture sur la trace générée</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-141 trace]$ vi CAPDATADB_ora_3131.trc
.....
Block dump from disk:
buffer tsn: 4 rdba: 0x01c0016c (7/364)
scn: 0x2d5c3b seq: 0x01 flg: 0x06 tail: 0x5c3b0601
frmt: 0x02 chkval: 0xc56d type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007FC2BF1F4000 to 0x00007FC2BF1F6000
7FC2BF1F4000 0000A206 01C0016C 002D5C3B 06010000 [....l...;\-.....]
7FC2BF1F4010 0000C56D 00000001 0001211C 002D5BD0 [m........!...[-.]
7FC2BF1F4020 00008000 00320002 01C00168 00110005 [......2.h.......]
7FC2BF1F4030 0000042C 0100019D 000A014D 00002002 [,.......M.... ..]
7FC2BF1F4040 002D5C3B 00000000 00000000 00000000 [;\-.............]
7FC2BF1F4050 00000000 00000000 00000000 00000000 [................]
7FC2BF1F4060 00000000 00020100 0016FFFF 1E6A1E80 [..............j.]
7FC2BF1F4070 00001E6A 1F0D0002 00001E80 00000000 [j...............]
7FC2BF1F4080 00000000 00000000 00000000 00000000 [................]
Repeat 485 times
7FC2BF1F5F60 8EC02273 4486D297 E07C4F05 9C90F57B [s&quot;.....D.O|.{...]
7FC2BF1F5F70 04012C44 6D6D4508 65756E61 6152046C [D,...Emmanuel.Ra]
7FC2BF1F5F80 8844696D 7068CF63 36DD3BAB 98AD8A29 [miD.c.hp.;.6)...]
7FC2BF1F5F90 09A57855 59F7EC5E 18DDCBC6 22B4D7D7 [Ux..^..Y.......&quot;]
7FC2BF1F5FA0 46B26F31 45302B6F F053AA6D 81ECCB82 [1o.Fo+0Em.S.....]
7FC2BF1F5FB0 F74A0CC2 5735C61A 58C03130 C2BA128F [..J...5W01.X....]
7FC2BF1F5FC0 6193530D 34C00E2B F231A006 9EB73BA2 [.S.a+..4..1..;..]
7FC2BF1F5FD0 DE7291C6 2B5EBC99 02CEA21C E627E11B [..r...^+......'.]
7FC2BF1F5FE0 0620CDFA 810E446B A5D64062 955C5360 [.. .kD..b@..`S\.]
7FC2BF1F5FF0 0092AA85 8215A721 844747EB 5C3B0601 [....!....GG...;\]
Block header dump: 0x01c0016c
 50 67 87 7b f4 69 c8 de e0 a1 49 ad 7e 2e f6 c6 78 e9 56 09 10 ef 78 bd a0
31 d3 10 ba 21 19 8e a3 fe 4d 6b 0d c8 52 a9 7e a5 08 c1 b2 fd 65 a2 ce 87
03 b1 b9 df 03 58 93 e3 32 2c a6 63 19 47 1e ae ff 52
col 3: [52]
ab d0 54 ec ca 0a 6c 64 d5 42 a9 68 ed 3e cb 53 db 11 33 0b 27 38 9b 08 39
50 de 4e a5 9f 39 ea 86 02 f8 73 22 c0 8e 97 d2 86 44 05 4f 7c e0 7b f5 90
9c 44
end_of_block_dump
End dump data blocks tsn: 4 file#: 7 minblk 364 maxblk 364</pre>
<p>&nbsp;</p>
<p>Nous récupérons le nom et prénom , mais les informations adresse et code postal ne sont pas lisibles directement.</p>
<p>&nbsp;</p>
<h2>Les restrictions</h2>
<p>&nbsp;</p>
<p>Oracle TDE comporte également certaines restrictions, ne pouvant fonctionner sous les conditions suivantes.</p>
<p>Il ne sera pas possible d’utiliser le chiffrage sur une table du schéma SYS, ni même chiffrer une colonne avec des type LONG ou LOB.</p>
<p>Pour les colonnes clés primaires ou clés étrangères, il ne sera pas possible d’utiliser le mécanisme SALT. L’option NO SALT sera alors utiliser. Une erreur sera rencontrée si tel est le cas</p>
<p>&nbsp;</p>
<pre><span style="color: #993300;">ORA-28338: cannot encrypt indexed column(s) with salt</span></pre>
<p>&nbsp;</p>
<p>Pour chaque donnée chiffrée sur une colonne, il faudra prendre en compte le fait que celle-ci utilise 20 bytes de plus en raison de l’utilisation du check d’intégrité (checksum via SHA-1).</p>
<p>&nbsp;</p>
<p>L’utilisation de Oracle TDE n&#8217;est réservé qu’aux index de type balancé (B-TREE), de plus, comme le stockage de la donnée est chiffrée, l’index ne peut trouver de correspondance logique, en outre, il ne sera pas possible d’effectuer du « range scan » dans la clause WHERE d’une opération SQL (pas de WHERE T &gt; 1000).<br />
Seuls les prédicats d’égalité sur une valeur sont possibles (WHERE T=1).</p>
<p>Le seul moyen de faire du range scan sur un index est de chiffré le tablespace et que cet index y soit inclus.</p>
<p>TDE ne gère pas non plus les tablespaces de type transportables.</p>
<p>Pour faire un export des données chiffrées , il est indispensable d’utiliser DataPump en passant alors dans la commande expdp le password de la master key :</p>
<p>&nbsp;</p>
<pre><span style="color: #993300;">$ expdp « ‘/ as sysdba’ » DIRECTORY=&lt;DIR&gt;  ENCRYPTION_PASSWORD=*******</span></pre>
<p>&nbsp;</p>
<p>Attention également aux performances, les benchmarks que l’on peut trouver concernant TDE indiquent en moyenne des valeurs supérieures de 10 à 35% en terme de consommation CPU.</p>
<p>&nbsp;</p>
<h2>Pour aller plus loin &#8230;</h2>
<p>&nbsp;</p>
<p>Afin de pouvoir gérer une solution de sécurité globale, Oracle a créé une appliance nommée Oracle Key Vault (OKV).<br />
Cet outil permet de stocker et centraliser des informations relatives à la sécurité d’un parc informatique, celles-ci pouvant correspondre à des wallets mais aussi des keystores java (JKS), des fichiers de type credential ou keystore JCE (java cryptography extension).</p>
<p>&nbsp;</p>
<p>Oracle Key vault fournit donc une plateforme de sécurité ou l&#8217;on peut centraliser un ensemble de stratégies Oracle TDE au sein d’une entreprise.<br />
Cette appliance centralise l’ensemble des wallets des serveurs d’une entreprise.<br />
Les informations de connexions ainsi que les requêtes de cryptage/decryptage transitent via le réseau entre le Oracle Key Vault et les serveurs bases de données.</p>
<p>L’appliance Oracle Key Vault communique avec différents composants.<br />
Ceux-ci pouvant se caractériser par :</p>
<ul>
<li>Des composants Oracle TDE (colonnes ou tablespaces cryptée)</li>
<li>Des keystores files , ou JCEKS</li>
<li>La management console, qui servira à l’administration de OKV</li>
<li>Une appliance backup pour les besoins de sauvegarde de OKV</li>
<li>Les wallets de connexion et Java keystores</li>
</ul>
<p>&nbsp;</p>
<p>N&#8217;hésitez pas à laisser un commentaire.</p>
<p>&nbsp;</p>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /><strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" rel="bookmark" title="13 mars 2024">Le chiffrement Oracle : native network encryption</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/oracle-19c-les-partitions-hybrides-sur-les-tables/" rel="bookmark" title="8 juin 2021">Oracle 19c : Les partitions hybrides</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/" rel="bookmark" title="4 octobre 2023">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/" rel="bookmark" title="16 novembre 2023">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/oracle-text-pour-dba-oracle-part-1/" rel="bookmark" title="2 avril 2012">Oracle Text pour DBA Oracle : Partie 1</a> (Capdata team) [Oracle]</li>
</ul>
<p><!-- Similar Posts took 2.158 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%3D10515&#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%3D10515&#038;title=Le%20chiffrement%20Oracle%20%3A%20Transparent%20Data%20Encryption%20sur%20Oracle%2019c" 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=Le%20chiffrement%20Oracle%20%3A%20Transparent%20Data%20Encryption%20sur%20Oracle%2019c&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10515" 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/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/">Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Le chiffrement Oracle : native network encryption</title>
		<link>https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/</link>
					<comments>https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Wed, 13 Mar 2024 09:30:00 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[chiffrement]]></category>
		<category><![CDATA[sécurité]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10467</guid>

					<description><![CDATA[<p>&#160; Pour continuer dans la série &#8220;chiffrement et bases de données&#8221;, nous allons évoquer le sujet &#8220;native network encryption&#8221; et &#8220;data integrity&#8221; dans le cadre d&#8217;une connexion client /serveur sur une base de données Oracle 19c. &#160; Présentation &#160; Le&#8230; <a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" 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/le-chiffrement-oracle-native-network-encryption/">Le chiffrement Oracle : native network encryption</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10467&#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%3D10467&#038;title=Le%20chiffrement%20Oracle%20%3A%20native%20network%20encryption" 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=Le%20chiffrement%20Oracle%20%3A%20native%20network%20encryption&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10467" 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><img loading="lazy" decoding="async" class="alignnone wp-image-10470 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/Capture-decran-2024-02-28-144722.png" alt="" width="545" height="298" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/Capture-decran-2024-02-28-144722.png 545w, https://blog.capdata.fr/wp-content/uploads/2024/02/Capture-decran-2024-02-28-144722-300x164.png 300w" sizes="auto, (max-width: 545px) 100vw, 545px" /></p>
<p>&nbsp;</p>
<p>Pour continuer dans la série &#8220;chiffrement et bases de données&#8221;, nous allons évoquer le sujet &#8220;native network encryption&#8221; et &#8220;data integrity&#8221; dans le cadre d&#8217;une connexion client /serveur sur une base de données Oracle 19c.</p>
<p>&nbsp;</p>
<h2>Présentation</h2>
<p>&nbsp;</p>
<p>Le mécanisme consiste à chiffrer le trafic réseau entre un client Oracle et la base cible distante. Pour cela, nous allons tester, via des traces, ce que nous pouvons relever dans les informations SQLNet entre le client et le serveur de bases de données.</p>
<p>Attention, point important à remonter, cette fonctionnalité est accessible pour <strong>toutes les éditions Oracle</strong> et ce pour toutes versions. C&#8217;est plutôt une bonne nouvelle quand on connaît le coût d&#8217;une licence Entreprise Edition avec &#8220;Advanced Security&#8221;.</p>
<p>Consulter l&#8217;information sur <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/Licensing-Information.html#GUID-AB56CEE3-955E-4E56-8B44-6075E889C283">ce lien</a></p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10471 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/license_oracle.png" alt="" width="833" height="357" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/license_oracle.png 833w, https://blog.capdata.fr/wp-content/uploads/2024/02/license_oracle-300x129.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/02/license_oracle-768x329.png 768w" sizes="auto, (max-width: 833px) 100vw, 833px" /></p>
<p>&nbsp;</p>
<h3>Les algorithmes de chiffrement</h3>
<p>Pour procéder au chiffrement client/server, Oracle travaille sur une liste d&#8217;algorithmes que l&#8217;on peut utiliser.</p>
<p>Voici une liste, extraite du site Oracle, pouvant être prise en charge.</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10472 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/algo.png" alt="" width="686" height="164" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/algo.png 686w, https://blog.capdata.fr/wp-content/uploads/2024/02/algo-300x72.png 300w" sizes="auto, (max-width: 686px) 100vw, 686px" /></p>
<p>Depuis la version 19c, seuls les algorithmes AES (Advanced Encryption Standard) ont été validés. On peut ajouter à cette liste AES192.</p>
<p>Pour utiliser les nouveaux algorithmes AES, Oracle recommande l&#8217;installation d&#8217;un patch de prise en charge à prendre en compte dans la note <a href="https://support.oracle.com/CSP/main/article?cmd=show&amp;amp;type=NOT&amp;amp;id=2118136.2" target="_blank" rel="noopener">2118136.2</a>. Ceci pour les anciennes versions Oracle.</p>
<p>L&#8217;algorithme 3DES peut également être choisi, mais attention aux soucis de performances récemment relevés par Oracle. A n&#8217;utiliser qu&#8217;avec des systèmes très performants en CPU.</p>
<p>&nbsp;</p>
<h3>Data Integrity</h3>
<p>Il faudra penser à conserver l’intégrité des données, en effectuant un « checksum » sur celles-ci.<br />
Oracle utilise pour cela les algorithmes SHA pour procéder: SHA1, SHA256, SHA384, and SHA512.</p>
<p>Attention, MD5 est encore compatible mais plus recommandé.</p>
<p>&nbsp;</p>
<h3>Performances</h3>
<p>Voici quelques tests de performances effectués permettant d’avoir un retour sur les temps de réponses des<br />
connexions passant à travers le chiffrement. Ces tests ont été faits sur certains algorithmes qui sont dépréciés aujourd&#8217;hui.</p>
<p>Un exemple, trouvé sur le net, est le suivant :</p>
<p>&#8220;on interroge 100 fois la table des objets d’une base (dba_objects).<br />
On relèvera les temps écoulés pour chacune de ces opérations.&#8221;<br />
Chacun des tests est exécuté 3 fois sur les différents algorithmes utilisés avec chacun des checksums.<br />
Ce qui donne le tableau suivant</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10473 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/tableau_perf.png" alt="" width="426" height="226" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/tableau_perf.png 426w, https://blog.capdata.fr/wp-content/uploads/2024/02/tableau_perf-300x159.png 300w" sizes="auto, (max-width: 426px) 100vw, 426px" /></p>
<p>&nbsp;</p>
<p>On considère une base 100% une connexion sans paramètres de chiffrement défini.</p>
<p>&nbsp;</p>
<h2>Configuration</h2>
<p>&nbsp;</p>
<p>Afin de mettre en œuvre le chiffrement, nous devrons passer des paramètres dans le fichier &#8220;<span style="color: #993300;">sqlnet.ora</span>&#8221;<br />
coté client, et coté serveur.</p>
<p>Les paramètres qui seront à configurer cotés client sont:</p>
<p><strong><em>SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT</em></strong><br />
<strong><em>SQLNET.CRYPTO_SEED</em></strong><br />
<strong><em>SQLNET.ENCRYPTION_TYPES_CLIENT</em></strong><br />
<strong><em>SQLNET.CRYPTO_CHECKSUM_CLIENT</em></strong><br />
<strong><em>SQLNET.ENCRYPTION_CLIENT</em></strong></p>
<p>&nbsp;</p>
<p>Au vu des résultats sur les performances et des recommandations Oracle, nous utiliserons, pour le test,<br />
les algorithmes <strong>AES256</strong> pour le chiffrement et <strong>SHA384</strong> pour le checksum.</p>
<p>A noter, que l&#8217;on pourra définir une clé &#8220;<strong><span style="color: #003366;">crypto_seed</span></strong>&#8221; pour la connexion, en choisissant une valeur de<br />
10 à 70 caractères alphanumériques.<br />
Pour la partie serveur, les paramètres à définir sont les suivants :</p>
<p><em><strong>SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER</strong></em><br />
<em><strong>SQLNET.CRYPTO_SEED</strong></em><br />
<em><strong>SQLNET.ENCRYPTION_TYPES_SERVER</strong></em><br />
<em><strong>SQLNET.CRYPTO_CHECKSUM_SERVER</strong></em><br />
<em><strong>SQLNET.ENCRYPTION_SERVER</strong></em></p>
<p>&nbsp;</p>
<p>Les algorithmes devront être <strong>identiques</strong> (<strong>encryption</strong> et <strong>crypto_checksum</strong>)<strong> entre le client et le serveur</strong><br />
pour que la communication se fasse.</p>
<p>Les 4 valeurs que l&#8217;on peut choisir pour le paramètre SQLNET.ENCRYPTION coté client et serveur sont les suivantes :</p>
<pre>REQUESTED : le chiffrement est demandé mais si ce n'est pas possible, le flux client/serveur se passera via trafic non chiffré.
REJECTED : Pas de chiffrement demandé.
REQUIRED : seul le trafic avec chiffrement sera accepté.
ACCEPTED : le client ou le serveur accepte toute connexion chiffrée ou non chiffrée. Valeur par défaut si "native network encryption" n'est pas activé</pre>
<p>&nbsp;</p>
<p>Le tableau suivant donnera la matrice de compatibilité des différents modes de négociations utilisés<br />
pour une connexion.</p>
<p>Il est possible, selon les paramètres <span style="color: #ff0000;">SQLNET.ENCRYPTION</span> et <span style="color: #ff0000;">SQLNET.CRYPTO_CHECKSUM </span>choisis d&#8217;activer ou non le chiffrement.</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10475 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/tab_integ.png" alt="" width="833" height="683" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/tab_integ.png 833w, https://blog.capdata.fr/wp-content/uploads/2024/02/tab_integ-300x246.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/02/tab_integ-768x630.png 768w" sizes="auto, (max-width: 833px) 100vw, 833px" /></p>
<p>Ce tableau est extrait du site Oracle sur <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/configuring-network-data-encryption-and-integrity.html#GUID-02CA9997-996F-413E-B00D-55DB96675ACB">ce lien</a></p>
<p>Lorsque la connexion échoue, le client reçoit une erreur ORA-12650.<br />
C&#8217;est le cas, si nous sommes en REJECTED coté client et REQUIRED coté serveur, par exemple.</p>
<p>&nbsp;</p>
<pre><code></code></pre>
<h4></h4>
<h4>Test avec chiffrement actif</h4>
<p>&nbsp;</p>
<p>Nous testons dans un premier temps une connexion avec chiffrement actif.<br />
Aussi, nous choisissons, coté serveur le mode ACCEPTED.</p>
<p>Puis coté client REQUESTED, ceci pour<br />
l&#8217;encryption et le checksum.</p>
<p>En résumé, voici les paramètres que nous définirons dans le sqlnet.ora coté client :</p>
<p><span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT= (SHA384)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_SEED = &#8216;&#8221;i5rrruweotcadsfdsafjkdsfqp5f201p45mxskdlfdasf&#8221;&#8216;</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_TYPES_CLIENT= (AES256)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_CLIENT = requested</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_CLIENT = requested</strong></span></p>
<p>et le sqlnet.ora coté serveur :</p>
<p><span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER= (SHA384)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_SEED = &#8216;&#8221;4fhfguweotcadsfdsafjkdsfqp5f201p45mxskdlfdasf&#8221;&#8216;</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_TYPES_SERVER= (AES256)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_SERVER = accepted</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_SERVER = accepted</strong></span></p>
<p>&nbsp;</p>
<p>Les opérations se déroulent avec les caractéristiques suivantes</p>
<ul>
<li>Pour la partie client, une installation d&#8217;Oracle Client 12.2.</li>
<li>Pour la partie serveur, 1 base CAPDATADB version 19c avec le patchser 19.11 d&#8217;avril 2021</li>
</ul>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

SQL&gt; show user
USER is &quot;MANU&quot;</pre>
<p>&nbsp;</p>
<p>Voici le contexte de connexion coté base de données. On s&#8217;assure ainsi que le chiffrement client/serveur est bien actif</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; set pages 3000 linesize 190
SQL&gt; select c.sid, c.serial#, c.network_service_banner,c.client_connection from v$session_connect_info c
2* inner join v$session s on (s.sid=c.sid) and s.username ='MANU';

SID        SERIAL#    NETWORK_SERVICE_BANNER                                                                          CLIENT_CONNEC
---------- ---------- ----------------------------------------------------------------------------------------------- -------------
32         1490       TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production                           Homogeneous
32         1490       Encryption service for Linux: Version 19.0.0.0.0 - Production                                   Homogeneous
32         1490       AES256 Encryption service adapter for Linux: Version 19.0.0.0.0 - Production                    Homogeneous
32         1490       Crypto-checksumming service for Linux: Version 19.0.0.0.0 - Production                          Homogeneous
32         1490       SHA384 Crypto-checksumming service adapter for Linux: Version 19.0.0.0.0 - Production           Homogeneous</pre>
<p>&nbsp;</p>
<p>L&#8217;algorithme <strong>AES256</strong> et le checksum <strong>SHA384</strong> apparaissent dans les infos de connexions.<br />
On pourra aussi mettre en place une trace sqlnet afin de voir que les données envoyées via le réseau sont bien chiffrées durant cette connexion.</p>
<p>Les paramètres à mettre en place sur le &#8220;<strong>sqlnet.ora</strong>&#8221; du client sont les suivants :</p>
<p><strong>TRACE_UNIQUE_CLIENT = on</strong><br />
<strong>TRACE_DIRECTORY_CLIENT = /opt/oracle/product/12.2/dbhome_1/network/trace</strong><br />
<strong>TRACE_FILE_CLIENT = trace_client</strong><br />
<strong>TRACE_LEVEL_CLIENT = 16</strong><br />
<strong>DIAG_ADR_ENABLED = OFF</strong></p>
<p>&nbsp;</p>
<p>Les traces sont donc générées, coté client, dans le répertoire « /<strong>opt/oracle/product/12.2/dbhome_1/network/trace</strong> ».</p>
<p>Nous passons le paramètre <strong>TRACE_LEVEL_CLIENT</strong> à 16 ou niveau SUPPORT afin de récupérer le maximum<br />
d’informations, y compris les trames envoyées vers le serveur.</p>
<p>On teste une connexion, avec une simple interrogation sur la vue d’instance (<strong>v$instance</strong>).</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">[oracle@ip-172-44-2-250 ]$ sqlplus manu@CAPDATADB

SQL*Plus: Release 12.2.0.1.0 Production on Wed Feb 28 15:34:23 2024

Copyright (c) 1982, 2016, Oracle. All rights reserved.

Enter password:
Last Successful login time: Wed Feb 28 2024 15:20:13 +00:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

SQL&gt; select INSTANCE_NAME,HOST_NAME,VERSION,STATUS,DATABASE_STATUS from v$instance;

INSTANCE_NAME
----------------
HOST_NAME
----------------------------------------------------------------
VERSION           DATABASE_STATUS
----------------- ------------ -----------------
CAPDATADB
ip-172-44-2-141.capdata-aws.fr
19.0.0.0.0        ACTIVE</pre>
<p>&nbsp;</p>
<p>Une trace est alors générée dans le répertoire $ORACLE_HOME/network/trace. Le pid de notre connexion Oracle est le 5129.</p>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-250 trace]$ ls -rtl
total 332
-rw-rw----. 1 oracle oinstall 337246 Feb 28 15:36 trace_client_5129.trc</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Si l’on observer les packets envoyés par le client, on voit que ceux-ci sont bien chifrrés. La requête<br />
select sur la vue v$instance n’apparait pas en clair, les trames reçus par le serveur sont chiffrés :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10482 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/packet_chiffre.png" alt="" width="707" height="436" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/packet_chiffre.png 707w, https://blog.capdata.fr/wp-content/uploads/2024/02/packet_chiffre-300x185.png 300w" sizes="auto, (max-width: 707px) 100vw, 707px" /></p>
<p>&nbsp;</p>
<p>On retrouve bien les informations d’encryption dans les trames des packets.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10483 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_actif.png" alt="" width="892" height="229" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_actif.png 892w, https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_actif-300x77.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_actif-768x197.png 768w" sizes="auto, (max-width: 892px) 100vw, 892px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h4>Test sans chiffrement actif</h4>
<p>&nbsp;</p>
<p>Nous effectuons le même test avec une connexion sans chiffrage actif.</p>
<p>S’il l’on se réfère au tableau de compatibilité client/serveur sur les mode ENCRYPTION, en plaçant les algorithmes d’encryption sur REJECTED coté<br />
client et ACCEPTED coté serveur, nous sommes dans un scénario ou le chiffrement est désactivé.</p>
<p>Les paramètres du sqlnet.ora sont les suivants coté client :</p>
<p>&nbsp;</p>
<p><span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT= (SHA384)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_SEED = &#8216;&#8221;i5rrruweotcadsfdsafjkdsfqp5f201p45mxskdlfdasf&#8221;&#8216;</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_TYPES_CLIENT= (AES256)</strong></span><br />
<span style="color: #000080;"><strong>SQLNET.CRYPTO_CHECKSUM_CLIENT = <span style="color: #800000;">rejected</span></strong></span><br />
<span style="color: #000080;"><strong>SQLNET.ENCRYPTION_CLIENT = <span style="color: #800000;">rejected</span></strong></span></p>
<p>&nbsp;</p>
<p>Coté serveur, on ne modifie rien, on laisse à ACCEPTED.</p>
<p>Les opérations se déroulent à l’identique, avec les mêmes environnements que précédemment.</p>
<p>Si l&#8217;on interroge les contextes de connexion du user MANU sur la base</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select c.sid, c.serial#, c.network_service_banner,c.client_connection from v$session_connect_info c
2* inner join v$session s on (s.sid=c.sid) and s.username ='MANU'



SID        SERIAL#     NETWORK_SERVICE_BANNER                                                                          CLIENT_CONNEC
---------- -------     ----------------------------------------------------------------------------------------------- -------------
32         24306       TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production                           Homogeneous
32         24306       Encryption service for Linux: Version 19.0.0.0.0 - Production                                   Homogeneous
32         24306       Crypto-checksumming service for Linux: Version 19.0.0.0.0 - Production                          Homogeneous</pre>
<p>&nbsp;</p>
<p>On voit à présent que les algorithmes AES256 et SHA384 n’apparaissaient pas dans les informations de<br />
connexion.</p>
<p>S’il l’on se réfère à nouveau au fichier trace, on cherche si les informations sont plus « parlantes ».<br />
On réexécute un test de connexion, avec la même requête sur un simple select dans la vue v$instance .</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

SQL&gt; set pages 3000 linesize 200
SQL&gt; select INSTANCE_NAME,HOST_NAME,VERSION,STATUS,DATABASE_STATUS from v$instance;

INSTANCE_NAME     HOST_NAME                                                        VERSION           STATUS       DATABASE_STATUS
----------------  ---------------------------------------------------------------- ----------------- ------------ -----------------
CAPDATADB         ip-172-44-2-141.capdata-aws.fr                                   19.0.0.0.0        OPEN         ACTIVE</pre>
<p>&nbsp;</p>
<p>La trace générée est la suivante , avec le pid 6116 cette fois ci.</p>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ip-172-44-2-250 trace]$ ls -lrt
total 568
-rw-rw----. 1 oracle oinstall 337246 Feb 28 15:36 trace_client_5129.trc
-rw-rw----. 1 oracle oinstall 239231 Feb 28 15:58 trace_client_6116.trc</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Nous voyons dans la trace que le chiffrement est bien inactif :</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10484 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_inactif.png" alt="" width="1097" height="308" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_inactif.png 1097w, https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_inactif-300x84.png 300w, https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_inactif-1024x288.png 1024w, https://blog.capdata.fr/wp-content/uploads/2024/02/encrypt_inactif-768x216.png 768w" sizes="auto, (max-width: 1097px) 100vw, 1097px" /></p>
<p>&nbsp;</p>
<p>Nous disposons mêmes des informations du user connecté, son nom, son programme, mais aussi, la<br />
machine depuis laquelle il est connecté, et le PID attachée à la session (ici 616 ce qui correspond au<br />
nom de la trace généré).</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10485 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/packet_nonchiff.png" alt="" width="707" height="550" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/packet_nonchiff.png 707w, https://blog.capdata.fr/wp-content/uploads/2024/02/packet_nonchiff-300x233.png 300w" sizes="auto, (max-width: 707px) 100vw, 707px" /></p>
<p>&nbsp;</p>
<p>Et surtout, nous retrouvons bien notre requête  select sur la vue from v$instance  dans les packets envoyés au<br />
serveur :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10486 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/requete_claire.png" alt="" width="729" height="484" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/requete_claire.png 729w, https://blog.capdata.fr/wp-content/uploads/2024/02/requete_claire-300x199.png 300w" sizes="auto, (max-width: 729px) 100vw, 729px" /></p>
<p>&nbsp;</p>
<p>Avec, en clair, le résultat obtenu pour cette requête :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10487 size-full" src="https://blog.capdata.fr/wp-content/uploads/2024/02/donnees_claires.png" alt="" width="723" height="475" srcset="https://blog.capdata.fr/wp-content/uploads/2024/02/donnees_claires.png 723w, https://blog.capdata.fr/wp-content/uploads/2024/02/donnees_claires-300x197.png 300w" sizes="auto, (max-width: 723px) 100vw, 723px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>Points d&#8217;attention</h3>
<p>&nbsp;</p>
<p>Selon les besoins en sécurité, nous choisirons d’activer, ou non, le chiffrement sur les connexions client/serveur en fonction du degré de criticité du serveur et de son exposition au réseau.</p>
<p>Etant donné que l’option se place sur le fichier sqlnet.ora, toutes les bases attachés à ce ORACLE_HOME prendront en compte le chiffrement.</p>
<p>Attention également à désactiver les traces coté client, une fois les divers tests terminés.</p>
<p>En effet, les répertoires ‘trace’ dans $ORACLE_HOME/network/trace peuvent vite être saturés.</p>
<p>Dans le sqlnet.ora du client :</p>
<p><strong>TRACE_LEVEL_CLIENT = OFF</strong></p>
<p>&nbsp;</p>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/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/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/" rel="bookmark" title="16 novembre 2023">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/" rel="bookmark" title="20 mars 2024">Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/" rel="bookmark" title="4 octobre 2023">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/se-connecter-a-sql-server-a-travers-oracle-quelle-drale-didae/" rel="bookmark" title="12 juin 2008">Se connecter à SQL Server à travers Oracle, quelle drôle d&#8217;idée ?</a> (Capdata team) [OracleVintage]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/" rel="bookmark" title="29 novembre 2023">Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</a> (Capdata team) [AzureSQL Server]</li>
</ul>
<p><!-- Similar Posts took 2.091 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%3D10467&#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%3D10467&#038;title=Le%20chiffrement%20Oracle%20%3A%20native%20network%20encryption" 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=Le%20chiffrement%20Oracle%20%3A%20native%20network%20encryption&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10467" 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/le-chiffrement-oracle-native-network-encryption/">Le chiffrement Oracle : native network encryption</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pseudonymiser vos bases PostgreSQL</title>
		<link>https://blog.capdata.fr/index.php/pseudonymisation-postgresql/</link>
					<comments>https://blog.capdata.fr/index.php/pseudonymisation-postgresql/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Tue, 30 Jan 2024 17:54:28 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[anonymisation]]></category>
		<category><![CDATA[pseudomynisation]]></category>
		<category><![CDATA[rgpd]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10377</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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.398 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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/pseudonymisation-postgresql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Oracle 23c : se protéger des attaques par injections SQL grâce à SQL Firewall</title>
		<link>https://blog.capdata.fr/index.php/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/</link>
					<comments>https://blog.capdata.fr/index.php/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/#respond</comments>
		
		<dc:creator><![CDATA[Emmanuel RAMI]]></dc:creator>
		<pubDate>Wed, 03 Jan 2024 08:00:02 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[firewall]]></category>
		<category><![CDATA[sécurité]]></category>
		<category><![CDATA[sql injection]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10405</guid>

					<description><![CDATA[<p>  Bonjour à toutes et tous et bonne année 2024 ! Nous débutons cette nouvelle année avec un article, une fois de plus, en lien avec la sécurité ! C&#8217;est un sujet que nous avons grandement abordé au cours de&#8230; <a href="https://blog.capdata.fr/index.php/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/" 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/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/">Oracle 23c : se protéger des attaques par injections SQL grâce à SQL Firewall</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10405&#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%3D10405&#038;title=Oracle%2023c%20%3A%20se%20prot%C3%A9ger%20des%20attaques%20par%20injections%20SQL%20gr%C3%A2ce%20%C3%A0%20SQL%20Firewall" 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=Oracle%2023c%20%3A%20se%20prot%C3%A9ger%20des%20attaques%20par%20injections%20SQL%20gr%C3%A2ce%20%C3%A0%20SQL%20Firewall&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10405" 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><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-10406" src="https://blog.capdata.fr/wp-content/uploads/2023/12/par_feu-300x235.png" alt="" width="300" height="235" srcset="https://blog.capdata.fr/wp-content/uploads/2023/12/par_feu-300x235.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/12/par_feu.png 398w" sizes="auto, (max-width: 300px) 100vw, 300px" />  Bonjour à toutes et tous et bonne année 2024 !</p>
<p>Nous débutons cette nouvelle année avec un article, une fois de plus, en lien avec la sécurité ! C&#8217;est un sujet que nous avons grandement abordé au cours de l&#8217;année 2023, sur les SGBD SQL Server et PostgreSQL.<br />
Pour débuter 2024 dans la même voie, je vous propose une présentation de la brique SQL Firewall présente dans la version Oracle 23c.</p>
<p>&nbsp;</p>
<p>Pour qualifier et tester les nouveautés Oracle 23c, nous avons la possibilité de télécharger et installer l&#8217;édition FREE Edition qui, de plus, est utilisable &#8220;on prem&#8221;.</p>
<p>Ce tableau, fourni par Oracle, nous montre les différentes offres présentes :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10407 size-full" src="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle_offres.png" alt="" width="838" height="622" srcset="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle_offres.png 838w, https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle_offres-300x223.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle_offres-768x570.png 768w" sizes="auto, (max-width: 838px) 100vw, 838px" /></p>
<p>&nbsp;</p>
<p>Vous remarquerez qu&#8217;il n&#8217;y a, à ce jour, ni Standard Edition 2, ni offre Enterprise Edition on prem.</p>
<p>Une note Oracle indique cependant une éventuelle prise en charge de cette version dès le premier semestre 2024 sous Linux x86-64</p>
<p><a href="https://support.oracle.com/epmos/faces/DocContentDisplay?_afrLoop=274050397197876&amp;id=742060.1&amp;_afrWindowMode=0&amp;_adf.ctrl-state=10y50u7v5a_4#aref_section19">Release Schedule of Current Database Releases (Doc ID 742060.1)</a></p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10408 size-full" src="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform.png" alt="" width="502" height="89" srcset="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform.png 502w, https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform-300x53.png 300w" sizes="auto, (max-width: 502px) 100vw, 502px" /></p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10409 size-full" src="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform2.png" alt="" width="500" height="522" srcset="https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform2.png 500w, https://blog.capdata.fr/wp-content/uploads/2023/12/Oracle23cplatform2-287x300.png 287w" sizes="auto, (max-width: 500px) 100vw, 500px" /></p>
<p>Nous suivrons plus précisément les informations à ce sujet au cour de ce début d&#8217;année.</p>
<p>&nbsp;</p>
<h2>Installation Oracle 23c FREE Edition</h2>
<p>&nbsp;</p>
<p>Nous disposons d&#8217;une VM EC2 de type Rocky Linux 8.6 pour tester la nouvelle version Oracle 23c</p>
<pre class="brush: bash; title: ; notranslate">[oracle@ etc]$ cat os-release
NAME=&quot;Rocky Linux&quot;
VERSION=&quot;8.6 (Green Obsidian)&quot;
ID=&quot;rocky&quot;
ID_LIKE=&quot;rhel centos fedora&quot;
VERSION_ID=&quot;8.6&quot;
PLATFORM_ID=&quot;platform:el8&quot;
PRETTY_NAME=&quot;Rocky Linux 8.6 (Green Obsidian)&quot;
ANSI_COLOR=&quot;0;32&quot;
CPE_NAME=&quot;cpe:/o:rocky:rocky:8:GA&quot;
HOME_URL=&quot;https://rockylinux.org/&quot;
BUG_REPORT_URL=&quot;https://bugs.rockylinux.org/&quot;
ROCKY_SUPPORT_PRODUCT=&quot;Rocky Linux&quot;
ROCKY_SUPPORT_PRODUCT_VERSION=&quot;8&quot;
REDHAT_SUPPORT_PRODUCT=&quot;Rocky Linux&quot;
REDHAT_SUPPORT_PRODUCT_VERSION=&quot;8&quot;</pre>
<p>L&#8217;installation de la version Oracle 23c Free Edition sur un fork Red Hat est on ne peut plus simple :</p>
<ul>
<li>Télécharger le package rpm de preinstallation -&gt;  <a class="" href="https://yum.oracle.com/repo/OracleLinux/OL8/developer/x86_64/getPackage/oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm" data-lbl="oracle-database-preinstall-23c-1-0-1-el8-x86-64-rp">oracle-database-preinstall-23c-1.0-1.el8.x86_64.rpm</a></li>
<li>Télécharger le package rpm d&#8217;installation du moteur Oracle 23c -&gt; <a href="https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23c-1.0-1.el8.x86_64.rpm" data-lbl="database-free-23c-1-0-1-el8-x86-64-rpm:red-hat">oracle-database-free-23c-1.0-1.el8.x86_64.rpm</a></li>
</ul>
<p>Une fois téléchargés et copiés vers le serveur linux, passer à l&#8217;installation via rpm sous &#8220;<strong>root</strong>&#8221;</p>
<p>&nbsp;</p>
<p>&#8212; Le package de preinstallation permettant la configuration OS propre à Oracle.</p>
<pre class="brush: bash; title: ; notranslate">[root@ ~]# rpm -iv /tmp/oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm
warning: /tmp/oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY
Verifying packages...
Preparing packages...
oracle-database-preinstall-23c-1.0-0.5.el8.x86_64</pre>
<p>&nbsp;</p>
<p>&#8212; Puis le package d&#8217;installation du moteur Oracle 23c. Attention, l&#8217;installation va se faire dans un répertoire &#8220;/opt&#8221;. Prévoir une place d&#8217;au moins 8Go sur ce montage.</p>
<pre class="brush: bash; title: ; notranslate">[root@ ~]# rpm -iv /tmp/oracle-database-free-23c-1.0-1.el8.x86_64.rpm
warning: /tmp/oracle-database-free-23c-1.0-1.el8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY
Verifying packages...
Preparing packages...
oracle-database-free-23c-1.0-1.x86_64
[INFO] Executing post installation scripts...
[INFO] Oracle home installed successfully and ready to be configured.
To configure Oracle Database Free, optionally modify the parameters in '/etc/sysconfig/oracle-free-23c.conf' and then run '/etc/init.d/oracle-free-23c configure' as root.</pre>
<p>&nbsp;</p>
<p>Valider l&#8217;installation des packages</p>
<pre class="brush: bash; title: ; notranslate">[root@ ~]# rpm -qav | grep -i oracle
oracle-database-preinstall-23c-1.0-0.5.el8.x86_64
oracle-database-free-23c-1.0-1.x86_64</pre>
<p>&nbsp;</p>
<pre class="brush: bash; title: ; notranslate">[root@ ~]# ls /opt/oracle/product/23c/dbhomeFree/
addnode clone ctx deinstall env.ora instantclient jdk LICENSE nls OPatch ord plsql R root.sh slax sqlplus usm
assistants crs cv demo has inventory jlib md odbc opmn oss precomp racg runInstaller sqlcl srvm utl
bin crypto data diagnostics hs javavm ldap mgw olap oracore oui python rdbms schagent.conf sqlj ss_oracle.sdo.acl xdk
cfgtoollogs css dbs dv install jdbc lib network oml4py oraInst.loc perl QOpatch relnotes sdk sqlpatch ucp</pre>
<pre class="brush: bash; title: ; notranslate">[root@ ~]# /opt/oracle/product/23c/dbhomeFree/root.sh
Check /opt/oracle/product/23c/dbhomeFree/install/root_************_2023-12-20_11-38-30-060078626.log for the output of root script</pre>
<p>&#8212; Comme indiqué en fin d&#8217;installation, lancer &#8216;/etc/init.d/oracle-free-23c configure&#8217; sous &#8220;<strong>root</strong>&#8221; pour créer une nouvelle instance. Le script est interactif et vous serez amener à saisir certaines informations comme le mot de passe SYS/SYSTEM et PDB_ADMIN.</p>
<pre class="brush: bash; title: ; notranslate">[root@~]# /etc/init.d/oracle-free-23c configure
Specify a password to be used for database accounts. Oracle recommends that the password entered should be at least 8 characters in length, contain at least 1 uppercase character, 1 lower case character and 1 digit [0-9]. Note that the same password will be used for SYS, SYSTEM and PDBADMIN accounts:
Confirm the password:
Configuring Oracle Listener.
Listener configuration succeeded.
Configuring Oracle Database FREE.
Enter SYS user password:
************
Enter SYSTEM user password:
**************
Enter PDBADMIN User Password:
************
Prepare for db operation
7% complete
Copying database files
29% complete
Creating and starting Oracle instance
30% complete
33% complete
36% complete
39% complete
[WARNING] ORA-20002: Directory creation failed
ORA-06512: at &quot;SYS.DBMS_QOPATCH&quot;, line 1644
ORA-06512: at &quot;SYS.DBMS_QOPATCH&quot;, line 1521
ORA-06512: at line 1

43% complete
Completing Database Creation
47% complete
49% complete
50% complete
Creating Pluggable Databases
54% complete
71% complete
Executing Post Configuration Actions
93% complete
Running Custom Scripts
100% complete
Database creation complete. For details check the logfiles at:
/opt/oracle/cfgtoollogs/dbca/FREE.
Database Information:
Global Database Name:FREE
System Identifier(SID):FREE
Look at the log file &quot;/opt/oracle/cfgtoollogs/dbca/FREE/FREE1.log&quot; for further details.

Database configuration failed. Check logs under '/opt/oracle/cfgtoollogs/dbca'.</pre>
<p>&nbsp;</p>
<p>Ne pas tenir compte de l&#8217;erreur lors de la création du DIRECTORY Oracle, celui ci pointe vers un répertoire inexistant sur la machine. Il nous sera possible d&#8217;en créer un ultérieurement.</p>
<p>&nbsp;</p>
<h2>Présentation SQL Firewall</h2>
<p>Comme son nom l&#8217;indique , Oracle 23c SQL Firewall est un firewall applicatif qui, au delà d&#8217;un firewall web classique (WAF), est capable d&#8217;interpréter le code SQL en entrée directement en base.<br />
Comme le montre l&#8217;image extraite de la documentation Oracle officielle, le processus passe par une phase d&#8217;apprentissage des différentes requêtes envoyées à la base de données.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10417 size-full" src="https://blog.capdata.fr/wp-content/uploads/2023/12/oracle_sql_firewall_etapes.png" alt="" width="716" height="121" srcset="https://blog.capdata.fr/wp-content/uploads/2023/12/oracle_sql_firewall_etapes.png 716w, https://blog.capdata.fr/wp-content/uploads/2023/12/oracle_sql_firewall_etapes-300x51.png 300w" sizes="auto, (max-width: 716px) 100vw, 716px" /></p>
<p>&nbsp;</p>
<p>Une liste des requêtes dites &#8220;autorisées&#8221; doit être générée afin de valider les opérations business officielles d&#8217;une production classique au cour de la journée.<br />
Ceci peut bien évidemment être facilité par un éditeur qui connait parfaitement son application, et donc son modèle conceptuel de données et les requêtes SQL qui en découlent.</p>
<p>Une fois cette phase d&#8217;apprentissage établie, nous allons pouvoir valider les requêtes capturées  et dresser la liste &#8220;verte&#8221; des requêtes autorisées.</p>
<p>C&#8217;est à partir de la que l&#8217;on pourra potentiellement, empêcher tout autre code SQL de passer en base.<br />
Le filtrage peut se faire selon plusieurs contextes à savoir, un utilisateur en particulier, une adresse IP où bien un programme.</p>
<p>&nbsp;</p>
<h2>Les prérequis pour Oracle 23c SQL Firewall</h2>
<p>&nbsp;</p>
<p>Rappelons que depuis la version 21c, Oracle nous impose la gestion du multitenant avec la possibilité de créer 3 PDBs gratuitement.<br />
L&#8217;instance FREE comporte donc bien une PDB exploitable en lecture/écriture</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">[oracle@ ~]$ . oraenv
ORACLE_SID = [oracle] ? FREE
The Oracle base has been set to /opt/oracle
[oracle@ ~]$ sqlplus / as sysdba

SQL*Plus: Release 23.0.0.0.0 - Production on Wed Dec 20 11:46:08 2023
Version 23.3.0.23.09

Copyright (c) 1982, 2023, Oracle. All rights reserved.

Connected to:
Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09

SQL&gt; show pdbs;

CON_ID     CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
2          PDB$SEED                       READ ONLY  NO
3          FREEPDB1                       READ WRITE NO
</pre>
<p>&nbsp;</p>
<p>Il sera donc nécessaire, pour se connecter à la PDB FREEPPDB1 depuis un compte applicatif, d&#8217;ajouter une entrée dans le tnsnames.</p>
<p>&nbsp;</p>
<pre>FREEPDB1 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = ip-********.eu-west-3.compute.internal)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = FREEPDB1)
)
)
</pre>
<h4>Configuration des données.</h4>
<p>Pour notre cas pratique, nous créons 3 utilisateurs Oracle dans la PDB. 1 utilisateur administrateur du Firewall, 1 utilisateur propriétaire des objets et 1 utilisateur applicatif utilisant des ordres DML sur les objets. A noter le rôle &#8220;<strong>sql_firewall_admin</strong>&#8221; pour le compte &#8220;FW_ADMIN&#8221;.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; alter session set container = FREEPDB1;

SQL&gt; create user FW_ADMIN identified by passwdfwadmin;
SQL&gt; grant create session, sql_firewall_admin, audit_admin to FW_ADMIN;
SQL&gt; create user application_owner identified by passwdappliowner default tablespace USERS quota unlimited on USERS;
SQL&gt; grant create session, create table, create view, create procedure, create synonym to application_owner;
SQL&gt; create user application identified by passwdappli default tablespace USERS quota unlimited on USERS;
SQL&gt; grant create session to application;
SQL&gt; grant select any table on schema application_owner to application;
</pre>
<p>&nbsp;</p>
<p>Création des objets et du jeu de données</p>
<pre class="brush: sql; title: ; notranslate">

SQL&gt;  create table salaries ( id number(15), name VARCHAR2(128), address VARCHAR2(400), date_entrée VARCHAR2(128));
SQL&gt;  create table entreprise (ent_id VARCHAR2(128), raison_sociale varchar2(20),taille_salaries number(5));

SQL&gt;  insert into salaries values (154484, 'Manuel', '14 rue voltaire', '16/12/2020');
SQL&gt;  insert into salaries values (275558, 'Jack', '24 rue du départ', '12/10/2005');
SQL&gt;  insert into salaries values (285548, 'Cyril', '27 avenue Pasteur', '01/02/2006');
SQL&gt;  insert into salaries values (472245, 'Thomas', '12 avenue principale', '15/02/2021');

SQL&gt;  insert into entreprise values (12232,'SARL',1200);
SQL&gt;  insert into entreprise values (13456,'SARL', 500);
SQL&gt;  insert into entreprise values (22522,'SA',288);
SQL&gt;  insert into entreprise values (25485,'SA', 144);
SQL&gt;  insert into entreprise values (31411,'SA',524);
SQL&gt;  insert into entreprise values (36879,'SARL', 56);
SQL&gt;  insert into entreprise values (40125,'EURL', 120);
SQL&gt;  insert into entreprise values (44588, 'SA', 2510);

SQL&gt;  create or replace view application_owner.somme_salaries_sa
as
select raison_sociale, sum(taille_salaries) as &quot;Somme_SA&quot;
from application_owner.entreprise
where raison_sociale='SA'
group by raison_sociale;

SQL&gt;  create or replace procedure maj_salarie_addresse (id number, address varchar2)
is
req varchar2(1000);
begin
req := 'BEGIN UPDATE salaries SET address = ''' || address || ''' WHERE id = ''' || id || '''; COMMIT; END;';
DBMS_OUTPUT.PUT_LINE('Query: ' || req);
execute immediate req;
end;
/

SQL&gt;  CREATE OR REPLACE PROCEDURE maj_entreprise_salaries ( id number, taille_salaries number)
IS
req VARCHAR2(1000);
BEGIN

req := 'BEGIN UPDATE entreprise SET taille_salaries =''' || taille_salaries || ''' WHERE ent_id = ''' || id || '''; COMMIT; END;';
DBMS_OUTPUT.PUT_LINE('Query: ' || req);
EXECUTE IMMEDIATE req;
END;
/

SQL&gt;  grant execute on application_owner.maj_salarie_addresse to application;
SQL&gt;  grant execute on application_owner.maj_entreprise_salaries to application;
SQL&gt;  grant select on application_owner.somme_salaries_sa to application;
SQL&gt;  grant insert, update, delete on application_owner.salaries to application;
SQL&gt;  grant insert, update, delete on application_owner.entreprise to application;
SQL&gt;  create public synonym somme_salaries_sa for application_owner.somme_salaries_sa;
SQL&gt;  create public synonym maj_salarie_addresse for application_owner.maj_salarie_addresse;
SQL&gt;  create public synonym maj_entreprise_salaries for application_owner.maj_entreprise_salaries;
</pre>
<p>&nbsp;</p>
<h2>Capture des requêtes dans Oracle 23c SQL Firewall</h2>
<p>&nbsp;</p>
<p>A cette étape, nous pouvons commencer à capturer de l&#8217;activité afin de débuter &#8220;l&#8217;apprentissage&#8221; pour le SQL Firewall.</p>
<p>Tout d&#8217;abord, il nous faut activer le Firewall et vérifier son statut avec le compte FW_ADMIN.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect fw_admin@freepdb1
Enter password:
Connected.
SQL&gt; exec dbms_sql_firewall.enable;

PL/SQL procedure successfully completed.

SQL&gt;  select STATUS,STATUS_UPDATED_ON from dba_sql_firewall_status;

STATUS   STATUS_UPDATED_ON
-------- ---------------------------------------------------------------------------
ENABLED  20-DEC-23 03.45.23.677823 PM +00:00</pre>
<p>&nbsp;</p>
<p>Démarrer la capture d&#8217;activité pour l&#8217;utilisateur Oracle nommé application.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt;  exec dbms_sql_firewall.create_capture('APPLICATION'); </pre>
<p>Vérifier le statut de la capture</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; col LAST_STARTED_ON for a35
SQL&gt; col LAST_STOPPED_ON for a35
SQL&gt; select * from dba_sql_firewall_captures where username='APPLICATION';

USERNAME        TOP_LEVEL_ONLY STATUS   LAST_STARTED_ON                     LAST_STOPPED_ON
--------------- -------------- -------- ----------------------------------- -----------------------------------
APPLICATION     N              ENABLED  20-DEC-23 03.49.48.573900 PM +00:00                                     </pre>
<p>&nbsp;</p>
<p>La suite consiste donc à générer de l&#8217;activité avec le compte APPLICATION</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; conn application@FREEPDB1
Connected.
SQL&gt; execute maj_salarie_addresse(154484, '18 rue voltaire');

SQL&gt; execute maj_entreprise_salaries(25485, 146);

SQL&gt; select name, address from application_owner.salaries where id = 472245;

SQL&gt; insert into application_owner.salaries values (510024, 'Marie', '12 rue de l eglise', '20/12/2023');

SQL&gt; select name, address from application_owner.salaries where id = 510024;

SQL&gt; delete from application_owner.salaries where id = 510024;

SQL&gt; select name, address application_owner.salaries where id = 510024;

SQL&gt; select * from application_owner.somme_salaries_sa;

SQL&gt; select count(*) from application_owner.salaries where date_entrée &amp;gt; '01/01/2015';</pre>
<p>&nbsp;</p>
<p>Allons voir à présent ce que le Firewall a capturé comme requêtes. Stoppons la capture et allons lire dans les logs du Firewall</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; exec dbms_sql_firewall.stop_capture('APPLICATION');

PL/SQL procedure successfully completed.

SQL&gt; select username, ip_address, login_time, client_program, os_user from dba_sql_firewall_session_logs order by login_time;

USERNAME        IP_ADDRESS      LOGIN_TIME                          CLIENT_PROGRAM                                     OS_USER
--------------- --------------- ----------------------------------- -------------------------------------------------- --------------------
APPLICATION     172.44.****     20-DEC-23 04.08.38.884619 PM +00:00 sqlplus@ip-********.fr (TNS V1-V3)                 oracle
APPLICATION     172.44.****     20-DEC-23 04.15.26.633200 PM +00:00 sqlplus@ip-********.fr (TNS V1-V3)                 oracle
APPLICATION     172.44.****     20-DEC-23 04.19.46.220001 PM +00:00 sqlplus@ip-********.fr (TNS V1-V3)                 oracle</pre>
<p>&nbsp;</p>
<p>Nous avons capturé les différentes connexions avec le compte APPLICATION, grâce à cela, les informations sur les SQL lancés sont enregistrées.<br />
A noter que les variables enregistrées sont &#8220;bindés&#8221;</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select username, top_level, command_type, sql_text,sql_signature,accessed_objects from dba_sql_firewall_capture_logs where username = 'APPLICATION' order by command_type, sql_signature;

USERNAME TOP_LEVEL COMMAND_TYPE
--------------- --------- ----------------------------------------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_SIGNATURE
----------------------------------------------------------------
ACCESSED_OBJECTS
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
APPLICATION Y DELETE
DELETE FROM APPLICATION_OWNER.SALARIES WHERE ID=:&quot;SYS_B_0&quot;
0D3C48AFA7C43F32036A1398B2C9FED8250C57D00C9731332E8B6BFAAD25F3A0
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION N EXECUTE
BEGIN UPDATE ENTREPRISE SET TAILLE_SALARIES=? WHERE ENT_ID=?; COMMIT; END;
243A35B41DAB8171B60C30BE90A3D725DEEDA3A22C8E118B5EB7E99C8D73E890
&quot;APPLICATION_OWNER&quot;.&quot;ENTREPRISE&quot;

APPLICATION Y EXECUTE
BEGIN MAJ_SALARIE_ADDRESSE (?,?); END;
6A8D28786FE3BFEBFEBEE70CF2AF81A3587F5FE8239696BE9939D443EFB52789
&quot;APPLICATION_OWNER&quot;.&quot;MAJ_SALARIE_ADDRESSE&quot;

APPLICATION N EXECUTE
BEGIN UPDATE SALARIES SET ADDRESS=? WHERE ID=?; COMMIT; END;
ED3DA8C122700A399BEB2D6C0CC88E5FC1B46A61961BF515E5DB0526856AD86A
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION Y EXECUTE
BEGIN MAJ_ENTREPRISE_SALARIES (?,?); END;
F4F779482E86BC0537A6BC59470DB7E8A0EBBCF3B6F2CB21CE3242DC2863C45A
&quot;APPLICATION_OWNER&quot;.&quot;MAJ_ENTREPRISE_SALARIES&quot;

APPLICATION Y INSERT
INSERT INTO APPLICATION_OWNER.SALARIES VALUES (:&quot;SYS_B_0&quot;,:&quot;SYS_B_1&quot;,:&quot;SYS_B_2&quot;,:&quot;SYS_B_3&quot;)
C24B251483E84751353F7B379414FFFBEBCC593E3E1CC32C0E419B69A66CA807
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION Y SELECT
SELECT NAME,ADDRESS FROM APPLICATION_OWNER.SALARIES WHERE ID=:&quot;SYS_B_0&quot;
633C0FBB9B54CB5F6BC0A75B9BA34A2EA453671A78A799644CF4D7956F6EEE4B
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION Y SELECT
SELECT NAME,ADDRESS FROM APPLICATION_OWNER.SALARIES WHERE ID=:&quot;SYS_B_0&quot;
633C0FBB9B54CB5F6BC0A75B9BA34A2EA453671A78A799644CF4D7956F6EEE4B
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION Y SELECT
SELECT DECODE (USER,:&quot;SYS_B_0&quot;,XS_SYS_CONTEXT (:&quot;SYS_B_1&quot;,:&quot;SYS_B_2&quot;),USER) FROM SYS.DUAL
8CD0E5550A8AF32553BDED7C77B8CC1FD103C51F438E11F1BC5F9CA315102794
&quot;SYS&quot;.&quot;DUAL&quot;

APPLICATION Y SELECT
SELECT DECODE (USER,:&quot;SYS_B_0&quot;,XS_SYS_CONTEXT (:&quot;SYS_B_1&quot;,:&quot;SYS_B_2&quot;),USER) FROM SYS.DUAL
8CD0E5550A8AF32553BDED7C77B8CC1FD103C51F438E11F1BC5F9CA315102794
&quot;SYS&quot;.&quot;DUAL&quot;

APPLICATION Y SELECT
SELECT DECODE (USER,:&quot;SYS_B_0&quot;,XS_SYS_CONTEXT (:&quot;SYS_B_1&quot;,:&quot;SYS_B_2&quot;),USER) FROM SYS.DUAL
8CD0E5550A8AF32553BDED7C77B8CC1FD103C51F438E11F1BC5F9CA315102794
&quot;SYS&quot;.&quot;DUAL&quot;

APPLICATION Y SELECT
SELECT * FROM APPLICATION_OWNER.SOMME_SALARIES_SA
BCCB5D0F6B4DE96D7C9E52C8678C489698D4ED23F8FEEA120FFC701560C99D0C
&quot;APPLICATION_OWNER&quot;.&quot;SOMME_SALARIES_SA&quot;

APPLICATION Y SELECT
SELECT * FROM APPLICATION_OWNER.SOMME_SALARIES_SA
BCCB5D0F6B4DE96D7C9E52C8678C489698D4ED23F8FEEA120FFC701560C99D0C
&quot;APPLICATION_OWNER&quot;.&quot;SOMME_SALARIES_SA&quot;

APPLICATION Y SELECT
SELECT COUNT (*) FROM APPLICATION_OWNER.SALARIES WHERE DATE_ENTR??E &amp;gt;:&quot;SYS_B_0&quot;
EE000C28DC61F8D21DCDC9BB6880A315EB12CC3682E0D3CD47A01EACF915EF98
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION Y SELECT
SELECT COUNT (*) FROM APPLICATION_OWNER.SALARIES WHERE DATE_ENTR??E &amp;gt;:&quot;SYS_B_0&quot;
EE000C28DC61F8D21DCDC9BB6880A315EB12CC3682E0D3CD47A01EACF915EF98
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;

APPLICATION N UPDATE
UPDATE ENTREPRISE SET TAILLE_SALARIES=:&quot;SYS_B_0&quot; WHERE ENT_ID=:&quot;SYS_B_1&quot;
36FE5B2C529FD88D46DD6C69649D30C12719CD2600945F8EF2D4B3D039B4CD06
&quot;APPLICATION_OWNER&quot;.&quot;ENTREPRISE&quot;

APPLICATION N UPDATE
UPDATE SALARIES SET ADDRESS=:&quot;SYS_B_0&quot; WHERE ID=:&quot;SYS_B_1&quot;
6D68C8BB02FFE46E37900E60275B0AB0698CF1217B95B3CA1C789E29FE8D0B6B
&quot;APPLICATION_OWNER&quot;.&quot;SALARIES&quot;</pre>
<p>&nbsp;</p>
<p>Chaque ordre SQL a une signature propre à lui. C&#8217;est ce qui permet au SQL Firewall de reconnaitre par la suite, tout ordre faisant parti de la liste.</p>
<p>&nbsp;</p>
<h2>Générer la liste &#8220;verte&#8221; de requêtes autorisées</h2>
<p>La liste &#8220;verte&#8221; se crée avec le compte FW_ADMIN</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect fw_admin@FREEPDB1
Enter password:
Connected.
SQL&gt; exec dbms_sql_firewall.generate_allow_list('APPLICATION');

PL/SQL procedure successfully completed.

SQL&gt;; col GENERATED_ON for a35
SQL&gt; select USERNAME,GENERATED_ON,STATUS,STATUS_UPDATED_ON,TOP_LEVEL_ONLY from dba_sql_firewall_allow_lists where username='APPLICATION';

USERNAME              GENERATED_ON                        STATUS   STATUS_UPDATED_ON                   TOP_LEVEL_ONLY
---------------       ----------------------------------- -------- ----------------------------------- --------------
APPLICATION           20-DEC-23 04.49.56.169700 PM +00:00 DISABLED 20-DEC-23 04.49.56.169700 PM +00:00 N</pre>
<p>&nbsp;</p>
<p>Pour le moment, le statut de la liste est à DISABLED car nous l&#8217;avons juste générée.</p>
<p>Comme évoqué quelques lignes au dessus, le contexte peut se faire via l&#8217;adresse IP</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from sys.dba_sql_firewall_allowed_ip_addr where username='APPLICATION';

USERNAME             IP_ADDRESS
-------------------- ------------------------------
APPLICATION          172.44.*********</pre>
<p>&nbsp;</p>
<p>Le programme associé</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from sys.dba_sql_firewall_allowed_os_prog where username='APPLICATION';

USERNAME               OS_PROGRAM
--------------------   --------------------------------------------------------------------------------------------------------------------------------
APPLICATION            sqlplus@ip-172-44-*************s.fr (TNS V1-V3)</pre>
<p>&nbsp;</p>
<p>Ou bien le user OS</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from sys.dba_sql_firewall_allowed_os_user where username='APPLICATION';

USERNAME               OS_USER
--------------------   --------------------------------------------------------------------------------------------------------------------------------
APPLICATION            oracle</pre>
<p>&nbsp;</p>
<h4>Activer la liste &#8220;verte&#8221;</h4>
<p>&nbsp;</p>
<p>la suite consiste à valider et surtout, activer cette liste. Ceci se fait avec le compte FW_ADMIN.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect fw_admin@FREEPDB1
Enter password:
Connected.
SQL&gt; exec dbms_sql_firewall.enable_allow_list('APPLICATION');

PL/SQL procedure successfully completed.

SQL&gt; select username, status, top_level_only, enforce, block from dba_sql_firewall_allow_lists where username='APPLICATION';

USERNAME             STATUS          TOP_LEVEL_ONLY ENFORCE         BLOCK
-------------------- --------------  -------------- --------------- --------------
APPLICATION          ENABLED         N              ENFORCE_ALL     N</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Cette liste &#8220;verte&#8221; est donc activée, elle &#8220;trappe&#8221; les futures requêtes qui ne matcheraient pas avec celles qui sont enregistrées. Mais, le &#8220;BLOCK&#8221; est à N, donc l&#8217;utilisateur n&#8217;a pas de message d&#8217;erreur en cas de saisie d&#8217;une requête non reconnue.</p>
<p>Le user APPLICATION peut tout à fait faire un SELECT sur une table.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect application@FREEPDB1
Enter password:
Connected.
SQL&gt; select name from application_owner.salaries where id &gt; 300000;

NAME
--------------------------------------------------------------------------------------------------------------------------------
Manuel
Jack
Cyril</pre>
<p>&nbsp;</p>
<p>Mais une fois connecté avec le user FW_ADMIN, une simple interrogation dans la vue DBA_SQL_FIREWALL_VIOLATIONS nous donne l&#8217;entrée suivante</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select USERNAME,COMMAND_TYPE,SQL_TEXT,IP_ADDRESS,OS_USER,OCCURRED_AT from dba_sql_firewall_violations;

USERNAME             COMMAND_TYPE                  SQL_TEXT                                                                              IP_ADDRESS          OS_USER&lt;/pre&gt;
-------------------- ---------------               --------------------------------------------------------------------------------      ---------------     ---------------
OCCURRED_AT
---------------------------------------------------------------------------
APPLICATION          SELECT                        SELECT NAME FROM APPLICATION_OWNER.SALARIES WHERE ID &lt;:&quot;SYS_B_0&quot;                      172.44.******        oracle
21-DEC-23 02.09.53.047973 PM +00:00</pre>
<p>&nbsp;</p>
<p>La requête lancée par le compte APPLICATION est donc bien enregistrée dans la liste des violations des règles du firewall.</p>
<p>&nbsp;</p>
<h4>Bloquer les requêtes non désirées</h4>
<p>Le blocage de requêtes s&#8217;effecctue, avec le compte FW_ADMIN, en activant le mode BLOCK sur la liste &#8220;verte&#8221;</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect fw_admin@FREEPDB1
Enter password:
Connected.
SQL&gt; exec dbms_sql_firewall.update_allow_list_enforcement('APPLICATION', block=&gt;TRUE);

PL/SQL procedure successfully completed.</pre>
<p>&nbsp;</p>
<p>Et lorsque nous souhaitons interroger la même requête avec le compte APPLICATION</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect application@FREEPDB1
Enter password:
Connected.
SQL&gt; select name from application_owner.salaries where id &gt; 300000;
select name from application_owner.salaries where id &gt; 300000
*
ERROR at line 1:
ORA-47605: SQL Firewall violation
Help: https://docs.oracle.com/error-help/db/ora-47605/
</pre>
<p>&nbsp;</p>
<p>Nous avons une belle erreur &#8220;ORA-47605&#8221; nous indiquant une violation des règles du Firewall. Petite nouveauté avec la 23c, Oracle nous donne l&#8217;URL pour rechercher directement la définition du message d&#8217;erreur.</p>
<p>Ceci se vérifie pour toute autre requête ne faisant pas partie de la liste &#8220;verte&#8221;</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from application_owner.entreprise where taille_salaries &gt; 10000;
select * from application_owner.entreprise where taille_salaries &gt; 10000
*
ERROR at line 1:
ORA-47605: SQL Firewall violation
Help: https://docs.oracle.com/error-help/db/ora-47605/</pre>
<p>&nbsp;</p>
<p>Dans la vue DBA_SQL_FIREWALL_VIOLATIONS, ces 2 dernières requêtes nous sont relevées</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; connect fw_admin@FREEPDB1
Enter password:
Connected.
SQL&gt; select USERNAME,COMMAND_TYPE,SQL_TEXT,OCCURRED_AT from dba_sql_firewall_violations;

USERNAME COMMAND_TYPE SQL_TEXT
-------------------- --------------- --------------------------------------------------------------------------------
OCCURRED_AT
---------------------------------------------------------------------------
APPLICATION SELECT SELECT * FROM APPLICATION_OWNER.ENTREPRISE WHERE TAILLE_SALARIES &gt;:&quot;SYS_B_0&quot;
21-DEC-23 02.30.31.690170 PM +00:00

APPLICATION SELECT SELECT NAME FROM APPLICATION_OWNER.SALARIES WHERE ID &lt;:&quot;SYS_B_0&quot;
21-DEC-23 02.09.53.047973 PM +00:00

APPLICATION SELECT SELECT NAME FROM APPLICATION_OWNER.SALARIES WHERE ID &lt;:&quot;SYS_B_0&quot;
21-DEC-23 02.24.29.523017 PM +00:00</pre>
<p>&nbsp;</p>
<p>Il est bien entendu possible de purger la table de log des requêtes interdites afin de réinitialiser son contenu.</p>
<p>&nbsp;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; exec dbms_sql_firewall.purge_log('APPLICATION', NULL, dbms_sql_firewall.VIOLATION_LOG);

PL/SQL procedure successfully completed.

SQL&gt; select USERNAME,COMMAND_TYPE,SQL_TEXT,OCCURRED_AT from dba_sql_firewall_violations;

no rows selected</pre>
<p>&nbsp;</p>
<h2>Conclusion</h2>
<p>&nbsp;</p>
<p>Gardez à l&#8217;esprit que cette fonctionnalité SQL Firewall de Oracle peut vous protéger de toute injection SQL non désirée, mais ceci sous entend surtout que la phase &#8220;d&#8217;apprentissage&#8221; soit correctement maitrisée afin de ne pas se retrouver avec une application potentiellement bloquée par des ordres SQL qui ne s&#8217;exécutent plus.</p>
<p>C&#8217;est bien pour cela que cette phase peut être longue, et nécessite le recensement de très nombreuses requêtes trappées dans DBA_SQL_FIREWALL_ALLOWED_SQL.<br />
La valeur de &#8220;SQL_SIGNATURE&#8221; est essentielle , dans la mesure ou le relevé des variables est &#8220;bindé&#8221;, les requêtes suivantes seront autorisées même si les résultats sont différents  :</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select name, address from application_owner.salaries where id = 510024;

SQL&gt; select name, address from application_owner.salaries where id = 285548;</pre>
<p>&nbsp;</p>
<p>Pensez également à purger la vue DBA_SQL_FIREWALL_VIOLATIONS de façon régulière, tout en portant attention sur ce qui aura été relevé durant les périodes de production.</p>
<p>&nbsp;</p>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /><strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/installation-oracle-64-bits-sur-red-hat-5/" rel="bookmark" title="5 juin 2009">Installation Oracle 64 bits sur Red Hat 5</a> (Capdata team) [OracleVintage]</li>
<li><a href="https://blog.capdata.fr/index.php/oracle-text-pour-dba-oracle-part-1/" rel="bookmark" title="2 avril 2012">Oracle Text pour DBA Oracle : Partie 1</a> (Capdata team) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" rel="bookmark" title="13 mars 2024">Le chiffrement Oracle : native network encryption</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/replication-logique-avec-postgresql/" rel="bookmark" title="23 janvier 2020">Réplication logique avec PostgreSQL</a> (Capdata team) [PostgreSQL]</li>
<li><a href="https://blog.capdata.fr/index.php/un-trigger-fait-il-parti-dune-transaction/" rel="bookmark" title="30 mars 2010">Un trigger fait-il parti d&#8217;une transaction ?</a> (Benjamin VESAN) [GénéralMySQLOracleSQL ServerSybase]</li>
</ul>
<p><!-- Similar Posts took 3.172 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%3D10405&#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%3D10405&#038;title=Oracle%2023c%20%3A%20se%20prot%C3%A9ger%20des%20attaques%20par%20injections%20SQL%20gr%C3%A2ce%20%C3%A0%20SQL%20Firewall" 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=Oracle%2023c%20%3A%20se%20prot%C3%A9ger%20des%20attaques%20par%20injections%20SQL%20gr%C3%A2ce%20%C3%A0%20SQL%20Firewall&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10405" 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/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/">Oracle 23c : se protéger des attaques par injections SQL grâce à SQL Firewall</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/oracle-23c-se-proteger-des-attaques-par-injections-sql-grace-a-sql-firewall/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Les Managed Service Account (MSA et gMSA) : se simplifier la vie pour gérer ses comptes de service SQL Server</title>
		<link>https://blog.capdata.fr/index.php/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/</link>
					<comments>https://blog.capdata.fr/index.php/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/#comments</comments>
		
		<dc:creator><![CDATA[Capdata team]]></dc:creator>
		<pubDate>Thu, 30 Nov 2023 09:56:30 +0000</pubDate>
				<category><![CDATA[Operating System]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[gMSA]]></category>
		<category><![CDATA[Managed Service Account]]></category>
		<category><![CDATA[MSA]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Virtual Service Account]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10382</guid>

					<description><![CDATA[<p>Pour faire tourner son service SQL Server sous Windows, il y a différentes écoles. Certains veulent un compte de service créé dans l&#8217;Active Directory, afin d&#8217;y appliquer des GPO et bien les identifier avec un nom explicite. D&#8217;autres préfèrent garder&#8230; <a href="https://blog.capdata.fr/index.php/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/" 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/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/">Les Managed Service Account (MSA et gMSA) : se simplifier la vie pour gérer ses comptes de service SQL Server</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10382&#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%3D10382&#038;title=Les%20Managed%20Service%20Account%20%28MSA%20et%20gMSA%29%20%3A%20se%20simplifier%20la%20vie%20pour%20g%C3%A9rer%20ses%20comptes%20de%20service%20SQL%20Server" 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=Les%20Managed%20Service%20Account%20%28MSA%20et%20gMSA%29%20%3A%20se%20simplifier%20la%20vie%20pour%20g%C3%A9rer%20ses%20comptes%20de%20service%20SQL%20Server&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10382" 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>Pour faire tourner son service SQL Server sous Windows, il y a différentes écoles. Certains veulent un compte de service créé dans l&#8217;Active Directory, afin d&#8217;y appliquer des GPO et bien les identifier avec un nom explicite. D&#8217;autres préfèrent garder les choses simples et laissent les Virtual Service Account (<em>NT Service\MSSQLSERVER</em>).</p>
<p>Le problème avec le passage sur un compte de service dédié est le risque de mauvaise administration et de s&#8217;en servir pour ouvrir une session interactive, combiné avec l&#8217;éventualité que le mot de passe soit récupéré par un utilisateur malicieux. On peut donc mettre en place une stratégie d&#8217;expiration de mot de passe, mais alors la rotation des mots de passe dans le parc peut vite devenir infernale !</p>
<p>C&#8217;est là qu&#8217;intervient une solution assez méconnue : les Managed Service Account. L&#8217;une des principales raisons pour laquelle cette solution est méconnue est qu&#8217;elle nécessite que le niveau fonctionnel de la forêt Active Directory soit au minimum au niveau fonctionnel de <a href="https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/getting-started-with-group-managed-service-accounts#BKMK_gMSA_Req">Windows 2012.</a></p>
<p>Maintenant que nous sommes en 2023, on peut supposer que la plupart des infrastructures Active Directory sont dans des niveaux supérieurs.</p>
<p>Les MSA étaient le premier nom donné à ce type de compte, et ne fonctionnait que pour les service qui tournaient sur une seule machine. Peu de temps après a été ajouté la possibilité de fonctionner sur un cluster avec la notion de Group Managed Service Account. Mais les commandes restent les mêmes.</p>
<h2>Pré-requis à l&#8217;utilisation d&#8217;un Managed Service Account pour SQL Server (MSA/gMSA) :</h2>
<p>Comme indiqué, le premier pré-requis est au niveau de l&#8217;Active Directory qui doit être au niveau fonctionnel 2012 ou supérieur.</p>
<p>Pour SQL Server, si c&#8217;est pour travailler avec une instance &#8220;standalone&#8221; , il faudra un SQL Server 2014. Pour de l&#8217;AlwaysOn et du Failover Cluster Instance (FCI), cela nécessitant la couche cluster, et donc un gMSA, il faudra un SQL Server 2016.</p>
<p>Pour créer un (g)MSA, il faudra soit être administrateur du domaine ou bien disposer du privilège de création des objets de type &#8220;msDS-GroupManagedServiceAccount&#8221;.</p>
<p>Un accès PowerShell avec l&#8217;extension Active Directory (disponible par exemple en installant le feature Windows &#8220;Remote Management&#8221;) doit être présent sur les machines SQL Server.</p>
<p>Afin de faire fonctionnaire les MSA, ils est également nécessaire qu&#8217;une infrastructure KDS soit présente sur le domaine. Si vous ne savez pas si vous en avez une, vous pouvez interrogez votre domaine avec la commande PowerShell Get-KDSRootKey :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10383" src="https://blog.capdata.fr/wp-content/uploads/2023/11/GetRootKey-300x152.png" alt="" width="614" height="311" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/GetRootKey-300x152.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/GetRootKey.png 739w" sizes="auto, (max-width: 614px) 100vw, 614px" /></p>
<p>On voit ici qu&#8217;une clé a été créée le 29/11/2023.</p>
<p>Si jamais vous n&#8217;en avez pas, il faut la créer avec la commande :</p>
<pre class="brush: plain; title: ; notranslate">
Add-KdsRootKey -EffectiveImmediately
</pre>
<p>Bien que le paramètre <em>EffectiveImmediately</em> permette son usage immédiatement, j&#8217;ai rencontré des délais avant que mes machines SQL Server puissent utiliser les MSA, il se peut donc qu&#8217;il faille attendre également chez vous. Par ailleurs, si vous avez plusieurs contrôleurs de domaine, le temps de réplication peut atteindre 10 heures.</p>
<h2>Création d&#8217;un gMSA pour SQL Server :</h2>
<p>Notre but ici est de créer un compte de service managé pour un SQL Server 2022 avec un groupe de disponibilité. Nous sommes dans le scénario le plus compliqué, où SQL Server a déjà été installé et le groupe de disponibilité est déjà présent.</p>
<p>Notre configuration est telle que nous avons deux serveurs LAB1SQL1 et LAB1SQL2 avec un listener pour le groupe de disponibilité LAB1_LSTN. Nous voulons un compte de service commun pour nos deux serveurs SQL que l&#8217;on appellera LAB1_gMSA. Nous voulons également que les SPN soient enregistrés correctement sans intervention supplémentaire.</p>
<h2>Création d&#8217;un groupe AD pour les machines autorisées à utiliser le gMSA :</h2>
<p>Dans la console Active Directory Users and Computers, on va créer un groupe de sécurité et y ajouter les deux comptes ordinateurs de notre groupe de disponibilité :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10384" src="https://blog.capdata.fr/wp-content/uploads/2023/11/group1-300x259.png" alt="" width="577" height="498" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/group1-300x259.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/group1.png 437w" sizes="auto, (max-width: 577px) 100vw, 577px" /><img loading="lazy" decoding="async" class="alignnone wp-image-10385" src="https://blog.capdata.fr/wp-content/uploads/2023/11/group2-264x300.png" alt="" width="515" height="585" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/group2-264x300.png 264w, https://blog.capdata.fr/wp-content/uploads/2023/11/group2.png 400w" sizes="auto, (max-width: 515px) 100vw, 515px" /></p>
<h2>Création du gMSA :</h2>
<p>La création du gMSA se fait avec la commande PowerShell &#8220;New-ADServiceAccount&#8221;. Elle n&#8217;est pas possible par l&#8217;interface graphique &#8220;Active Directory Users and Computers&#8221;.</p>
<pre class="brush: plain; title: ; notranslate"> 
New-ADServiceAccount -Name LAB1_gMSA -DNSHostName LAB1SQL1.LAB1.local -ManagedPasswordIntervalInDays 90 -PrincipalsAllowedToRetrieveManagedPassword LAB1_SQL_Group -ServicePrincipalNames MSSQLSvc/LAB1SQL1.lab1.local, MSSQLSvc/LAB1SQL1.lab1.local:1433, MSSQLSvc/LAB1_LSTN.lab1.local, MSSQLSvc/LAB1_LSTN.lab1.local:1434 -Enabled $true
</pre>
<p>On aura donc un groupe de disponibilité avec les paramètres suivants:</p>
<ul>
<li>Son nom sera LAB1_gMSA</li>
<li>La rotation automatique des mots de passe du compte aura lieu tous les 90 jours (par défaut 30)</li>
<li>La liste des machines autorisées à utiliser le gMSA se trouve dans le groupe LAB1_SQL_Group</li>
<li>Les SPN créés seront : MSSQLSvc/LAB1SQL1.lab1.local, MSSQLSvc/LAB1SQL1.lab1.local:1433, MSSQLSvc/LAB1_LSTN.lab1.local et MSSQLSvc/LAB1_LSTN.lab1.local:1434</li>
<li>Il sera actif dès sa création</li>
</ul>
<p>On peut désormais le voir dans la console &#8220;Active Directory Users and Computers&#8221; :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10387" src="https://blog.capdata.fr/wp-content/uploads/2023/11/gMSA-300x100.png" alt="" width="711" height="237" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/gMSA-300x100.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/gMSA-768x257.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/gMSA.png 832w" sizes="auto, (max-width: 711px) 100vw, 711px" /></p>
<h2>Déploiement du gMSA sur les machines SQL :</h2>
<p>Une fois compte créé, il faut donc le déployer sur les machines SQL. On va se connecter en PowerShell sur les machines SQL et utiliser la commande &#8220;Install-ADServiceAccount&#8221;. Pour rappel, il faut avoir le composant Remote Management pour pouvoir exécuter cette commande. Si jamais vous ne l&#8217;avez pas, vous pouvez le déployer rapidement avec la commande PowerShell</p>
<pre class="brush: plain; title: ; notranslate">
Add-WindowsFeature RSAT-AD-PowerShell
</pre>
<p>Après on peut donc activer notre compte LAB1_gMSA :</p>
<pre class="brush: plain; title: ; notranslate">
Install-ADServiceAccount -Identity LAB1_gMSA
</pre>
<p>Après avoir réalisé ça sur nos deux machines, on peut les spécifier dans notre configuration SQL Server.</p>
<h2>Configuration du gMSA dans SQL Server :</h2>
<p>Dans la console &#8220;SQL Server Configuration Manager&#8221;, dans la section &#8220;SQL Server Services&#8221;, on doit aller dans l&#8217;onglet &#8220;Log On&#8221; des propriétés du service SQL Server (et de son agent éventuellement).</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10388" src="https://blog.capdata.fr/wp-content/uploads/2023/11/service_account-300x231.png" alt="" width="668" height="514" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/service_account-300x231.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/service_account-768x592.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/service_account.png 956w" sizes="auto, (max-width: 668px) 100vw, 668px" /></p>
<p>Comme un objet ordinateur, un gMSA s&#8217;écrit avec un $ à la fin.</p>
<p>Dans SQL Server, on n&#8217;a plus qu&#8217;à lui donner les privilèges suffisants :</p>
<pre class="brush: plain; title: ; notranslate">
CREATE LOGIN [LAB1\LAB1_gMSA$] FROM WINDOWS ; 
GRANT CONNECT ON ENDPOINT::Hadr_endpoint TO [LAB1\LAB1_gMSA$] ;
ALTER SERVER ROLE [sysadmin] ADD MEMBER [LAB1\LAB1_gMSA$]
GO
</pre>
<p>Si on regarde dans les logs, on verra désormais les lignes suivantes :</p>
<pre class="brush: plain; title: ; notranslate">
The service account is 'LAB1\LAB1_gMSA$'. This is an informational message; no user action is required.
[...]
The SQL Server Network Interface library successfully registered the Service Principal Name (SPN) [ MSSQLSvc/LAB1SQL1.lab1.local ] for the SQL Server service.
The SQL Server Network Interface library successfully registered the Service Principal Name (SPN) [ MSSQLSvc/LAB1SQL1.lab1.local:1433 ] for the SQL Server service.
</pre>
<p>Ca y est ! vous avez configuré le gMSA de votre SQL Server. Mais vous pouvez (devriez ?) utiliser également ça pour vos applications qui utilisent un compte de service spécifique.<br />
<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/elastic-job-agent-lagent-sql-server-pour-le-paas-azure/" rel="bookmark" title="4 juillet 2019">Elastic Job Agent : l&#8217;Agent SQL Server pour le PaaS Azure</a> (Capdata team) [AzureSQL Server]</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>
<li><a href="https://blog.capdata.fr/index.php/les-nouveautes-de-sql-server-2022/" rel="bookmark" title="13 juillet 2022">Les nouveautés de SQL Server 2022</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/aws-backup-restore-sql-server-rds-iaas-ec2-et-vice-versa/" rel="bookmark" title="25 février 2019">AWS : Backup Restore SQL Server RDS vers une EC2 ou On-Premise et vice versa !</a> (Emmanuel RAMI) [AWSSQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/importez-une-vm-dun-esx-vers-aws/" rel="bookmark" title="11 décembre 2018">Export d&#8217;une VM d&#8217;un ESX vers une EC2 AWS</a> (Emmanuel RAMI) [AWS]</li>
</ul>
<p><!-- Similar Posts took 2.035 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%3D10382&#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%3D10382&#038;title=Les%20Managed%20Service%20Account%20%28MSA%20et%20gMSA%29%20%3A%20se%20simplifier%20la%20vie%20pour%20g%C3%A9rer%20ses%20comptes%20de%20service%20SQL%20Server" 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=Les%20Managed%20Service%20Account%20%28MSA%20et%20gMSA%29%20%3A%20se%20simplifier%20la%20vie%20pour%20g%C3%A9rer%20ses%20comptes%20de%20service%20SQL%20Server&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10382" 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/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/">Les Managed Service Account (MSA et gMSA) : se simplifier la vie pour gérer ses comptes de service SQL Server</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/les-managed-service-account-msa-et-gmsa-se-simplifier-la-vie-pour-gerer-ses-comptes-de-service-sql-server/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</title>
		<link>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/</link>
					<comments>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/#respond</comments>
		
		<dc:creator><![CDATA[Capdata team]]></dc:creator>
		<pubDate>Wed, 29 Nov 2023 11:10:03 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[always encrypted]]></category>
		<category><![CDATA[certificat]]></category>
		<category><![CDATA[chiffrement]]></category>
		<category><![CDATA[cypher]]></category>
		<category><![CDATA[Key Vault]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10334</guid>

					<description><![CDATA[<p>Comme nous l&#8217;avions vu dans le premier épisode de cette série sur le chiffrement dans SQL Server, la solution la plus rigoureuse pour le chiffrement des données reste Always Encrypted. Celle-ci peut s&#8217;appliquer en complément de TDE qui lui va&#8230; <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/" 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/le-chiffrement-et-sql-server-episode-3-always-encrypted/">Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10334&#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%3D10334&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%203%20%3A%20Always%20Encrypted" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%203%20%3A%20Always%20Encrypted&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10334" 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>Comme nous l&#8217;avions vu dans le premier <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/">épisode</a> de cette série sur le chiffrement dans SQL Server, la solution la plus rigoureuse pour le chiffrement des données reste Always Encrypted. Celle-ci peut s&#8217;appliquer en complément de <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/">TDE</a> qui lui va s&#8217;occuper de protéger l&#8217;intégralité des données et notamment ces backups. Always Encrypted, lui, travaille sur par colonne par colonne et partage les éléments cryptographique avec le composant applicatif qui veut lire ou écrire les données, de manière à que SQL Server ne voit pas les données en clair (ou presque : nous parlerons des Secure Enclaves plus loin)</p>
<h1>Les objets cryptographiques derrière Always Encrypted :</h1>
<p>La technologie derrière Always Encrypted repose sur des clés de chiffrements.</p>
<p>La première est la Column Master Key (ou CMK). C&#8217;est une clé RSA de 2048 bits qui sert à chiffré les clés de chiffrement de données. C&#8217;est cette clé que l&#8217;on devra sécuriser et que l&#8217;on doit administrer avec précaution. C&#8217;est aussi celle-ci que le composant applicatif devra être capable de lire pour chiffrer et déchiffrer les données. Cette clé peut-être stockée soit dans le magasin de certificat Windows, soit le magasin certificat Java si on développe avec le driver JDBC, soit dans un Azure Key Vault. Aucun autre support de gestion de cette clé n&#8217;est disponible pour le moment. Si notre composant applicatif est sous Linux on peut cependant utiliser un Azure Key Vault (par exemple, avec le driver PHP : <a href="https://learn.microsoft.com/fr-fr/sql/connect/php/using-always-encrypted-php-drivers?view=sql-server-ver16">https://learn.microsoft.com/fr-fr/sql/connect/php/using-always-encrypted-php-drivers?view=sql-server-ver16</a> ) ou le driver JDBC (<a href="https://learn.microsoft.com/fr-fr/sql/connect/jdbc/using-always-encrypted-with-the-jdbc-driver?view=sql-server-ver16">https://learn.microsoft.com/fr-fr/sql/connect/jdbc/using-always-encrypted-with-the-jdbc-driver?view=sql-server-ver16</a> ).</p>
<p>Dans cet article, nous nous concentrerons sur l&#8217;Azure Key Vault et le magasin de certificat Windows.</p>
<p>La deuxième est la Column Encryption Key (CEK). Elle sert à chiffrer les données dans la colonne de notre base. Le chiffrement des données se fait par un algorithme publique et reconnu : <strong>AEAD_AES_256_CBC_HMAC_SHA_256</strong> , qui est documenté par l&#8217;IETF. Cela veut dire qu&#8217;à partir que si le stockage de la clé est sécurisé, on peut avoir confiance en ce chiffrement. Dans le cas où la CMK est stockée dans l&#8217;Azure Key Vault, Microsoft (et donc le gouvernement américain, même si la zone Azure est en France, dans le cadre du <a href="https://fr.wikipedia.org/wiki/CLOUD_Act">Cloud Act</a> ) pourrait donc déchiffrer nos données, en théorie.</p>
<p>La CEK va donc chiffrer les données par colonne et va affecter la taille de celles-ci. Avec l&#8217;algorithme AEAD_AES_256_CBC_HMAC_SHA_256, on obtient donc le tableau des tailles suivantes par type :</p>
<table width="416" aria-label="Table 1">
<tbody>
<tr>
<td width="110"><strong>Data Type</strong></td>
<td width="306"><strong>Longueur en octet</strong></td>
</tr>
<tr>
<td width="110">bigint</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">binary</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">bit</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">char</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">date</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">datetime</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">datetime2</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">datetimeoffset</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">decimal</td>
<td width="306">81</td>
</tr>
<tr>
<td width="110">float</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">geography</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">geometry</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">hierarchyid</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">image</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">int</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">money</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">nchar</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">ntext</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">numeric</td>
<td width="306">81</td>
</tr>
<tr>
<td width="110">nvarchar</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">real</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">smalldatetime</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">smallint</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">smallmoney</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">sql_variant</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">sysname</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">text</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">time</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">timestamp<br />
(rowversion)</td>
<td width="306">non supporté</td>
</tr>
<tr>
<td width="110">tinyint</td>
<td width="306">65</td>
</tr>
<tr>
<td width="110">uniqueidentifier</td>
<td width="306">81</td>
</tr>
<tr>
<td width="110">varbinary</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">varchar</td>
<td width="306">Variable se référer à la documentation de l&#8217;algo</td>
</tr>
<tr>
<td width="110">xml</td>
<td width="306">non supporté</td>
</tr>
</tbody>
</table>
<p>On comprend donc que le chiffrement implique forcément de consommer plus d&#8217;espace : <strong>un integer consomme 4 octets, mais une fois chiffré il en consomme 65.</strong></p>
<p>Dans la documentation disponible <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-cryptography?view=sql-server-ver16">ici</a>, on a la formule suivante qui nous permet de déterminer la taille en octet d&#8217;un contenu :</p>
<pre class="brush: plain; title: ; notranslate">
1 + 32 + 16 + (FLOOR(DATALENGTH(cell_data)/16) + 1) * 16  
</pre>
<p>Ainsi par exemple avec une chaine de caractère de 76 caractères telle que &#8220;Par exemple du texte là qui peut être assez long finalement si on est bavard&#8221;, dans SQL Server je fais le calcul suivant :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10338" src="https://blog.capdata.fr/wp-content/uploads/2023/11/always_encrypted_size-300x39.png" alt="" width="892" height="116" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/always_encrypted_size-300x39.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/always_encrypted_size-768x99.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/always_encrypted_size.png 1002w" sizes="auto, (max-width: 892px) 100vw, 892px" /></p>
<p>On a doc 129 octets pour ce varchar(76). Si c&#8217;était du nvarchar(76), cela prendrait 209 octets chiffrés au lieu de 152. L&#8217;impact est donc non-négligeable dès que l&#8217;on arrive sur des grosses volumétries.</p>
<p>&nbsp;</p>
<h1>Usage et limitation :</h1>
<p>Le chiffrement par Always Encrypted doit être considéré uniquement pour des colonnes stockant des informations très sensibles. Si les informations très sensibles n&#8217;ont pas été très bien identifiées par le métier, Microsoft propose de l&#8217;outillage et de la méthodologie afin de les identifier. cela peut se faire soit dans le Portail Azure, soit dans SSMS (<a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/sql-data-discovery-and-classification?view=sql-server-ver16&amp;tabs=t-sql">https://learn.microsoft.com/en-us/sql/relational-databases/security/sql-data-discovery-and-classification?view=sql-server-ver16&amp;tabs=t-sql</a> ).</p>
<p>Une fois les colonnes identifiées, il faut également comprendre que leur chiffrement va impacteur leur usage. Dans le meilleur des cas (avec un chiffrement déterministe), on ne pourra faire que des recherches d&#8217;égalité dans nos requêtes ( SELECT =  / IN  / GROUP BY / DISTINCT). On ne peut pas non plus comparer les valeurs non-chiffrées et chiffrées directement. Il faut impérativement utiliser la <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/develop-using-always-encrypted-with-net-framework-data-provider?view=sql-server-ver16">paramétrisation</a>.</p>
<p>La liste des limitations étant longue, il vaut mieux se reporter à la documentation pour vérifier si l&#8217;usage envisagé est compatible :  <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver16&amp;source=docs#limitations">https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver16&amp;source=docs#limitations</a></p>
<h1>Séparation des rôles lors de la mise en œuvre :</h1>
<p>Le but du chiffrement Always Encrypted est déporter les activités de chiffrements et de déchiffrements au driver SQL. Cela implique donc une responsabilité sur la détention de la paire de clés (CEK) : qui doit les posséder ? les mettre en oeuvre ? Microsoft répond à cela en considérant qu&#8217;il y a deux fonctions différentes :</p>
<p>Le <strong>DBA</strong> qui gère les métadonnées des clés dans la base de données : c&#8217;est à dire qu&#8217;il va voir des informations dans certaines DMV relatives à la gestion de ces clés, y compris l&#8217;emplacement de ces clés (magasin de certificat ou bien URL Azure Key Vault), mais il ne verra pas la CMK directement.</p>
<p>Le &#8220;<strong>Security Administrator</strong>&#8221; aura pour rôle de générer la CMK et de les placer dans le magasin de certificat ou l&#8217;Azure Key Vault, mais il n&#8217;aura pas forcément besoin d&#8217;un accès direct à la base de données. Il aura aussi potentiellement à appliquer une rotation des clés si cela fait partie de la stratégie d&#8217;entreprise ou bien si une risque de vol de la CMK a eu lieu.</p>
<p>Cette séparation n&#8217;est pas indispensable, mais elle représente l&#8217;aboutissement du besoin de cacher les données à des utilisateurs non-autorisés, surtout si le rôle de DBA est délégué à un prestataire ou à un opérateur de cloud par exemple.</p>
<h1>Mise en oeuvre d&#8217;Always Encrypted :</h1>
<p>Always Encrypted peut être mis en œuvre par deux outils  :</p>
<ul>
<li>SQL Server Management Studio</li>
<li>PowerShell</li>
</ul>
<p>SSMS encapsule beaucoup les tâches et ne laisse pas beaucoup de marge de manœuvre pour le nommage des clés en particulier. L&#8217;utilisation de PowerShell est donc recommandée.</p>
<p>Par ailleurs, il y a deux cadres d&#8217;utilisation à considérer à notre époque : le composant applicatif est hébergé sur un serveur On-Prem (et dispose donc d&#8217;un magasin de certificats ) ou bien il est hébergé dans le Cloud et voudra accéder à l&#8217;Azure Key Vault.</p>
<h2>Dans le cadre Azure (avec le Key Vault) :</h2>
<p>Deux modules PowerShell sont nécessaire pour travailler sur ces tâches : SqlServer et Az.</p>
<pre class="brush: plain; title: ; notranslate">
Install-Module SqlServer -allowclobber
Install-Module Az
</pre>
<p>On peut ensuite créer la CMK. Si on veut travailler avec Azure et son Key vault, on peut utiliser le script ci-dessous :</p>
<pre class="brush: plain; title: ; notranslate">
$subscriptionId = &quot;xxxxxxxxxxxxxxxxxxxxxxxxx&quot; #Id de la souscription Azure
$TenantId = &quot;xxxxxxxxxxxxxxxxxxxxxxxxx&quot; #Id du Tenant Azure Active Directory (Entra ID désormais)
$resourceGroup = &quot;Lab1_RG&quot; #Resource Group de l'Azure Key Vault
$keyVaultName = &quot;VDE-KeyVault&quot; #Nom de l'Azure Key Vault
$keyVaultKeyName = &quot;AdventureWorks-CEK&quot;
Import-Module &quot;Az&quot;
Connect-AzAccount -Tenant $TenantId

# Pour se placer dans le contexte de la souscription souhaitée
Set-AzContext -SubscriptionId $subscriptionId

# Récupération du nom du key vault name
Get-AzKeyVault-VaultName $keyVaultName-ResourceGroupName $resourceGroup

# Création de la clé
$keyVaultKey = Add-AzKeyVaultKey -VaultName $keyVaultName -Name $keyVaultKeyName -Destination &quot;Software&quot;
$keyVaultKey
</pre>
<p>On peut désormais voir la clé dans le Key Vault Azure :</p>
<p><img loading="lazy" decoding="async" class="wp-image-10350 alignleft" src="https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault1-300x161.png" alt="" width="809" height="434" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault1-300x161.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault1-768x412.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault1.png 878w" sizes="auto, (max-width: 809px) 100vw, 809px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>On peut voit le détail de la clé, son URL (Key Identifier) et ses permissions ensuite :</p>
<p><img loading="lazy" decoding="async" class="wp-image-10351 alignleft" src="https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault2-187x300.png" alt="" width="512" height="821" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault2-187x300.png 187w, https://blog.capdata.fr/wp-content/uploads/2023/11/KeyVault2.png 617w" sizes="auto, (max-width: 512px) 100vw, 512px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>L&#8217;URL ici visible sera à renseigner dans nos applications cloud. On autorisera l&#8217;applicatif à utiliser le rôle  &#8220;Key Vault Crypto Service Encryption User&#8221;.</p>
<p>Il nous reste à l&#8217;enregistrer dans notre base de données. On peut le faire en PowerShell ou bien en SQL. C&#8217;est ici que l&#8217;on peut dessiner la frontière entre le rôle de &#8220;dba&#8221; et celle de &#8220;security administrator&#8221;. Le &#8220;security administrator&#8221; peut juste donner l&#8217;URL de la clé au dba pour qu&#8217;il l&#8217;intègre à la base  :</p>
<pre class="brush: plain; title: ; notranslate">

USE [AdventureWorks]
GO
CREATE COLUMN MASTER KEY [AdventureWorks-CMK-SQL-Object]
WITH
(
KEY_STORE_PROVIDER_NAME = N'AZURE_KEY_VAULT',
KEY_PATH = N'https://vde-keyvault.vault.azure.net/keys/AdventureWorks-CMK/9112fb34e3a84051a6ee490bb110a259'
)
GO

</pre>
<p>Ou bien en PowerShell (attention c&#8217;est plus long, mais on part du principe que cela fait suite au script PowerShell précédent ) :</p>
<pre class="brush: plain; title: ; notranslate">

Import-Module SqlServer

$connectionString = &quot;Data Source=LAB1SQL1;Initial Catalog=AdventureWorks;Integrated Security=True;Connect Timeout=30;Trust Server Certificate=True;Application Name=`&quot;Script PowerShell de déploiement AlwaysEncrypted`&quot;&quot;
$database = Get-SqlDatabase -ConnectionString $connectionString
$database# Récupération des paramètres de la CMK stockée dans le Key Vault :
$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyURL $keyVaultKey.Key.Kid
# Déclaration de la CMK dans SQL Server
$cmkName = &quot;AdventureWorks-CMK-SQL-Object&quot;
New-SqlColumnMasterKey -Name $cmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings
</pre>
<p>On peut ensuite vérifier que notre CMK est bien déclarée dans SQL Server avec la requête suivante :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10357" src="https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cmk-300x31.png" alt="" width="1306" height="135" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cmk-300x31.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cmk-1024x107.png 1024w" sizes="auto, (max-width: 1306px) 100vw, 1306px" /></p>
<p>On peut ensuite crée la CEK qui nous servira à chiffrer les données dans les colonnes :</p>
<pre class="brush: plain; title: ; notranslate">
$cekName = &quot;AdventureWorks-Person-emailaddress-CEK&quot;
New-SqlColumnEncryptionKey -Name $cekName -InputObject $database -ColumnMasterKey $cmkName
</pre>
<p>Là aussi, on peut vérifier la création de la CEK en SQL :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10358" src="https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cek1-300x56.png" alt="" width="680" height="127" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cek1-300x56.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/alwaysencrypted_cek1.png 691w" sizes="auto, (max-width: 680px) 100vw, 680px" /></p>
<p>Une fois la CEK présente, on peut l&#8217;utiliser pour chiffrer nos colonnes. Imaginons que l&#8217;on souhaite chiffrer la colonne EmailAddress de la table Person.EmailAddress : il faudra choisir entre deux type de chiffrement déterministe (Deterministic) ou aléatoire (Randomized). Déterministe signifie que deux chaines de caractères identiques renverront la même valeur une fois chiffrée, là où aléatoire renverra deux valeurs différentes. L&#8217;avantage de déterministe est ainsi de pouvoir faire des indexations et des jointures, contrairement au chiffrement aléatoire. Par contre, pour un attaquant, le fait d&#8217;avoir du déterministe (des valeurs qui se répètent dans la table) est une information déjà intéressante.</p>
<pre class="brush: plain; title: ; notranslate">

# Création des instructions de chiffrements : elles peuvent être multiples.
$ces = @()
$ces += New-SqlColumnEncryptionSettings -ColumnName &quot;Person.EmailAddress.Employees.SSN&quot; -EncryptionType &quot;Deterministic&quot; -EncryptionKey $cekName
Set-SqlColumnEncryption -InputObject $database -ColumnEncryptionSettings $ces -LogFileDirectory .

</pre>
<p>Désormais, la lecture des données sans les privilèges d&#8217;accès à la clé dans le Key Vault présentera les données ainsi :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10359" src="https://blog.capdata.fr/wp-content/uploads/2023/11/data_read_without_access-300x108.png" alt="" width="822" height="296" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/data_read_without_access-300x108.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/data_read_without_access.png 960w" sizes="auto, (max-width: 822px) 100vw, 822px" /></p>
<p>SSMS permet d&#8217;accéder à des données chiffrées avec Always Encrypted, même si la clé est dans un Azure Key Vault (on aura une pop-up d&#8217;authentification, comme avec la commande PowerShell <em>AzConnect</em>), pour cela il suffit juste de cliquer sur &#8220;options&#8221; dans la fenêtre d&#8217;ouverture de session et d&#8217;aller cocher &#8220;enable Always Encrypted (column encryption)&#8221; :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10361" src="https://blog.capdata.fr/wp-content/uploads/2023/11/ssms-275x300.png" alt="" width="503" height="549" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/ssms-275x300.png 275w, https://blog.capdata.fr/wp-content/uploads/2023/11/ssms.png 477w" sizes="auto, (max-width: 503px) 100vw, 503px" /> <img loading="lazy" decoding="async" class="alignnone wp-image-10363" src="https://blog.capdata.fr/wp-content/uploads/2023/11/Azure_auth-231x300.png" alt="" width="423" height="549" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/Azure_auth-231x300.png 231w, https://blog.capdata.fr/wp-content/uploads/2023/11/Azure_auth.png 568w" sizes="auto, (max-width: 423px) 100vw, 423px" /></p>
<p>Après cela, les données sont lisibles.</p>
<p>&nbsp;</p>
<h2>Dans le cadre On-Prem (avec le magasin de certificat Windows) :</h2>
<p>Imaginons désormais que notre composant applicatif soit hébergé sur un serveur Windows et ne souhaite pas utiliser l&#8217;Azure Key Vault. La seule solution aujourd&#8217;hui possible est le magasin de certificat Windows. On peut créer un certificat auto-signer</p>
<pre class="brush: plain; title: ; notranslate">
$cert = New-SelfSignedCertificate -Subject &quot;VDE-CMK-WindowsStore&quot; -CertStoreLocation Cert:CurrentUser\My -KeyExportPolicy Exportable -Type DocumentEncryptionCert -KeyUsage DataEncipherment -KeySpec KeyExchange
</pre>
<p>On pourra voir le certificat dans la branche &#8220;personal&#8221; de CurrentUser (Start/run/mmc.exe , on ajoute le composant &#8220;certificats&#8221; pour l&#8217;utilisateur courant).</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10354" src="https://blog.capdata.fr/wp-content/uploads/2023/11/always-encrypted_cert-300x96.png" alt="" width="663" height="212" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/always-encrypted_cert-300x96.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/always-encrypted_cert-1024x329.png 1024w, https://blog.capdata.fr/wp-content/uploads/2023/11/always-encrypted_cert-768x247.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/always-encrypted_cert.png 1105w" sizes="auto, (max-width: 663px) 100vw, 663px" /></p>
<p>Attention à bien générer le certificat sur une machine autre que celle SQL Server, afin de bien séparer les rôles : la machine SQL Server ne doit pas disposer de la CMK.</p>
<p>Après, de la même manière qu&#8217;avec l&#8217;Azure Key Vault, on peut créer la CEK :</p>
<pre class="brush: plain; title: ; notranslate">
$cekName = &quot;AdventureWorks-Person-emailaddress2-CEK&quot;
$cmkName = &quot;AdventureWorks-CMK-SQL-Object2&quot;
$connectionString = &quot;Data Source=LAB1SQL1;Initial Catalog=AdventureWorks;Integrated Security=True;Connect Timeout=30;Trust Server Certificate=True;Application Name=`&quot;Script PowerShell de déploiement AlwaysEncrypted`&quot;&quot;&amp;amp;amp;lt;/div&amp;amp;amp;gt;&amp;amp;amp;lt;div&amp;amp;amp;gt;$database = Get-SqlDatabase -ConnectionString $connectionString
New-SqlColumnEncryptionKey -Name $cekName -InputObject $database -ColumnMasterKey $cmkName
</pre>
<p>&nbsp;</p>
<div></div>
<div>Puis on peut chiffrer notre colonne en utilisant cette CEK  :</div>
<div>
<pre class="brush: plain; title: ; notranslate">
$ces = @()
$ces += New-SqlColumnEncryptionSettings -ColumnName &quot;Person.EmailAddress2.EmailAddress&quot; -EncryptionType deterministic -EncryptionKey $cekName
Set-SqlColumnEncryption  -InputObject $database -ColumnEncryptionSettings $ces -LogFileDirectory .
</pre>
<p>Si jamais on voulait retirer le chiffrement sur une colonne :</p>
<pre class="brush: plain; title: ; notranslate">
$ces = @()
$ces += New-SqlColumnEncryptionSettings -ColumnName &quot;Person.EmailAddress2.EmailAddress&quot; -EncryptionType PlainText
Set-SqlColumnEncryption -InputObject $database -ColumnEncryptionSettings $ces -LogFileDirectory .
</pre>
<p>Il est à noter qu&#8217;il est possible d&#8217;exporter le certificat dans la MMC des certificats Windows pour ensuite l&#8217;importer dans son Azure Key Vault. L&#8217;inverse n&#8217;est pas possible.</p>
<p>&nbsp;</p>
<h1>Considérations applicatives :</h1>
</div>
<p>Comme on l&#8217;a expliqué lors de ces deux épisodes, le bon fonctionnement applicatif repose sur deux points :</p>
<ul>
<li>La chaine de connexion spécifie &#8220;<code class="lang-cs" data-author-content="string connectionString = &quot;Data Source=server63; Initial Catalog=Clinic; Integrated Security=true; Column Encryption Setting=enabled&quot;; SqlConnection connection = new SqlConnection(connectionString); "><span class="hljs-string">Column Encryption Setting=enabled</span></code>&#8220;</li>
<li>Le compte d&#8217;exécution de l&#8217;applicatif dispose d&#8217;un accès à la Column Master Key.</li>
</ul>
<p>Dans le cas de l&#8217;utilisation d&#8217;un applicatif déployé nativement dans le cloud, les stratégies dites RBAC (Role-Based Access Control) sont à privilégier.<a href="https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli"> La documentation Microsoft</a> donne détaille davantage que cet article les éléments à considérer.</p>
<p>Si notre applicatif est plus classique, et est déployé sur un serveur Windows, on s&#8217;intéressera à son compte d&#8217;exécution pour utiliser le magasin de certificat approprié. Dans le cas de Linux, cela pourra être avec le driver JDBC et le Java KeyStore (<a href="https://learn.microsoft.com/en-us/sql/connect/jdbc/using-always-encrypted-with-the-jdbc-driver?view=sql-server-ver16">https://learn.microsoft.com/en-us/sql/connect/jdbc/using-always-encrypted-with-the-jdbc-driver?view=sql-server-ver16</a> )</p>
<p>&nbsp;</p>
<p>Les dernières versions de SQL Server (que ça soit dans Azure en PaaS ou bien On-Prem / IaaS) proposent cependant ce qu&#8217;ils appellent les Secure Enclaves, pour lever certaines limitations de développement d&#8217;Always Encrypted ! Peut-être l&#8217;objet d&#8217;un nouvel épisode ?</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/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/" rel="bookmark" title="4 octobre 2023">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/" rel="bookmark" title="16 novembre 2023">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/" rel="bookmark" title="20 mars 2024">Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" rel="bookmark" title="13 mars 2024">Le chiffrement Oracle : native network encryption</a> (Emmanuel RAMI) [Oracle]</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.341 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%3D10334&#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%3D10334&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%203%20%3A%20Always%20Encrypted" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%203%20%3A%20Always%20Encrypted&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10334" 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/le-chiffrement-et-sql-server-episode-3-always-encrypted/">Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</title>
		<link>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/</link>
					<comments>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/#respond</comments>
		
		<dc:creator><![CDATA[Capdata team]]></dc:creator>
		<pubDate>Thu, 16 Nov 2023 14:53:11 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#tde]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[chiffrement]]></category>
		<category><![CDATA[sécurité]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10315</guid>

					<description><![CDATA[<p>Dans un premier épisode (https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted ) nous avons eu l&#8217;occasion de voir les deux grandes approches du chiffrement dans SQL Server, sous un angle théorique. Maintenant que l&#8217;on a couvert les concepts derrière le chiffrement &#8220;at rest&#8221; de SQL Server,&#8230; <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/" 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/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10315&#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%3D10315&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%202%20%3A%20Mise%20en%20oeuvre%20de%20TDE" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%202%20%3A%20Mise%20en%20oeuvre%20de%20TDE&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10315" 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 un premier épisode (<a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted">https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted</a> ) nous avons eu l&#8217;occasion de voir les deux grandes approches du chiffrement dans SQL Server, sous un angle théorique.</p>
<p>Maintenant que l&#8217;on a couvert les concepts derrière le chiffrement &#8220;at rest&#8221; de SQL Server, voici comment le mettre en œuvre concrètement.</p>
<h2> Concepts de Transparent Data Encryption</h2>
<p>On notera que cela a été maquetté en SQL Server 2022, mais que mis à part la sauvegarde (cf. Episode 1), les concepts sont les mêmes.</p>
<p><img loading="lazy" decoding="async" class=" wp-image-10316 aligncenter" src="https://blog.capdata.fr/wp-content/uploads/2023/11/TDE_architecture-278x300.png" alt="" width="560" height="604" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/TDE_architecture-278x300.png 278w, https://blog.capdata.fr/wp-content/uploads/2023/11/TDE_architecture-950x1024.png 950w, https://blog.capdata.fr/wp-content/uploads/2023/11/TDE_architecture-768x827.png 768w, https://blog.capdata.fr/wp-content/uploads/2023/11/TDE_architecture.png 1008w" sizes="auto, (max-width: 560px) 100vw, 560px" /></p>
<p>&nbsp;</p>
<h2> Créer les clés de chiffrement</h2>
<p>Le fonctionnement est donc le suivant :</p>
<p>La clé de chiffrement par Windows à SQL Server va permettre de créer la Database Master Key de la base Master. Celle-ci servira à signer le certificat de la base Master. Ce certificat stockera les clés asymétriques utilisés pour chiffrer les bases de données utilisateurs.</p>
<p>Windows fourni une <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/sql-server-and-database-encryption-keys-database-engine?view=sql-server-ver16">Service Master Key</a> au premier démarrage de SQL Server après son installation. Celle-ci n&#8217;est visible et exploitable que par le compte de service SQL Server. Elle peut être identifiée ainsi :</p>
<pre class="brush: plain; title: ; notranslate">

use master
go
select * from sys.symmetric_keys ;

</pre>
<p>Elle porte le nom de ##MS_ServiceMasterKey##. Par mesure de sécurité, on va <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/back-up-the-service-master-key?view=sql-server-ver16">sauvegarder cette SMK</a> :</p>
<pre class="brush: plain; title: ; notranslate">

BACKUP SERVICE MASTER KEY TO FILE = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup\Service_Master_Key.smk' ENCRYPTION BY PASSWORD = 'HugEEEEEP@$$word4SMK!' ;
GO

</pre>
<p>On peut donc désormais créer le certificat dans la base master . Par défaut, celui-ci a une durée de vie d&#8217;un an. Cela peut potentiellement alourdir la maintenance, car il faudra déchiffrer la base, créer un nouveau certificat et re-chiffrer la base. Dans l&#8217;exemple ci-dessous, nous partons sur une durée de vie&#8230; &#8220;lointaine&#8221; :</p>
<pre class="brush: plain; title: ; notranslate">
Use master
go
CREATE CERTIFICATE MyServerMasterCertForTDE WITH SUBJECT = 'DEK Certificate used for TDE keys' ,EXPIRY_DATE = '2100-12-31';
GO

</pre>
<p>On peut la vérifier avec la commande suivante :</p>
<pre class="brush: plain; title: ; notranslate">
select * from sys.certificates where SUBJECT = 'DEK Certificate used for TDE keys';
</pre>
<p>De la même manière que l&#8217;on a sauvegardé la Service Master Key, on va sauvegarder ce certificat : il nous servira si l&#8217;on souhaite restaurer la base sur un autre serveur ou bien pour des solutions de haute disponibilité :</p>
<pre class="brush: plain; title: ; notranslate">
BACKUP CERTIFICATE MyServerMasterCertForTDE TO FILE = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup\Master_Certificate_for_TDE.crt' 
WITH PRIVATE KEY (FILE = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup\Master_Certificate_for_TDE_PrivateKey' , 
ENCRYPTION BY PASSWORD = 'HugEEEEEP@$$word4Cert!');
</pre>
<p>Maintenant que l&#8217;on a ce certificat, on peut enfin créer les clés de chiffrements TDE pour la base métier que l&#8217;on souhaite chiffrer :</p>
<pre class="brush: plain; title: ; notranslate">
Use AdventureWorks
GO
CREATE DATABASE ENCRYPTION KEY WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE MyServerMasterCertForTDE ; 
GO
</pre>
<p>Egalement, on peut vérifier la présence de notre paire de clés avec :</p>
<pre class="brush: plain; title: ; notranslate">
Use AdventureWorks
GO
select * from sys.key_encryptions
</pre>
<p>Deux lignes doivent apparaitre avec le key_id 101.</p>
<p>Il est possible que l&#8217;avertissement suivant apparaisse :</p>
<pre class="brush: plain; title: ; notranslate">
Warning: The certificate used for encrypting the database encryption key has not been backed up. You should immediately back up the certificate and the private key
associated with the certificate. If the certificate ever becomes unavailable or if you must restore or attach the database on another server,
you must have backups of both the certificate and the private key or you will not be able to open the database.
</pre>
<p>C&#8217;est parce que vous avez oublié de réaliser une sauvegarde du certificat. Cela peut être vérifié aisément :</p>
<pre class="brush: plain; title: ; notranslate">

Use AdventureWorks
GO
SELECT pvt_key_last_backup_date,
Db_name(dek.database_id) AS encrypteddatabase,
c.name AS Certificate_Name
FROM master.sys.certificates c
INNER JOIN sys.dm_database_encryption_keys dek ON c.thumbprint = dek.encryptor_thumbprint;

</pre>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10320" src="https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check1-300x92.png" alt="" width="701" height="215" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check1-300x92.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check1.png 748w" sizes="auto, (max-width: 701px) 100vw, 701px" /></p>
<p>&nbsp;</p>
<p>On voit donc le certificat associé à la paire de clé ainsi que la date de la dernière sauvegarde.</p>
<h2> Activation de TDE pour la base</h2>
<p>Maintenant que les pré-requis cryptographiques sont présents, on peut activer enfin TDE :</p>
<pre class="brush: plain; title: ; notranslate">
Use AdventureWorks
GO
ALTER DATABASE AdventureWorks SET ENCRYPTION ON ;
GO
</pre>
<p>Cela se fera en fond de tâche et n&#8217;empêchera pas l&#8217;utilisation de la base. La durée de l&#8217;opération dépendera de la volumétrie, de la version de SQL Server et des CPU présents (cf. Episode 1).</p>
<p>On peu cependant mettre en suspend si jamais cela consommait trop de ressource, à partir de SQL Server 2019 avec les commandes suivantes  :</p>
<pre class="brush: plain; title: ; notranslate">
ALTER DATABASE AdventureWorks SET ENCRYPTION SUSPEND ;
ALTER DATABASE AdventureWorks SET ENCRYPTION RESUME ;
</pre>
<p>Si jamais vous ne savez plus où vous en êtes du chiffrement de vos bases, vous pouvez également interroger vérifier ainsi :</p>
<pre class="brush: plain; title: ; notranslate">
select
db.name,
EncryptionStatus = CASE encryption_state
WHEN 0 THEN 'Pas de DEK, pas chiffrée' -- oui, alors dans les faits, si c'est le cas il n'y pas de ligne dans la table dek. Mais dans la doc...
WHEN 1 THEN 'DEK présente, pas chiffrée'
WHEN 2 THEN 'Chiffrement en cours'
WHEN 3 THEN 'Chiffrée'
WHEN 4 THEN 'Changement de clé en cours'
WHEN 5 THEN 'Déchiffrement en cours'
WHEN 6 THEN 'Changement de certificat ou clé en cours'
ELSE 'Statut inconnu'
END
from sys.dm_database_encryption_keys dek
LEFT OUTER JOIN sys.databases db ON dek.database_id = db.database_id
</pre>
<p>Cela nous renvoie ainsi le tableau suivant :</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-10323" src="https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check2-300x89.png" alt="" width="998" height="296" srcset="https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check2-300x89.png 300w, https://blog.capdata.fr/wp-content/uploads/2023/11/tde_check2-768x227.png 768w" sizes="auto, (max-width: 998px) 100vw, 998px" /></p>
<p>On voit ainsi que notre base AdventureWorks est bien chiffrée (et non pas &#8220;chiffrement en cours&#8221;). On voit également que la TempDb est chiffrée ! Eh oui : si jamais il y avait des opérations avec des tables temporaires, il faut bien entendu que les données soient chiffrée sur celle-ci. Cela veut donc dire un impact en terme de de performances pour les autres bases métier qui seraient sur le même serveur, même si elles ne sont pas chiffrées !</p>
<h2> Intégration du chiffrement TDE dans un groupe de disponibilité Always-On</h2>
<p>Maintenant, imaginons que l&#8217;on veuille mettre cette base de données dans un groupe de disponibilité AlwaysOn : comment faire ? Il suffit simplement de restaurer le certificat et sa clé privé sur le(s) réplicat(s) secondaire(s) pour pouvoir accéder aux données. C&#8217;est également la même procédure si on veut juste restaurer la base sur un autre serveur.</p>
<p>Dans un premier temps, on va s&#8217;assurer que le serveur qui nous servira de réplica secondaire a bien une Master Key  :</p>
<pre class="brush: plain; title: ; notranslate">
Use master
GO
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'HugEEEEEP@$$word!'
GO
</pre>
<p>On copie la paire de fichiers sauvegardés avec le certificat plus tôt sur le serveur destination (par exemple dans le répertoire de backup, mais attention à ne pas les y laisser trainer trop longtemps) :</p>
<pre class="brush: plain; title: ; notranslate">
Use master
go
CREATE CERTIFICATE MyServerMasterCertForTDE FROM FILE = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup\Master_Certificate_for_TDE.crt'
WITH PRIVATE KEY (FILE = 'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Backup\Master_Certificate_for_TDE_PrivateKey',
DECRYPTION BY PASSWORD = 'HugEEEEEP@$$word4Cert!') ;
GO
</pre>
<p>A partir de maintenant, une base de données chiffrée avec une paire de clé issue de ce certificat sera lisible sur ce serveur !</p>
<p>On peut donc par exemple l&#8217;intégrer à un groupe de disponibilité déjà présent. Sur le réplica principal (LAB1SQL1 et LAB1SQL2 est le secondaire):</p>
<pre class="brush: plain; title: ; notranslate">
USE [master]
GO
ALTER AVAILABILITY GROUP [LAB1AG]
MODIFY REPLICA ON N'LAB1SQL2' WITH (SEEDING_MODE = AUTOMATIC)
GO

ALTER AVAILABILITY GROUP [LAB1AG]
ADD DATABASE [AdventureWorks];
GO
</pre>
<p>puis sur le réplica secondaire :</p>
<pre class="brush: plain; title: ; notranslate">
ALTER AVAILABILITY GROUP [LAB1AG] GRANT CREATE ANY DATABASE;
GO
</pre>
<p>Avec l&#8217;Automatic Seeding, la base sera crée et dupliquée chiffrée, sans même passer par l&#8217;étape de backup.</p>
<h2> Remarques sur les différences de taille de base</h2>
<p>Dans l&#8217;épisode 1, on parlait de l&#8217;impact du chiffrement sur la taille des backups. Ici, dans notre exemple avec SQL Server 2022 et la base AdventureWorks, la base de données sans TDE fait 54 688 Ko, tandis que la version avec TDE fait 52 292 Ko : autant dire que c&#8217;est négligeable et que l&#8217;écart est lié à la fragmentation après le déchiffrement.</p>
<p><strong>Une fois de plus et comme expliqué dans l&#8217;épisode 1, pensez à mettre les sauvegardes de vos clés et certificats dans un endroit sécurisé !! En l&#8217;absence de votre certificat, vous serez incapable de restaurer vos données !</strong></p>
<p>Dans le prochain épisode, nous verrons concrètement comment déployer une stratégie de chiffrement avec Always Encrypted : stay tuned !<br />
<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/" rel="bookmark" title="4 octobre 2023">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/" rel="bookmark" title="29 novembre 2023">Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</a> (Capdata team) [AzureSQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" rel="bookmark" title="13 mars 2024">Le chiffrement Oracle : native network encryption</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/retrouver-la-requete-a-lorigine-dune-erreur-8623-the-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/" rel="bookmark" title="27 mai 2016">Retrouver la requête à l&#8217;origine d&#8217;une erreur 8623 &#8220;The query processor ran out of internal resources and could not produce a query plan&#8221;</a> (David Baffaleuf) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/aws-backup-restore-sql-server-rds-iaas-ec2-et-vice-versa/" rel="bookmark" title="25 février 2019">AWS : Backup Restore SQL Server RDS vers une EC2 ou On-Premise et vice versa !</a> (Emmanuel RAMI) [AWSSQL Server]</li>
</ul>
<p><!-- Similar Posts took 2.111 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%3D10315&#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%3D10315&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%202%20%3A%20Mise%20en%20oeuvre%20de%20TDE" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%202%20%3A%20Mise%20en%20oeuvre%20de%20TDE&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10315" 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/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>OrioleDB : la promesse d&#8217;un No-Vacuum</title>
		<link>https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/</link>
					<comments>https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Wed, 11 Oct 2023 10:27:27 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[maintenance]]></category>
		<category><![CDATA[vacuum]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10248</guid>

					<description><![CDATA[<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>
]]></description>
										<content:encoded><![CDATA[<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/postgresql-optimiser-vos-operations-vacuum-et-analyze/" rel="bookmark" title="26 février 2025">PostgreSQL : optimiser vos opérations vacuum et analyze !</a> (Emmanuel RAMI) [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/la-montee-de-version-en-zero-downtime-merci-la-replication/" rel="bookmark" title="19 décembre 2024">La montée de version en zero-downtime : merci la réplication !</a> (Sarah FAVEERE) [PostgreSQL]</li>
<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/cybertec-migrator-une-alternative-a-ora2pg/" rel="bookmark" title="10 mai 2023">Cybertec Migrator : une alternative à ora2pg ?</a> (Sarah FAVEERE) [OraclePostgreSQL]</li>
</ul>
<p><!-- Similar Posts took 4.476 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>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/orioledb-la-promesse-dun-no-vacuum/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</title>
		<link>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/</link>
					<comments>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/#respond</comments>
		
		<dc:creator><![CDATA[Capdata team]]></dc:creator>
		<pubDate>Wed, 04 Oct 2023 08:55:12 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#tde]]></category>
		<category><![CDATA[always encrypted]]></category>
		<category><![CDATA[chiffrement]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10260</guid>

					<description><![CDATA[<p>Le chiffrement sous SQL Server n&#8217;est pas nouveau et il existe sous plusieurs formes, selon les versions. Les deux principales implémentations sont TDE (Transparent Data Encryption, qui est un chiffrement &#8220;at rest&#8221;) et Always Encrypted (chiffrement par colonne). La première&#8230; <a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/" 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/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10260&#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%3D10260&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%201%20%3A%20Transparent%20Data%20Encryption%20%28TDE%29%20vs%20Always%20Encrypted" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%201%20%3A%20Transparent%20Data%20Encryption%20%28TDE%29%20vs%20Always%20Encrypted&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10260" 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>Le chiffrement sous SQL Server n&#8217;est pas nouveau et il existe sous plusieurs formes, selon les versions. Les deux principales implémentations sont TDE (Transparent Data Encryption, qui est un chiffrement &#8220;at rest&#8221;) et Always Encrypted (chiffrement par colonne).</p>
<p>La première implémentation du chiffrement est arrivé en 2008 est TDE. C&#8217;est une solution assez simple à mettre en oeuvre et qui a le mérite de proposer un élément de réponse aux considérations RGPD.</p>
<p>Always Encrypted est une solution plus complexe à déployer qui nécessitera une évaluation des données à risque, table par table, colonne par colonne. L&#8217;architecture applicative devra également être considérée.</p>
<h1>SQL Server TDE &#8211; chiffrement des données &#8220;at rest&#8221; :</h1>
<p>TDE est apparu avec SQL Server 2008 et uniquement en édition Enterprise, ce qui était un frein à son déploiement pour beaucoup d&#8217;entreprise. Depuis SQL Server 2019, cette fonctionnalité est disponible dès l&#8217;édition Standard. Dans Azure avec SQL Database, tous les niveaux de service propose le chiffrement TDE et il est même activé par défaut.</p>
<p>L&#8217;idée avec TDE est d&#8217;appliquer un chiffrement, base de données par base de données, sur les fichiers de données et de log transactionnel. Si un backup de la base est réalisé, celui-ci ne sera qu&#8217;une retranscription des fichiers de la base de données et donc sera également chiffré.</p>
<p>Le chiffrement est basé soit sur un certificat ou une paire de clés asymétriques généré par SQL Server, soit par le module EKM. Dans les deux cas, une attention très importante devra être apportée à la sécurisation des clés de chiffrements. En effet, imaginons la destruction du serveur SQL pour une raison quelconque. Si on veut restaurer une sauvegarde d&#8217;une base de données sur un autre serveur, il faudra posséder la clé de chiffrement et la déployer sur ce nouveau serveur. En l&#8217;absence de cette clé, l&#8217;utilisation du backup sera impossible.</p>
<p><img loading="lazy" decoding="async" class=" wp-image-10301 aligncenter" src="https://blog.capdata.fr/wp-content/uploads/2023/10/TDE-254x300.png" alt="" width="487" height="575" srcset="https://blog.capdata.fr/wp-content/uploads/2023/10/TDE-254x300.png 254w, https://blog.capdata.fr/wp-content/uploads/2023/10/TDE.png 507w" sizes="auto, (max-width: 487px) 100vw, 487px" /></p>
<p>&nbsp;</p>
<p>Par ailleurs, dans le cas d&#8217;une configuration en mirroring ou en AlwaysOn, il faudra copier la clé de chiffrement sur tous les réplicas afin que SQL Server puisse manipuler les données chiffrées.</p>
<p>En conséquence, on comprend bien que TDE a pour objectif de protéger les donnée contre un vol des fichiers de SQL Server : .mdf, .ndf, .ldf et les backups de ces bases de données.</p>
<p>L&#8217;accès dans le moteur de SQL Server étant &#8220;transparent&#8221; comme son nom l&#8217;indique, si l&#8217;attaque se fait à travers l&#8217;applicatif ou le moteur de SQL Server (parce qu&#8217;un compte sur le serveur ou dans le réseau a des privilèges suffisants de connexion à la base), cela n&#8217;apportera pas de sécurité supplémentaire.</p>
<p>En contrepartie de cette fonctionnalité, SQL Server <strong>va consommer un peu plus de ressource CPU</strong> pour manipuler ces données. Si vous comptez déployer TDE sur un serveur qui connait déjà une forte charge CPU : attention, donc !</p>
<p>Avant SQL Server 2016, la compression des backups est également problématique<strong>.</strong> En effet, la compression apporte un caractère aléatoire aux données dans les pages et donc diminue les répétitions de valeurs qui permettent à la compression d&#8217;apporter du gain. Ainsi donc, la compression des backups n&#8217;apportera quasiment aucun gain d&#8217;espace disque tout en consommant des cycles de CPU. Pour peu que vous ayez pris l&#8217;habitude de sauvegarder votre backup à un endroit avec un espace assez limité, l&#8217;activation de TDE va saturer ce volume et faire échouer vos backups.</p>
<p>A partir de SQL Server 2016, si on ajoute l&#8217;option MAXTRANSFERSIZE = 65536 (ou supérieur) à la commande de backup, l&#8217;algorithme de backup et chiffrement sera différent, permettant de gagner de l&#8217;espace disque.</p>
<p>A partir de SQL Server 2019 CU5, il n&#8217;y a plus besoin d&#8217;ajouter le paramètre MAXTRANSFERSIZE, du moment qu&#8217;il y a l&#8217;option COMPRESS d&#8217;activé, cela mettra la valeur à 128K et permettra automatiquement de gagner l&#8217;espace lié à la compression.</p>
<p>&nbsp;</p>
<p>L&#8217;activation de TDE est relativement souple : elle va se faire en fond de tâche avec une consommation modérée de ressources, surtout depuis SQL 2016 où ce travail est dévolu au processeur par un accélérateur dédié (Intel AES-NI). Mais si votre serveur est déjà assez chargé niveau CPU, peut-être que cela peut valoir le coup de créer une base de test avec une copie d&#8217;une table et lancez le TDE uniquement sur cette base pour en voir l&#8217;impact.</p>
<p>Enfin, pensez bien à sauvegarder votre certificat TDE ou votre clé asymétrique de déchiffrement et mettez la à un voir deux endroits sécurisés. Peut-être une clé USB dans une coffre-fort ? Un NAS avec une solution de chiffrement tierce ? Dans tous les cas, il faut bien comprendre que si vous perdez cette clé et que vous rencontrez un problème sur votre serveur chiffré par TDE , vos données sont en grave péril.</p>
<p>&nbsp;</p>
<h1>SQL Server Always Encrypted &#8211; le chiffrement de bout en bout, par colonne :</h1>
<p>La solution Always Encrypted est plus récente que TDE et est arrivé avec SQL Server 2016. Il n&#8217;est pas nécessaire d&#8217;être en édition Enterprise : c&#8217;est accessible dès l&#8217;édition Standard.</p>
<p>Contrairement à TDE qui va chiffrer l&#8217;intégralité de la base de données, Always Encrypted est une option qui s&#8217;active colonne par colonne, table par table. Le chiffrement/déchiffrement n&#8217;est pas non plus &#8220;interne&#8221; au serveur SQL, mais géré par l&#8217;application. Le but est de proposer que l&#8217;administrateur de l&#8217;infrastructure (l&#8217;administrateur système ou de base de données) ne soit pas en mesure de voir ces données. Seul l&#8217;applicatif sera capable de déchiffrer les données.</p>
<p>En effet : la philosophie de cette technologie est vraiment dans son nom &#8220;Always Encrypted&#8221; : toujours chiffré.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class=" wp-image-10271 aligncenter" src="https://blog.capdata.fr/wp-content/uploads/2023/09/always-encrypted-300x105.jpg" alt="" width="563" height="197" srcset="https://blog.capdata.fr/wp-content/uploads/2023/09/always-encrypted-300x105.jpg 300w, https://blog.capdata.fr/wp-content/uploads/2023/09/always-encrypted.jpg 601w" sizes="auto, (max-width: 563px) 100vw, 563px" /></p>
<p>Dans ce schéma, on voit TDE entre la base et le stockage &amp; backup : cela ne veut pas dire qu&#8217;AlwaysOn nécessite TDE. Mais l&#8217;addition de ces deux technologies permet un chiffrement exhaustif.</p>
<p>On voit également que le chiffrement se fait entre la base de données et l&#8217;appli. Cela veut dire qu&#8217;il faut penser au driver SQL qui sera utilisé pour se connecter à la base. Si à sa sortie Always Encrypted exigeait un driver ADO .Net , on peut désormais avoir recours à plus d&#8217;options. On peut ainsi utiliser Always Encrypted avec des drivers pour :</p>
<ul>
<li>.NET Core &amp; .NET Framework</li>
<li>JDBC</li>
<li>ODBC</li>
<li>PHP</li>
</ul>
<p>Les pages de documentations pour l&#8217;utilisation de ces drivers dans le cadre Always Encrypted est disponible à partir d&#8217;ici : <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-client-development?view=sql-server-ver16">https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-client-development?view=sql-server-ver16 </a></p>
<p>Always Encrypted va nécessiter la génération de deux type de clés : les Column Encryption Keys (qui vont servir à chiffrer les données des colonnes) et les Column Master Keys (qui protège les Column Encryption Keys). Comme avec TDE on peut utiliser le magasin de certificat de Windows, Azure Key Vault, ou bien un HSM pour les stocker. Cependant, le but est de sortir cette Column Master Key du serveur SQL pour bien séparer les rôles : SQL Server stock de la donnée chiffrée, et le driver compatible Always Encrypted se charge de chiffrer/déchiffrer les données.</p>
<p>Une Column Encryption Key peut chiffrer une ou plusieurs colonnes. Une base de données peut gérer plusieurs Column Encryptions Keys pour gérer des cas métiers avec différents profils d&#8217;utilisateurs par exemple. L&#8217;algorithme utilisé est un AES-256.</p>
<p>Il y aura donc deux tâches qui devront être considéré spécifiquement : le provisionnements des clés dans un key store approprié (avec le Magasin de Certificats Windows ou Azure Key Vault dans la plupart des cas), et la rotation des clés (parce qu&#8217;elles ont été compromises ou bien parce que le certificat est arrivé à expiration après une certaine date). On peut réaliser ces tâches avec SSMS ou en PowerShell.</p>
<p>La bonne pratique est de générer ces clés sur une autre machine, comme par exemple le serveur d&#8217;application qui a de toute façon vocation à les avoir pour chiffrer/déchiffrer les données.</p>
<p>SQL Server lui ne possédera que les méta-données de ces clés, qui lui serviront à identifier les colonnes comme recevant ce chiffrement.</p>
<p>Une fois les clés générées, on peut procéder au chiffrement des colonnes. Cependant, il y a un choix à faire : le chiffrement peut-être déterministe ou bien aléatoire.</p>
<p>Un chiffrement déterministe renvoie la même valeur chiffrée pour la même donnée en entrée. Cela peut permettre de deviner certaines informations, mais cela permet aussi de réaliser des opérations comme une recherche d&#8217;égalité ( &#8220;ma_colonne = ma valeur&#8221; et donc jointures), des agrégats et des indexations.</p>
<p>Un chiffrement aléatoire ne renvoie pas les mêmes valeurs pour des données identiques en entrées. C&#8217;est plus sécurisé mais toutes ces opérations deviennent impossibles.</p>
<p>Quand cette déclaration est faite dans le modèle de données, et pour peu que l&#8217;on est accès à la Column Master Key ainsi que bien sûr un accès à la base, on peut lire et écrire des données chiffrées. Il faut également spécifier dans la chaine de connexion qu&#8217;Always Encrypted est activé (en ADO. Net : <em>Column Encryption Setting=enabled</em> , en JDBC : <em>encrypt=true</em> &#8230;).</p>
<p>Il existe quelques limitations à l&#8217;usage d&#8217;Always Encrypted. On notera parmi celles-ci les suivantes :</p>
<ul>
<li>Impossible d&#8217;utiliser des colonnes de type xml, timestamp, rowversion, image, ntext, text, sql_variant, hierarchyid, geography, geometry, alias et les user-defined types.</li>
<li>Les chaines de caractères doivent avoir un collation de type _BIN2 pour faire du déterministe.</li>
<li>Le Full Text est bien entendu impossible.</li>
<li>Pas de contrainte de valeur par défaut.</li>
<li>Pas de Change Data Capture.</li>
<li>Pas de Dynamic Data Masking.</li>
<li>La réplication logique est impossible (AlwaysOn est possible).</li>
<li>Les requêtes distribuées sont impossibles.</li>
<li>Les requêtes cross-databases en joignant sur les colonnes (même avec chiffrement déterministe) sont impossibles.</li>
</ul>
<p>Plus d&#8217;information : <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver16">https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver16 </a></p>
<p>&nbsp;</p>
<h1>TDE vs Always Encrypted &#8211; synthèse des différences :</h1>
<p>On peut voir une approche complémentaire de ces deux solutions technologique. Une première phase étant TDE qui est une protection &#8220;basique&#8221;. Always Encrypted vient ajouter une vraie notion de chiffrement applicative mais qui va nécessiter un travail de réflexion sur les colonnes et les tables à chiffrer. Et sans parler du fait que, si il y a une interface utilisateur devant l&#8217;application, il faudrait également que ce flux soit chiffré avec SSL/TLS.</p>
<p>On peut cependant faire la synthèse suivante :</p>
<table width="850">
<tbody>
<tr>
<td width="277"></td>
<td width="261">Always Encrypted</td>
<td width="312">TDE</td>
</tr>
<tr>
<td width="277">Version</td>
<td width="261">A partir de 2016 + Azure SQL Database</td>
<td width="312">A partir de 2008 + Azure SQL Database</td>
</tr>
<tr>
<td width="277">Edition</td>
<td width="261">Standard</td>
<td width="312">Enterprise avant 2019</td>
</tr>
<tr>
<td width="277">Chiffrement des données &#8220;at rest&#8221;</td>
<td width="261">Oui</td>
<td width="312">Oui</td>
</tr>
<tr>
<td>Chiffrement des données en utilisation</td>
<td width="261">Oui</td>
<td width="312">Non</td>
</tr>
<tr>
<td>Données visibles par le DBA ?</td>
<td width="261">Non</td>
<td width="312">Oui</td>
</tr>
<tr>
<td>Données chiffrées par</td>
<td width="261">Côté client (driver de l&#8217;applicatif)</td>
<td width="312">SQL Server</td>
</tr>
<tr>
<td>Fonctionne niveau :</td>
<td width="261">Colonne</td>
<td width="312">Base de données</td>
</tr>
<tr>
<td>Impact applicatif</td>
<td width="261">Oui (limitations sur colonne chiffrée)</td>
<td width="312">Non</td>
</tr>
<tr>
<td>Protège les clés de chiffrement</td>
<td width="261">Oui</td>
<td width="312">Non</td>
</tr>
<tr>
<td>Nécessite un driver adapté</td>
<td width="261">Oui</td>
<td width="312">Non</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>Nous reviendrons prochainement sur la mise en oeuvre technique de TDE et d&#8217;Always Encrypted ! Et n&#8217;hésitez pas à poser vos questions dans les commentaires.<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-3-always-encrypted/" rel="bookmark" title="29 novembre 2023">Le chiffrement et SQL Server &#8211; Episode 3 : Always Encrypted</a> (Capdata team) [AzureSQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-2-mise-en-oeuvre-de-tde/" rel="bookmark" title="16 novembre 2023">Le chiffrement et SQL Server – Episode 2 : Mise en oeuvre de TDE</a> (Capdata team) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-transparent-data-encryption-sur-oracle-19c/" rel="bookmark" title="20 mars 2024">Le chiffrement Oracle : Transparent Data Encryption sur Oracle 19c</a> (Emmanuel RAMI) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/quelles-solutions-de-chiffrement-de-donnees-pour-mysql-mariadb/" rel="bookmark" title="18 mars 2022">Quelles solutions de chiffrement de données pour MySQL / MariaDB</a> (David Baffaleuf) [MySQL]</li>
<li><a href="https://blog.capdata.fr/index.php/le-chiffrement-oracle-native-network-encryption/" rel="bookmark" title="13 mars 2024">Le chiffrement Oracle : native network encryption</a> (Emmanuel RAMI) [Oracle]</li>
</ul>
<p><!-- Similar Posts took 4.254 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%3D10260&#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%3D10260&#038;title=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%201%20%3A%20Transparent%20Data%20Encryption%20%28TDE%29%20vs%20Always%20Encrypted" 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=Le%20chiffrement%20et%20SQL%20Server%20%E2%80%93%20Episode%201%20%3A%20Transparent%20Data%20Encryption%20%28TDE%29%20vs%20Always%20Encrypted&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10260" 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/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/">Le chiffrement et SQL Server &#8211; Episode 1 : Transparent Data Encryption (TDE) vs Always Encrypted</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/le-chiffrement-et-sql-server-episode-1-transparent-data-encryption-tde-vs-always-encrypted/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Fatal : too many connections</title>
		<link>https://blog.capdata.fr/index.php/fatal-too-many-connections/</link>
					<comments>https://blog.capdata.fr/index.php/fatal-too-many-connections/#respond</comments>
		
		<dc:creator><![CDATA[Sarah FAVEERE]]></dc:creator>
		<pubDate>Thu, 28 Sep 2023 15:30:03 +0000</pubDate>
				<category><![CDATA[Non classé]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10267</guid>

					<description><![CDATA[<p>PostgreSQL est largement utilisé pour sa fiabilité, sa robustesse et ses performances élevées. Cependant, même dans un système aussi puissant que PostgreSQL, des erreurs peuvent survenir. L&#8217;une de ces erreurs courantes est &#8220;Fatal : Too Many Connections.&#8221; Dans cet article,&#8230; <a href="https://blog.capdata.fr/index.php/fatal-too-many-connections/" 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/fatal-too-many-connections/">Fatal : too many connections</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10267&#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%3D10267&#038;title=Fatal%20%3A%20too%20many%20connections" 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=Fatal%20%3A%20too%20many%20connections&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10267" 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>PostgreSQL est largement utilisé pour sa fiabilité, sa robustesse et ses performances élevées. Cependant, même dans un système aussi puissant que PostgreSQL, des erreurs peuvent survenir. L&#8217;une de ces erreurs courantes est &#8220;Fatal : Too Many Connections.&#8221; Dans cet article, nous allons explorer en détail cette erreur, comprendre pourquoi elle se produit et découvrir comment la résoudre.</p>
<h2>Explication de l&#8217;erreur &#8220;Fatal : Too Many Connections&#8221;</h2>
<p>L&#8217;erreur &#8220;Fatal : Too Many Connections&#8221; se produit lorsque le nombre de connexions simultanées à la base de données atteint la limite définie dans la configuration de PostgreSQL. Chaque fois qu&#8217;une application ou un utilisateur se connecte à PostgreSQL, une nouvelle connexion est établie, et PostgreSQL alloue des ressources pour gérer cette connexion. Si le nombre de connexions actives dépasse la limite prédéfinie, PostgreSQL génère cette erreur &#8220;Fatal : Too Many Connections.&#8221;</p>
<p>Plusieurs facteurs peuvent contribuer à cette erreur, notamment :</p>
<p>Limite de Connexion : PostgreSQL a une limite de connexion maximale définie dans son fichier de configuration (postgresql.conf). Par défaut, cette limite est souvent assez basse pour éviter de surcharger le système, mais elle peut être ajustée en fonction des besoins de votre application.</p>
<p>Fuites de Connexion : Des fuites de connexion peuvent survenir lorsque des connexions sont ouvertes mais jamais fermées. Cela peut se produire en raison de bogues dans le code de l&#8217;application ou d&#8217;une gestion incorrecte des connexions.</p>
<p>Trafic Élevé : Si votre application connaît un pic soudain de trafic et que le nombre de connexions simultanées dépasse la limite, cette erreur peut se produire.</p>
<p>Sessions Inactives : Des sessions inactives ou bloquées peuvent également contribuer à la saturation des connexions.</p>
<h2>Résoudre l&#8217;erreur &#8220;Fatal : Too Many Connections&#8221;</h2>
<p>Maintenant que nous avons une compréhension de pourquoi cette erreur se produit, voyons comment la résoudre.</p>
<h3>1. Augmenter la limite de connexions</h3>
<p>La première étape consiste à augmenter la limite de connexions dans le fichier de configuration postgresql.conf. Recherchez la ligne suivante et modifiez-la selon vos besoins :</p>
<pre class="brush: sql; title: ; notranslate"> max_connections = 100 </pre>
<p>Assurez-vous de redémarrer PostgreSQL après avoir apporté cette modification.</p>
<h3>2. Vérifier les fuites de connexion</h3>
<p>Il est essentiel de rechercher et de corriger les fuites de connexion dans votre application. Assurez-vous que chaque connexion ouverte est correctement fermée après son utilisation. Utilisez des outils de profilage et de débogage pour identifier les zones de code qui pourraient causer des fuites.</p>
<h3>3. Optimiser les requêtes</h3>
<p>Si votre application génère un grand nombre de connexions en raison d&#8217;un trafic élevé, il peut être nécessaire d&#8217;optimiser vos requêtes SQL pour réduire la charge sur la base de données. Assurez-vous que vos requêtes sont efficaces et utilisez des indexes pour accélérer les opérations de recherche.</p>
<h3>4. Surveillance et nettoyage</h3>
<p>Mettez en place une surveillance continue de l&#8217;état de votre base de données. Identifiez les sessions inactives ou bloquées et mettez en œuvre des mécanismes pour les libérer automatiquement après un certain temps.</p>
<h3>5. Équilibrage de charge</h3>
<p>Si votre application est soumise à une charge importante, envisagez d&#8217;utiliser un équilibrage de charge pour répartir le trafic sur plusieurs serveurs PostgreSQL. Cela peut aider à réduire la pression sur chaque serveur individuel.</p>
<p>En conclusion, l&#8217;erreur &#8220;Fatal : Too Many Connections&#8221; dans PostgreSQL peut être gérée en ajustant la limite de connexions, en traquant et en corrigeant les fuites de connexion, en optimisant les requêtes, en surveillant activement la base de données et en envisageant l&#8217;équilibrage de charge lorsque cela est nécessaire. Une compréhension approfondie de la façon dont les connexions fonctionnent dans PostgreSQL est essentielle pour gérer efficacement cette erreur et maintenir la stabilité de votre application.<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/" rel="bookmark" title="18 septembre 2023">[ERROR] Error reading packet from server: Lost connection to MySQL server during query</a> (David Baffaleuf) [Codes erreurMySQL]</li>
<li><a href="https://blog.capdata.fr/index.php/ora-01555-et-query-duration0/" rel="bookmark" title="17 juin 2015">ORA-01555 et Query Duration=0</a> (Benjamin VESAN) [Oracle]</li>
<li><a href="https://blog.capdata.fr/index.php/mythe-sql-server-associe-un-thread-a-chaque-connexion/" rel="bookmark" title="1 août 2010">Mythe: SQL Server associe un thread à chaque connexion</a> (David Baffaleuf) [SQL Server]</li>
<li><a href="https://blog.capdata.fr/index.php/linux-out-of-memory-killer-oom-killer-pour-un-serveur-base-de-donnees-postgresql/" rel="bookmark" title="1 février 2021">Linux Out-Of-Memory Killer (OOM-Killer) pour un serveur base de données PostgreSQL</a> (Emmanuel RAMI) [Non classé]</li>
<li><a href="https://blog.capdata.fr/index.php/texte-sql-tronque-dans-les-vues-performance_schema-en-5-6-et-5-7-il-faut-migrer/" rel="bookmark" title="22 août 2019">Texte SQL tronqué dans les vues performance_schema en 5.6 et 5.7, il faut migrer !</a> (David Baffaleuf) [MySQL]</li>
</ul>
<p><!-- Similar Posts took 3.081 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%3D10267&#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%3D10267&#038;title=Fatal%20%3A%20too%20many%20connections" 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=Fatal%20%3A%20too%20many%20connections&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10267" 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/fatal-too-many-connections/">Fatal : too many connections</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/fatal-too-many-connections/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[ERROR] Error reading packet from server: Lost connection to MySQL server during query</title>
		<link>https://blog.capdata.fr/index.php/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/</link>
					<comments>https://blog.capdata.fr/index.php/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/#respond</comments>
		
		<dc:creator><![CDATA[David Baffaleuf]]></dc:creator>
		<pubDate>Mon, 18 Sep 2023 10:32:29 +0000</pubDate>
				<category><![CDATA[Codes erreur]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[connexion]]></category>
		<category><![CDATA[timeout]]></category>
		<guid isPermaLink="false">https://blog.capdata.fr/?p=10238</guid>

					<description><![CDATA[<p>Salut à toutes et tous ! Au sein de notre pôle d&#8217;administration à distance AllDB, nous traitons depuis presque 20 ans des incidents de base de données. Notre base d&#8217;incidents renferme un peu plus d&#8217;1 million de problèmes divers et&#8230; <a href="https://blog.capdata.fr/index.php/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/" 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/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/">[ERROR] Error reading packet from server: Lost connection to MySQL server during query</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></description>
										<content:encoded><![CDATA[<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%3D10238&#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%3D10238&#038;title=%5BERROR%5D%20Error%20reading%20packet%20from%20server%3A%20Lost%20connection%20to%20MySQL%20server%20during%20query" 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=%5BERROR%5D%20Error%20reading%20packet%20from%20server%3A%20Lost%20connection%20to%20MySQL%20server%20during%20query&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10238" 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>Salut à toutes et tous !</p>
<p>Au sein de notre pôle d&#8217;administration à distance <a href="https://capdata.fr/dbadistance">AllDB</a>, nous traitons depuis presque 20 ans des incidents de base de données. Notre base d&#8217;incidents renferme un peu plus d&#8217;1 million de problèmes divers et variés sur 5 à 6 moteurs de base de données différents. Chaque incident s&#8217;étant produit une à plusieurs fois, pour vous donner un ordre d&#8217;idée rien que sur la partie MySQL / MariaDB qui est la moins représentée dans le volume d&#8217;incidents générés, nous avons recensé 1.2 milliard d&#8217;occurrences de codes erreurs depuis 2005, sur un échantillon de versions allant de la 3.23 à la 8.0 aujourd&#8217;hui.   </p>
<p>Ce qu&#8217;on voulait vous proposer c&#8217;est une petite vulgarisation des codes erreurs principaux que nous avons rencontré au fil des années, de ce qu&#8217;ils signifient et comment corriger ces erreurs. Cette série va commencer avec MySQL/MariaDB mais se poursuivra avec les autres moteurs SQL Server, Oracle, PostgreSQL, etc&#8230; </p>
<h2>Comprendre l&#8217;erreur Lost connection to MySQL server during query</h2>
<p>Cette erreur générique indique simplement que le serveur MySQL ne perçoit plus de signe du programme à l&#8217;autre bout du canal d&#8217;une connexion cliente. Le problème peut survenir à toutes les étapes du cycle de vie d&#8217;une connexion : à son établissement , au milieu de l&#8217;exécution d&#8217;une requête, lorsque la connexion est inactive depuis un certain temps, lorsque l&#8217;instance est saturée et ne prend plus de connexions, ou que le serveur est injoignable (stoppé : <em>MySQL Server has gone away</em>), ou enfin lors d&#8217;un transfert de données massives entre le client et le serveur. </p>
<p>Elle sera souvent accompagnée d&#8217;un autre message qui précise l&#8217;origine du problème, par exemple ici il s&#8217;agit de la connexion entre un slave de réplication et le master:</p>
<pre class="brush: bash; title: ; notranslate">
121010 17:32:18 [Note] Start binlog_dump to slave_server(2), pos(mysql-bin.000020, 4137470)
121010 17:34:02 [ERROR] Error reading packet from server: Lost connection to MySQL server during query ( server_errno=2013)
121010 17:34:02 [Note] Slave I_O thread: Failed reading log event, reconnecting to retry, log  mysql-bin.000012  at position 107
</pre>
<p>Dans ce cas il se peut que le réseau sont coupé entre les 2 machines ou services, ou que le master soit lui-même stoppé. Cela vient du fonctionnement de type pull de la réplication où le <a href="https://dev.mysql.com/doc/refman/8.0/en/replica-io-thread-states.html">slave IO</a> streame les positions ou GTIDs depuis les binlogs du master vers des relaylogs locaux. Il est connecté en permanence via une connexion classique et est donc sujets aux mêmes problèmes que les connections clientes. </p>
<h2>Résoudre l&#8217;erreur Lost connection to MySQL server during query</h2>
<p>Il faut commencer par identifier la cause, qui on l&#8217;a vu peut être multiple:</p>
<ol>
<li>Le serveur MySQL est-il démarré et en capacité de répondre à une demande de connexion ? utiliser mysqlping par exemple pour valider que le serveur est disponible</li>
<li>Le réseau est-il coupé entre le client et le serveur : firewall, panne réseau, etc&#8230; Utiliser un client telnet par exemple avec le nom de l&#8217;hôte et le port (par défaut TCP3306) pour valider que le flux est ouvert.</li>
<li>Si vous étiez en train de transférer des données (mysqldump, mysqlpump ou LOAD DATA INFILE) il se peut qu&#8217;une partie des données dépasse le payload réseau client ou serveur, vérifier vos valeurs de max_allowed_packet des 2 côtés. </li>
<li>Côté métrologie, vérifier la valeur de &#8216;Aborted Connects&#8217; à intervalles réguliers pour évaluer s&#8217;il s&#8217;agit d&#8217;un problème isolé ou d&#8217;un blocage global.
<pre class="brush: sql; title: ; notranslate">
MariaDB [(none)]&gt; show global status like 'Aborted_connects' ;
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Aborted_connects | 2245  |
+------------------+-------+
1 row in set (0.000 sec)
</pre>
</li>
</ol>
<p>Pour retrouver toute la série sur les codes erreurs, c&#8217;est par ici : <a href="https://blog.capdata.fr/index.php/category/codes-erreur/">https://blog.capdata.fr/index.php/category/codes-erreur/</a> !<br />
<strong>Continuez votre lecture sur le blog :</strong></p>
<ul class="similar-posts">
<li><a href="https://blog.capdata.fr/index.php/replication-mysql-resynchronisation-dun-slave-mysql/" rel="bookmark" title="8 février 2019">Réplication MySQL : Resynchronisation d&#8217;un Slave MySQL</a> (Capdata team) [MySQL]</li>
<li><a href="https://blog.capdata.fr/index.php/que-faire-des-warning-aborted-connection-avec-mysql/" rel="bookmark" title="25 mars 2010">Que faire des &#8220;[Warning] Aborted connection&#8221; avec MySQL ?</a> (Benjamin VESAN) [MySQL]</li>
<li><a href="https://blog.capdata.fr/index.php/deplacer-le-repertoire-de-donnees-datadir-mysql-vers-un-nouvel-emplacement-sur-debian/" rel="bookmark" title="26 février 2019">Déplacer le répertoire de données (datadir) MySQL vers un nouvel emplacement sur Debian</a> (Capdata team) [MySQL]</li>
<li><a href="https://blog.capdata.fr/index.php/migrer-dun-cluster-galera-mariadb-10-3-vers-mariadb-10-5-avec-la-replication-logique/" rel="bookmark" title="25 février 2022">Migrer d&#8217;un cluster Galera MariaDB 10.3 vers MariaDB 10.5 avec la réplication logique</a> (David Baffaleuf) [ContainerMySQLNon classé]</li>
<li><a href="https://blog.capdata.fr/index.php/nouveautes-mysql-8-0-variables-persistes/" rel="bookmark" title="2 avril 2019">Nouveautés MySQL 8.0 : Variables persistés</a> (Capdata team) [MySQL]</li>
</ul>
<p><!-- Similar Posts took 4.092 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%3D10238&#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%3D10238&#038;title=%5BERROR%5D%20Error%20reading%20packet%20from%20server%3A%20Lost%20connection%20to%20MySQL%20server%20during%20query" 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=%5BERROR%5D%20Error%20reading%20packet%20from%20server%3A%20Lost%20connection%20to%20MySQL%20server%20during%20query&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2F%3Fp%3D10238" 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/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/">[ERROR] Error reading packet from server: Lost connection to MySQL server during query</a> est apparu en premier sur <a rel="nofollow" href="https://blog.capdata.fr">Capdata TECH BLOG</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.capdata.fr/index.php/error-error-reading-packet-from-server-lost-connection-to-mysql-server-during-query/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
