<?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>Oracle - Concepts et Exemples &#187; Traductions</title>
	<atom:link href="https://blog.developpez.com/pachot/category/traductions/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/pachot</link>
	<description>Les fonctionalités et concepts d&#039;Oracle à partir de traductions et de démos</description>
	<lastBuildDate>Sun, 03 Apr 2016 20:36:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Histogrammes et bind variables, par Jonathan Lewis</title>
		<link>https://blog.developpez.com/pachot/jl_philosophy_1/</link>
		<comments>https://blog.developpez.com/pachot/jl_philosophy_1/#comments</comments>
		<pubDate>Fri, 22 Jun 2012 20:46:19 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;un article de Jonathan Lewis publié sur son blog. L&#8217;article original en anglais se trouve ici Sur Oracle, certains concepts sont si fondamentaux qu&#8217;on doit toujours les avoir en tête à chaque fois qu&#8217;on veut &#8230; <a href="https://blog.developpez.com/pachot/jl_philosophy_1/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Cet article est la traduction d&rsquo;un article de Jonathan Lewis publié sur son blog. L&rsquo;article original en anglais se trouve <a href="http://jonathanlewis.wordpress.com/2009/05/06/philosophy-1/">ici</a></ins></p></blockquote>
<p>Sur Oracle, certains concepts sont si fondamentaux qu&rsquo;on doit toujours les avoir en tête à chaque fois qu&rsquo;on veut étudier un problème de performance. Et voici l&rsquo;un d&rsquo;eux:</p>
<p><strong>Les histogrammes et les <em>bind variables</em> existent pour des raison diamétralement opposées: sans y prêter garde, ils ne fonctionneront pas bien ensemble.</strong><br />
</p>
<p>Vous utilisez des <em>bind variables</em> parce que vous voulez que tout le monde partage le même plan d&rsquo;exécution pour une requête SQL qui va être utilisé fréquemment. Tout le monde va faire la même charge de travail avec (charge normalement faible). Son plan d&rsquo;exécution sera optimal pour tout le monde. Et vous ne voulez pas ré-optimiser cette requête à chaque fois, car cette réoptimisation utiliserait à elle seule plus de ressources qu&rsquo;il n&rsquo;en faut ensuite pour l&rsquo;exécuter.</p>
<p>En général on utilise beaucoup les <em>bind variables</em> lorsqu&rsquo;on est en transactionnel (<em>OLTP</em>) &#8211; sauf quelques cas particuliers où on préférera des valeurs littérales.<br />
</p>
<p>Et vous créez des histogrammes pour des requêtes qui, mêmes si elles sont similaires, vont faire un travail très différent les unes des autres. Elles ont besoin de plans d&rsquo;exécution différents. Et de toute façon le travail d&rsquo;optimisation est négligeable par rapport au travail d&rsquo;exécution de la requête. Alors que si on utilise un plan d&rsquo;exécution qui n&rsquo;est pas optimal, on peut se retrouver à gaspiller beaucoup de ressources.</p>
<p>En général, on a besoin des histogrammes en <em>datawarehouse</em>, en BI, où les requêtes peuvent être très grosses et coûteuses.</p>
<p>
C&rsquo;est là qu&rsquo;est la contradiction: on a une technologie qui est censée nous donner un seul plan d&rsquo;exécution partagé par tout le monde, et une autre qui elle est censée trouver pour chacun le plan qui lui convient le mieux.<br />
</p>
<p>Gardez celà en mémoire, et vous vous rappellerez qu&rsquo;il faut être très prudent lorsqu&rsquo;on met des histogrammes sur une base transactionnelle (<em>OLTP</em>) et qu&rsquo;il ne faut pas non plus transformer absolument toutes les valeurs littérales en <em>bind variables</em>.</p>
<blockquote><p><ins>A noter les commentaires de Doug Burns et Hemant K Chitale sur le fait qu&rsquo;en 10g Oracle collecte par défaut des histogrammes avec la méthode &lsquo;FOR ALL COLUMNS SIZE AUTO&rsquo; de dbms_stats.</ins></p></blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Clustering Factor, Jonathan Lewis traduit par Mohamed Houri</title>
		<link>https://blog.developpez.com/pachot/clustering_factor_jonathan_lewis_traduit/</link>
		<comments>https://blog.developpez.com/pachot/clustering_factor_jonathan_lewis_traduit/#comments</comments>
		<pubDate>Mon, 23 May 2011 22:36:01 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Mohamed Houri a traduit un chapitre de Jonathan Lewis sur le Clustering Factor qui est si important dans le coùt d&#8217;accès à une table via un index. Voici le lien de la traduction en français: http://jonathanlewis.files.wordpress.com/2011/05/le-clustering-factor.pdf Jonathan Lewis a mis &#8230; <a href="https://blog.developpez.com/pachot/clustering_factor_jonathan_lewis_traduit/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Mohamed Houri a traduit un chapitre de Jonathan Lewis sur le Clustering Factor qui est si important dans le coùt d&rsquo;accès à une table via un index.<br />
Voici le lien de la traduction en français: <a href="http://jonathanlewis.files.wordpress.com/2011/05/le-clustering-factor.pdf">http://jonathanlewis.files.wordpress.com/2011/05/le-clustering-factor.pdf</a></p>
<p>Jonathan Lewis a mis les liens de l&rsquo;article original et de la traduction sur son <a href="http://jonathanlewis.wordpress.com/2011/05/19/clustering_factor/">blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Les principes fondamentaux d&#8217;un datawarehouse &#8211; traitement batch, par Greg Rahn</title>
		<link>https://blog.developpez.com/pachot/gr_batch/</link>
		<comments>https://blog.developpez.com/pachot/gr_batch/#comments</comments>
		<pubDate>Mon, 18 Oct 2010 21:07:14 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Greg Rahn]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;un article de Greg Rahn publié sur son blog. L&#8217;article original en anglais est: The Core Performance Fundamentals Of Oracle Data Warehousing – Set Processing vs Row Processing. Cet article fait partie d&#8217;une série sur &#8230; <a href="https://blog.developpez.com/pachot/gr_batch/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Cet article est la traduction d&rsquo;un article de Greg Rahn publié sur son blog. L&rsquo;article original en anglais est: <a href="http://structureddata.org/2010/07/20/the-core-performance-fundamentals-of-oracle-data-warehousing-%E2%80%93-set-processing-vs-row-processing/">The Core Performance Fundamentals Of Oracle Data Warehousing – Set Processing vs Row Processing</a>. Cet article fait partie d&rsquo;une série sur les principes fondamentaux des datawarehouse, mais s&rsquo;applique à tous les traitements de type batch.<br />
</ins></p></blockquote>
<p>Durant 6 ans à faire des <i>Proof Of Concept</i> et des <i>Benchmarks</i> sur des datawarehouse pour les clients, il y a un domaine qui s&rsquo;est toujours montré problématique: les traitements par lots (<i>batch</i>). La plupart du temps, ces <i>batchs</i> prennent la forme de procédures et packages PL/SQL, qui font du chargement de donnée, de la transformation, du traitement, ou quelque chose de similaire.<br />
La raison pour laquelle c&rsquo;est souvent problématique, c&rsquo;est que les développeurs y ont codé en dur la lenteur du traitement. Je suis certain que les développeurs ne savaient pas qu&rsquo;ils faisaient cela, lorsqu&rsquo;ils ont codé leur PL/SQL, mais en tout cas, c&rsquo;est ce qui est arrivé.</p>
<p><b>Alors comment ont-ils codé &lsquo;en dur&rsquo; cette lenteur en PL/SQL ?</b><br />
<span id="more-45"></span></p>
<p>En général, c&rsquo;est parce que au lieu d&rsquo;avoir lu les spécifications métiers en étudiant l&rsquo;état &lsquo;avant&rsquo; et &lsquo;après&rsquo; des données, puis d&rsquo;en avoir déterminé la manière la plus efficace de faire ces modifications de données, les développeurs PL/SQL ont fait une traduction littérale de chaque règle/exigence lues dans les specs, une par une. On devine cela lorsqu&rsquo;on voit du code qui parcours un curseur ligne par ligne, mais aussi dans du PL/SQL qui ne fait qu&rsquo;exécuter une série de requêtes SQL souvent mal concues.</p>
<p><b>Etude de cas de la lenteur codée &lsquo;en dur&rsquo;</b></p>
<p><em>La suite est basée sur une histoire vécue. Seuls les noms ont été modifiés pour protéger les innocents.</em></p>
<p>Voici un extrait de pseudo-code que j&rsquo;ai rencontré dans un POC:</p>
<pre>
{truncate de toutes les tables intermédiaires}
insert into temp1 select * from t1 where create_date = hier;
insert into temp1 select * from t2 where create_date = hier;
insert into temp1 select * from t3 where create_date = hier;
insert into temp2 select * from temp1 where {conditions};
insert into target_table select * from temp2;
pour chacune des  20 colonnes
loop
  update target_table t
    set t.column_name =
      (select column_name
       from t4
       where t.id=t4.id )
    where i.column_name is null
end loop
update target_table t set {liste de 50 columns} = select {50 colonnes} from t5 where t.id=t5.id;
</pre>
<p>Je m&rsquo;arrête ici car la suite risquerait de vous faire pleurer.<br />
J&rsquo;hésite un peu à poser la question, mais n&rsquo;est-ce pas évident ce qu&rsquo;il y a de pas correct dans ce traitement ?<br />
Voici les principales raisons que je vois à cette inefficacité:</p>
<ul>
<li>Pourquoi insérer toutes les données dans temp1, et ne les filtrer que lorsqu&rsquo;on remplit temp2 ? Si vous n&rsquo;avez jamais entendu le conseil <strong>&lsquo;filtrez les données au plus tôt&rsquo;</strong> alors vous avez des devoirs à faire.</li>
<li>Pourquoi publier dans la table cible, puis faire 20 update d&rsquo;une seule colonne chacun, suivi d&rsquo;un update de 50 colonnes ? Meilleure question: pourquoi faire des update de masse ? <strong>Les update (et delete) de masse sont à proscrire</strong>, il faut les éviter à tout prix.</li>
</ul>
<p>Et alors, comme beaucoup de client qui font un POC (preuve de concept) sur une machine Exadata, ils n&rsquo;ont pas du tout envie de modifier leur code, ils veulent juste voir quelle performance la plateforme Exadata peur leur délivrer. Et pour leur plus grand bonheur, ils ont réduit la durée du traitement de 2.5 jours (batch de week-end qui commence le vendredi après midi et n&rsquo;est toujours pas terminé le lundi matin) à 10 heures, gagnant ainsi plus de 2 jours. Maintenant, le batch peut planter et ils auront le temps de le relancer avant l&rsquo;ouverture aux utilisateurs du lundi matin. Eh, je suppose que je serais aussi très exité si je gagnait 24 heures sur un traitement de 38 heures. Mais ce n&rsquo;est pas le cas lorsque je suis un ingénieur de performance de base de données qui sait qu&rsquo;il y a encore plus de performance à gagner.</p>
<p>Comme je n&rsquo;était pas satisfait par cela, j&rsquo;ai pris sur moi de prouver que de re-designer le code peut avoir un retour très intéressant sur la plateforme Exadata, et j&rsquo;ai codé complètement un flux de donnée qui travaille de manière ensembliste, avec juste une poignée de requêtes SQL (et pas de PL/SQL). Le résultat: le traitement d&rsquo;une semaine complète de données (plusieurs centaines de millions de lignes) prend maintenant 12 minutes. C&rsquo;est exact: 7 jours de données nettoyées, transformées, enrichies et publiées en 12 minutes seulement.</p>
<p>Lorsque j&rsquo;ai annoncé la nouvelle au client, qu&rsquo;il était possible de charger une semaine de données en seulement 12 minutes, ils étaient très excités, c&rsquo;est le moins que l&rsquo;on puisse dire. En fait, il y en a un qui a même dit, un peu hors du contexte, que maintenant une seule journée de données pourrait être chargées en 2 minutes, et que cela donnerait un autre niveau de fraîcheur  aux données, ce qui permettrait au métier de prendre des décision meilleures et plus rapides, grâce aux données actualisées plus souvent. Ma réponse: CQFD ! Le client voit maintenant ce qu&rsquo;il est possible de faire avec Exadata, et qui était impossible avant.</p>
<p><b>C&rsquo;est pas obligatoire, mais c&rsquo;est souhaitable</b></p>
<p>Je ne vais pas changer ma casquette d&rsquo;ingénieur base de données en vendeur de produit, pas maintenant, et probablement jamais, mais c&rsquo;est le réalité telle qu&rsquo;elle existe. Les boites informatiques ont démarré avec des faibles volumes de données, et une logique de programmation sur des petits volumes, et cela a fonctionné un certain temps. Pourquoi ? Parce que le traitement peu efficace d&rsquo;un faible volume de données est seulement <strong>un peu inefficace</strong> Mais la même logique de développement sur un gros volume de données devient alors <strong>très inefficace</strong>.<br />
C&rsquo;est pourquoi j&rsquo;ai déjà dit: pour exploiter à fond la plateforme Exadata (ou n&rsquo;importe quelle plateforme actuelle), il faut changer le code. Ne me faites pas dire ce que je n&rsquo;ai pas dit: je ne dis pas qu&rsquo;il est nécessaire revoir le code de l&rsquo;application pour Exadata. Je dis que vous pouvez choisir de revoir le code pour Exadata parce que l&rsquo;application n&rsquo;est tout simplement pas conçue pour profiter du traitement massivement parallèle que Exadata permet. Il est temps que les décisions de design de l&rsquo;application soient basées sur les technologies d&rsquo;aujourd&rsquo;hui, et non sur celles sur lesquelles l&rsquo;application a été faite. Avance rapide vers aujourd&rsquo;hui.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Redo privé et Undo en mémoire (In Memory Undo), par Jonathan Lewis</title>
		<link>https://blog.developpez.com/pachot/jl_private_redo/</link>
		<comments>https://blog.developpez.com/pachot/jl_private_redo/#comments</comments>
		<pubDate>Tue, 28 Dec 2010 16:17:13 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ceci est une traduction de d&#8217;un ancien post de Jonathan Lewis sur forums.oracle.com, référencé récemment sur son blog. Il décrit le fonctionnement de la journalisation en mémoire (IMU &#8211; In Memory Undo), une optimisation introduite en 10g qui utilise des &#8230; <a href="https://blog.developpez.com/pachot/jl_private_redo/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Ceci est une traduction de d&rsquo;un ancien post de Jonathan Lewis sur <a href="http://forums.oracle.com/forums/message.jspa?messageID=3974313#3974313">forums.oracle.com</a>, référencé récemment sur son <a href="http://jonathanlewis.wordpress.com/2010/12/23/private-redo/">blog</a>. Il décrit le fonctionnement de la journalisation en mémoire (IMU &#8211; In Memory Undo), une optimisation introduite en 10g qui utilise des structures en mémoire pour diminuer la contention sur les blocs d&rsquo;undo et le redo log buffer.</ins></p></blockquote>
<p>Le contenu des blocs d&rsquo;undo et des fichiers de redo log sont quasiment les même que l&rsquo;on utilise <em>in-memory undo</em> (et les <em>private redo threads</em>) ou que l&rsquo;on utilise la journalisation &lsquo;normale&rsquo;.<br />
La principale différence se trouve dans l&rsquo;ordre où sont faites les choses.<br />
Il y a aussi, avec <em>in-memory undo</em>, une diminution du nombre de <em>redo records</em> même si le nombre de <em>change vectors</em> reste le même.</p>
<p>Voici le séquencement d&rsquo;une transaction courte avec gestion normale de la journalisation.</p>
<ul>
<li>Vous modifiez un bloc de table ou d&rsquo;index. Un vecteur de changement (<em>redo change vector</em>) est généré pour cette modification.</li>
<li>En même temps, vous devez enregistrer l&rsquo;information nécessaire pour défaire (<em>rollback</em>) de cette modification. C&rsquo;est un enregistrement d&rsquo;annulation (<em>undo record</em>) qui est généré pour décrire ce qui a été altéré.</li>
<li>Mais comme cet <em>undo record</em> est stocké dans un bloc d&rsquo;undo (rollback segment), alors un vecteur de changement <em>redo change vector</em> est généré pour décrire cette modification du bloc d&rsquo;undo</li>
<li>Oracle combine ces deux <em>redo change vector</em> (vecteurs de changement du bloc de donnée et du bloc d&rsquo;undo) dans en un enregistrement de redo (<em>redo record</em>), ce qui incrémente la statistique de session &lsquo;redo entries&rsquo;.</li>
<li>Donc pour cette modification, Oracle doit acquérir de l&rsquo;espace dans le tampon journalisation <em>redo log buffer</em> avec le latch &lsquo;redo allocation&rsquo; et y copier l&rsquo;enregistrement de redo avec le latch &lsquo;redo copy&rsquo;</li>
</ul>
<p>Si l&rsquo;on insère 10 lignes, une par une, dans une table qui a 4 indexes, alors on va générer 50 <em>redo records</em> et 50 <em>undo records</em>, et faire appel 50 fois au latches de redo: 5 <em>redo record</em> par ligne (un pour la table et un pour chaque index) pour 10 lignes.</p>
<p>Lorsque la fonctionnalité de journalisation en mémoire (<em>in-memory undo</em>) est activée, et parce que dans cet exemple il s&rsquo;agit d&rsquo;une petite transaction, voici ce qu&rsquo;il se passe:</p>
<ul>
<li>A moment où on modifie la première ligne de la table, Oracle alloue dans la shared pool son propre <strong>buffer de redo privé</strong> (appelé <em>redo strand</em>) et son propre <strong>buffer de &laquo;&nbsp;undo&nbsp;&raquo;</strong>. En fait, ce buffer de &laquo;&nbsp;undo&nbsp;&raquo; contient du redo: c&rsquo;est le redo qui décrit ce qui doit être modifié dans les bloc d&rsquo;<em>undo</em>.</li>
<li>Lors de la mise à jour de la table et des index, chaque <em>change vector</em> qui décrit la modification est écrit dans le <em>buffer de redo privé</em>.</li>
<li>En même temps, les <em>change vector</em> qui décrivent le <em>undo record</em> correspondant sont écrits dans le <strong>buffer de &laquo;&nbsp;undo&nbsp;&raquo; privé</strong>.</li>
<li>Le nombre total de <em>change vectors</em>, et leur contenu sont exactement les mêmes que pour les <em>change vectors</em> traditionnels.</li>
<li>Au commit, oracle concatène ces 2 buffers pour faire <strong>un seul <em>redo record</em></strong> et l&rsquo;écrit dans le tampon de journalisation normal (<em>redo log buffer</em>)</li>
<li>En même temps, ces 100 <em>change vectors</em> sont appliqués: 10 sur la table, 10 sur chaque index, et 50 sur les blocs d&rsquo;undo. Et en dehors de cela, tout ce qui doit se faire lors d&rsquo;un commit s&rsquo;applique aussi.</li>
<li>Le nombre de modification de blocs (&laquo;&nbsp;db block changes&nbsp;&raquo;) reste le même dans tous les cas</li>
<li>La différence la plus significative dans le volume de redo généré vient de l&rsquo;entête du <em>redo record</em> qui fait 12 octets. Avec la gestion &lsquo;in-memory&rsquo; de l&rsquo;undo il n&rsquo;y  qu&rsquo;un seul <em>redo record</em>, donc un <em>header</em> de 12 octets, alors que la méthode traditionnelle en génère 50, donc 50*12=600 octets.</li>
</ul>
<p>Il y a de nombreux détails et variations autour de ce qui se passe là. Par exemple au début et à la fin de la transaction, ou lorsque un des deux buffers est plein (puisqu&rsquo;ils ne font que 64Ko ou 128Ko) mais la description faite ci-dessus couvre les différences essentielles.</p>
<blockquote><p><ins>Question:</ins> Supposons que je démarre l&rsquo;instance et effectue quelques mises à jour. J&rsquo;ai donc un buffer privé de redo et un buffer privé de undo, créés en shared pool. Immédiatement après le système se plante et rien n&rsquo;est encore écrit dans les fichiers de redo ni dans les blocs d&rsquo;undo. Dans cette situation comment fait Oracle pour récupérer les données d&rsquo;undo ?</p></blockquote>
<p>Il y a deux chose que vous devez prendre en compte dans ma description:</p>
<ul>
<li>la précision: &lsquo;Il y a de nombreux détails et variations&rsquo;</li>
<li>la partie qui montre que les modifications faites dans les blocs tables et index est tout à la fin.</li>
</ul>
<p>Si la session a fait un commit, elle a écrit le redo privé dans le <em>redo thread</em> public, qui doit être écrit sur disque avant que le commit ne soit terminé. Donc il n&rsquo;y a rien de différent au niveau du recovery.</p>
<p>Maintenant, si la session n&rsquo;a pas encore fait de commit, alors du point de vue des autres utilisateurs, rien ne s&rsquo;est encore passé (ils ne sont censés voir que les effets des transactions commitées). Du coup, cela n&rsquo;a pas d&rsquo;importance que les redo et undo privés aient disparu.</p>
<p>Mais voici où ca devient plus complexe: Comment les autres sessions voient que vous êtes en train de modifier les mêmes blocs qu&rsquo;elles, si vous ne les mettez à jour que lorsque vous faites le commit de votre transaction ? Comment Oracle fait pour minimiser le temps que prennent toutes les modifications de blocs qui doivent être faites lors du commit ? J&rsquo;ai quelques réponses à ces questions, mais elles ne sont ni exactes, ni complètes, alors je ne préfère pas les publier.</p>
<p>Cependant, un point clé de ce mécanisme, c&rsquo;est le fait qu&rsquo;il ne s&rsquo;applique qu&rsquo;à des petites transactions. Les zones privées ne font que 64Ko ou 128Ko suivant qu&rsquo;on est en 32 ou 64 bits, et dès que la transaction devient trop grande, Oracle les écrit dans les <em>redo buffer</em> et poursuit avec le mécanisme normal. </p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Design physique d&#8217;une table pour des performances maximales, par Tom Kyte</title>
		<link>https://blog.developpez.com/pachot/tk_design_perf/</link>
		<comments>https://blog.developpez.com/pachot/tk_design_perf/#comments</comments>
		<pubDate>Thu, 30 Sep 2010 17:00:54 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Tom Kyte]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;une réponse de Tom Kyte sur son site AskTom décrivant rapidement les points à considérer lorsqu&#8217;on a une table a fort volume transactionnel et forte concurrence (L&#8217;article original en anglais se trouve ici). Question Que &#8230; <a href="https://blog.developpez.com/pachot/tk_design_perf/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Cet article est la traduction d&rsquo;une réponse de Tom Kyte sur son site AskTom décrivant rapidement les points à considérer lorsqu&rsquo;on a une table a fort volume transactionnel et forte concurrence (L&rsquo;article original en anglais se trouve <a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2683932200346712379">ici</a>).<br />
</ins></p></blockquote>
<h3>Question</h3>
<p>Que puis-je faire du point de vue du design physique pour maximiser les performances et la concurrence lorsque une table va être la cible de centaines de milliers de <i>select</i> et probablement autour de 80000 <i>insert</i>, autant d&rsquo;<i>update</i> et <i>delete</i> <b>par heure</b>, de manière transactionnels sur une base OLTP.<br />
Ces débits de <i>insert/update/delete</i> sont juste un exemple. En réalité ils seront beaucoup plus élevés, même si on ne sait pas à quel point ils seront plus élevés car nous sommes toujours en phase de design.</p>
<p>Je suis à la recherche de quelques lignes directrices que je pourrais essayer sur mon application.</p>
<h3>Réponse</h3>
<p>On pourrait écrire un livre là dessus <img src="https://blog.developpez.com/pachot/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /> Le mien est &lsquo;Expert Oracle Database Architecture&rsquo; et vous serez surement intéressé par de nombreux chapitres, plus particulièrement ceux sur les types de données, les tables et les index.</p>
<ul>
<li>Vous pourriez avoir besoin de partitionner: répartir les <i>inserts</i> sur de nombreux <i>segments</i>, afin d&rsquo;éviter des contentions sur la partie droite des <i>index</i>  (sur les dates ou les séquences par exemple)&#8230;</li>
<li>Vous pourriez avoir besoin d&rsquo;IOT (tables organisées index), plus lent pour les <i>insert</i> dans la plupart des cas, mais si vous faites des requêtes qui ramènent de nombreuses lignes qui sont arrivées dans la table à des moments différents dans le temps, l&rsquo;IOT peut permettre de regrouper (<i>cluster</i>) ces lignes afin de rendre plus efficace le fait de les récupérer ensembles.</li>
<li>Vous pourriez aussi utiliser ASSM (<i>Automatic segment space management</i>) pour améliorer la concurrence, pour éviter de chercher les bonnes valeurs de PCTUSED, FREELISTS et FREELIST GROUP (mais vous devez comprendre ce qu&rsquo;il y a de différent entre ASSM et MSSM&#8230;)</li>
<li>Vous pourriez chercher à comprendre comment les types de données sont stockés physiquement, réfléchir à PCTFREE, et comment maximiser les performances possibles sur les LOB, si vous les utilisez, etc.</li>
</ul>
<p>En bref, vous voulez comprendre comment fonctionnent les choses à un certain niveau. Le <i><a href="http://www.oracle.com/pls/db112/homepage">concepts guide</a></i> de la documentation Oracle et un bon point de départ. Si vous aimez ma manière d&rsquo;écrire, vous pouvez commencer aussi par &lsquo;Expert Oracle Database Architecture&rsquo;.</p>
<p>Vous aurez besoin de réfléchir à la concurrence, aux choses comme ASSM, le partitionnement, voire les technique de regroupement de données (<i>clustering</i>): IOT, hash/btree clusters.</p>
<p>Vous aurez besoin de réfléchir sur l&rsquo;archivage des données dans le temps.</p>
<p>Vous devrez peut-être envisager la nécessité de faire une réorganisation des tables à l&rsquo;occasion, et donc prévoir le design qui permettra de le faire: à nouveau le partitionnement.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fragmentation &#8211; Index, par Jonathan Lewis (4ème partie)</title>
		<link>https://blog.developpez.com/pachot/jl_fragmentation_4/</link>
		<comments>https://blog.developpez.com/pachot/jl_fragmentation_4/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 19:57:00 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>
		<category><![CDATA[Traductions]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ceci est une traduction de d&#8217;un post de Jonathan Lewis sur son blog &#8211; la quatrième et dernière partie d&#8217;une série de quatre sur la fragmentation (original en anglais). Il est conseillé de lire avant: Fragmentation &#8211; Introduction, Fragmentation &#8211; &#8230; <a href="https://blog.developpez.com/pachot/jl_fragmentation_4/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Ceci est une traduction de d&rsquo;un post de Jonathan Lewis sur son blog &#8211; la quatrième et dernière partie d&rsquo;une série de quatre sur la fragmentation (<a href="http://jonathanlewis.wordpress.com/2010/07/22/fragmentation-4/">original en anglais</a>). Il est conseillé de lire avant: <a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">Fragmentation &#8211; Introduction</a>, <a href="http://blog.developpez.com/pachot/p9126/auteurs/jl-fragmentation-2/">Fragmentation &#8211; Disque et Tablespace</a>, <a href="http://blog.developpez.com/pachot/p9193/auteurs/jl-fragmentation-3/">Fragmentation &#8211; Table</a></ins></p></blockquote>
<h2>Fragmentation Index</h2>
<p>La fragmentation en extents multiples et la fragmentation due à ASSM que j&rsquo;ai décrit dans la note précédente à propos des tables s&rsquo;appliquent aussi aux indexes, bien sûr, et nous importe de la même manière, c&rsquo;est à dire presque jamais. Lorsque les gens parlent de fragmentation d&rsquo;index, ils pensent en général au problème des blocs avec un faible taux de remplissage (<em>sparsely populated blocks</em>) qui est aussi un phénomène que j&rsquo;ai décrit à propos de la fragmentation des tables, mais il y a quelques différences entre une table et un index, que nous allons examiner tout de suite.<br />
Il est intéressant de considérer aussi un autre sens possible pour la fragmentation d&rsquo;un index, que nous allons aussi examiner: c&rsquo;est l&rsquo;effet de bord de la division d&rsquo;un bloc feuille (<em>leaf block splitting</em>) qui fait que des blocs qui sont logiquement à la suite se retrouvent physiquement dispersés.<br />
<span id="more-7"></span><br />
Nous allons commencer avec une suppression en masse, et étudier les mêmes cas représentatif que nous avons vu à propos des tables (c&rsquo;est à dire 20% des blocs ayant 100% de leurs ligne supprimées, et 100% des blocs ayant 20% de leur lignes supprimées). Et quand on fait ça, il faut garder à l&rsquo;esprit que la suppression dans un index est différente de la suppression dans une table, d&rsquo;où une différence de comportement dans ce qui suit.<br />
Lorsqu&rsquo;une transaction supprime une ligne d&rsquo;une table (DELETE) la ligne est réduite à un <em>stub</em> de quelques octets, avant que ne se fasse le <strong>commit</strong>, et elle peut réutiliser immédiatement l&rsquo;espace libéré dans le bloc de la table. Mais lorsqu&rsquo;une transaction supprime une ligne d&rsquo;un index, il doit laisser en place l&rsquo;entrée d&rsquo;index entière, et la marquer comme supprimée. Elle ne peut pas réutiliser l&rsquo;espace immédiatement, mais cela doit attendre que le <strong>commit</strong> soit fait.</p>
<p>Une autre différence majeure entre une table et un index est le fait que dans un index chaque entrée a sa place et doit aller au bon endroit. Ce qui fait que lorsqu&rsquo;un bloc d&rsquo;index a de l&rsquo;espace libre, sans être complètement vide, il n&rsquo;y a que les lignes qui correspondent exactement à cette partie de l&rsquo;index (<ins>à cette plage de valeurs</ins>) qui peuvent réutiliser cet espace.  </p>
<p>De plus, lorsqu&rsquo;un bloc feuille devient complètement vide, il reste toujours chaîné dans la même position de la structure de l&rsquo;index, même s&rsquo;il est aussi référencé par la <em>freelist</em>. (Mon hypothèse là dessus est qu&rsquo;il est probablement plus facile de gérer les problèmes de lecture cohérente &#8211; <em>read consistency</em> &#8211; mais cela peut aussi être lié à des problèmes de rollback et au coût de la modification de 3 pointeurs dans la structure de l&rsquo;index.) Ce qui veut dire que si on a un index qui a eu une large suppression des valeurs les plus basses, alors une requête qui demande la valeur minimum va devoir faire un range scan d&rsquo;un grand nombre de blocs vides avant de trouver le bloc feuille qui contient une donnée présente. C&rsquo;est pour cela qu&rsquo;il faut toujours penser à faire un ALTER INDEX &#8230; COALESCE sur un index lorsqu&rsquo;on supprime fréquemment les premières entrées. Et de manière plus générique, même si c&rsquo;est moins courant, lorsque on un supprime un grand nombre de valeurs consécutives n&rsquo;importe où dans l&rsquo;index.</p>
<p>Dans le cas plus général d&rsquo;un suppression en masse, on peut se retrouver avec un espace libre important dans tous les blocs feuilles et, contrairement à l&rsquo;espace libre des tables, on ne peut pas faire en sorte qu&rsquo;Oracle le réutilise en choisissant une valeur idéale pour PCTUSED puisque ce paramètre n&rsquo;a pas de sens pour un index. Donc, dans le cas des index, la question que l&rsquo;on doit se poser est: à quel point cet espace libre a un impact sur l&rsquo;application.</p>
<p>Les considérations habituelles s&rsquo;appliquent ici, bien sûr:  un plus gros volume à sauvegarder lors des <em>backup </em>et plus de blocs à garder en <em>buffer cache</em>. Mais nous devons voir si le fait d&rsquo;avoir un grand nombre de blocs feuilles faiblement remplis n&rsquo;a pas un impact plus direct et plus significatif sur les performances.<br />
La réponse est dépendante de l&rsquo;application, bien sûr. Mais en général un index est utilisé pour lister les valeurs clés et les regrouper dans un faible espace. Et en gardant cela à l&rsquo;esprit, on peut voir que le plus gros du travail de la plupart des requêtes est passé à aller voir les lignes de la table après avoir récupéré un certain nombre de valeurs clés dans l&rsquo;index. Par conséquent, le travail supplémentaire venant du fait qu&rsquo;il y a une grand quantité d&rsquo;espace libre dans les blocs feuilles de l&rsquo;index n&rsquo;est qu&rsquo;une petite fraction du travail total de la requête. Et on peut donc décider de ne pas dépenser des ressources à réorganiser les index sauf s&rsquo;ils sont vraiment très faiblement remplis. (Un index B-Arbre typique avec une arrivée aléatoire va tourner avec un taux d&rsquo;utilisation de 70%, donc 30% d&rsquo;espace libre, dans les blocs feuilles. Je ne suis pas particulièrement inquiet des performances d&rsquo;un index avant que le taux d&rsquo;utilisation ne passe en dessous de 50%, sauf si j&rsquo;ai une preuve que cet index contribue significativement au temps d&rsquo;exécution d&rsquo;un ensemble de requêtes critiques.)</p>
<p>Il y a cependant deux autres problèmes de &lsquo;fragmentation&rsquo; spécifiques aux index, et qui n&rsquo;existent pas avec les tables. </p>
<p>Le premier est le fait qu&rsquo;on ne met pas à jour une entrée d&rsquo;index: on supprime l&rsquo;entrée correspondant à l&rsquo;ancienne valeur, et on ajoute la nouvelle entrée correspondant à la nouvelle valeur. Si ces mises à jours se font de manière aléatoire, alors il n&rsquo;y a aucun des problèmes associés aux suppressions de masse. Mais si il y a un pattern de modification lié au temps, par exemple si vous avez un index sur une colonne &lsquo;dernière_modification&rsquo; alors vous pouvez vous retrouver avec le pire effet d&rsquo;un index partiellement rempli. Dans un cas comme celui-ci, vous allez supprimer (lentement) des entrées vers le début de l&rsquo;index pour les insérer tout à la fin. Et l&rsquo;espace libéré par les suppression ne sera jamais réutilisé puisque les lignes ne peuvent pas êtres modifiées dans le passé. En plus, si vous continuez à modifier des lignes du passé vers le futur, vous continuez à visiter des blocs faiblement remplis. Et si c&rsquo;est un système transactionnel où les utilisateurs modifient une ou deux lignes à chaque fois, la recherche de l&rsquo;entrée et la mise à jour dans l&rsquo;index peut prendre une proportion significative du travail effectué par chaque requête d&rsquo;update. Vous devez au moins être au courant de ce type d&rsquo;activité afin de prévoir comment en mesurer l&rsquo;impact sur les performances et adopter une stratégie pour y faire face.</p>
<p>Le deuxième type de fragmentation propre aux index, pour lequel le terme de fragmentation semble le plus approprié, vient des divisions des blocs feuilles (<em>leaf block split</em>). Si vous voulez ajouter une entrée dans un bloc feuille qui est plein, alors Oracle doit trouver un bloc vide quelque part, y déplacer à peu près la moitié des données du bloc courant, puis lier ce bloc à sa bonne place dans la structure de l&rsquo;index. Par consequent, les blocs qui sont &lsquo;logiquement&rsquo; adjacents ne sont pas nécessairement &lsquo;physiquement&rsquo; adjacents. Cela veut dire que lorsque vous faites un <em>index range scan</em> assez large (ou un <em>index full scan</em>) vous vous retrouvez à faire beaucoup de lectures aléatoires de blocs.</p>
<p>C&rsquo;est ici que SQL Server (et probablement sybase et peut-être DB2) entrent en jeu. La manière dont SQL Server gère l&rsquo;espace libre pour les tables non clusterisées (<em>heap tables</em>) n&rsquo;est pas très efficace. Donc c&rsquo;est presque un article de foi (voire un dogme) que toutes les tables dans SQL Server doivent être construites en index cluster (<em>clustered indexes</em>), ce qui veut dire dans les termes Oracle, que toutes les tables sont des IOT (<em>index organized tables</em> tables organisées en index).  Si vous avez essayé de regrouper (<em>cluster</em>) vos données, et y avez réfléchi soigneusement et délibérément, alors les divisions des blocs feuilles (<em>leaf block split</em>) détruisent votre effort de garder ensemble les données liées. Il n&rsquo;est donc pas surprenant que les DBA qui ont une expérience en SQL Server (et Sybase et DB2) soient si favorables à l&rsquo;idée de reconstruire les index fréquemment. Si vous reconstruisez un index cluster, vous ramenez les enregistrements là où vous voulez. Heureusement, cela ne nécessite pas de reconstruire tous les autres indexes de la table puisque, comme les index secondaires des IOT d&rsquo;Oracle, les autres indexes en SQL Server utilisent la clé unique (ou rendue unique) comme identifiant de la ligne.</p>
<p>Pour Oracle, ce type de fragmentation ne pose généralement pas de problème, à condition qu&rsquo;il concerne des index B-arbre standard, vu que, comme c&rsquo;est précisé plus haut, la plupart des requêtes passent la plus grande partie de leur temps à visiter la table. Mais le cas de SQL Server donne une indication du cas où vous devrez considérer plus sérieusement les effets de la &lsquo;fragmentation&rsquo; et le besoin de reconstruire les index. Si, en tant que DBA Oracle vous avez crée une table en IOT, alors vous aviez probablement une bonne raison de faire ce choix, et il s&rsquo;agissait probablement de s&rsquo;assurer que les données qui arrivent dans un certain ordre sont stockées dans un autre ordre, afin de garder ensemble des données qui sont liées.</p>
<p>Si vous avez crée une IOT pour garder les données regroupées, alors les divisions de blocs feuilles vont amener les données à être un peu dispersées. Avant de s&rsquo;inquiéter, il faut étudier l&rsquo;importance de cette dispersion, et le bénéfice, peut-être marginal, d&rsquo;y faire quelque chose. Pour illustrer cela, imaginez que vous ayez une requête sur une table IOT importante qui récupère 200 lignes de 200 octets. Comme sur une table normale cela aurait demandé à lire 200 blocs différents dispersés aléatoirement, vous avez décidé d&rsquo;implémenter la table en IOT. Si l&rsquo;on prends le cas le pire pour le divisions de blocs feuilles (50/50 avec aucun re-remplissage) alors les 200 lignes vont aller dans la IOT avec environ 20 lignes par blocs sur un total de 10 blocs feuilles. A cause du moment où chaque division de bloc feuille a lieu, on peut penser que ces 1à blocs vont finir dispersés assez aléatoirement dans tout le segment d&rsquo;index. Si vous reconstruisez l&rsquo;index, vous allez pouvoir compacter les données sur seulement 5 blocs, et ces 5 blocs vont souvent être adjacents dans le segment plutôt que dispersés. Et ce regroupement ca probablement faire que vous aurez un petit bénéfice de performance si l&rsquo;<em>index range scan</em> doit aller sur disque. Note: SQL Server travaille avec des tailles d&rsquo;extents de 8 blocs de 8 KB et le logiciel de base de donnée peut coopérer avec le système d&rsquo;exploitation pour négocier une lecture en avance (<em>readahead</em>) de l&rsquo;extent complet dans ce genre de situation. Cet ensemble de détails font que la réorganisation d&rsquo;index dans ces circonstances et plus bénéfique sur SQL Server que ce cerait le cas sur Oracle.</p>
<p>Après avoir passé un peu de temps à réfléchir à ce genre de scénario, il est plus facile de comprendre comment évaluer le bénéfice que vous pouvez tirer de la réorganisation d&rsquo;une IOT. Quelle est la rédution du nombre total de visites de blocs que vous aurez à faire ? Combien de ces visites seraient des I/O disque distincts ? Quel serait le bénéfice du point de vue d&rsquo;Oracle, du système d&rsquo;exploitation, des drivers hardware, capable d&rsquo;implémenter un <em>readahead</em> qui réduit le temps de lecture de ces blocs ? Gardez à l&rsquo;esprit la remarque importante que j&rsquo;ai fait dans l&rsquo;article sur la fragmentation de disque: même si deux blocs aparaissent comme adjacent du point de vue des fichiers Oracle, l&rsquo;introduction du <em>stripping</em>, et du <em>load balancing</em> peuvent faire que ces deux blocs sont sur des disques différents.</p>
<p><ins>Fin de la série</ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fragmentation &#8211; Table, par Jonathan Lewis (3ème partie)</title>
		<link>https://blog.developpez.com/pachot/jl_fragmentation_3/</link>
		<comments>https://blog.developpez.com/pachot/jl_fragmentation_3/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 19:55:00 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>
		<category><![CDATA[Traductions]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ceci est une traduction de d&#8217;un post de Jonathan Lewis sur son blog &#8211; la troisième partie d&#8217;une série de quatre sur la fragmentation (original en anglais). Il est conseillé de lire avant: Fragmentation &#8211; Introduction, Fragmentation &#8211; Disque et &#8230; <a href="https://blog.developpez.com/pachot/jl_fragmentation_3/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Ceci est une traduction de d&rsquo;un post de Jonathan Lewis sur son blog &#8211; la troisième partie d&rsquo;une série de quatre sur la fragmentation (<a href="http://jonathanlewis.wordpress.com/2010/07/19/fragmentation-3/">original en anglais</a>). Il est conseillé de lire avant: <a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">Fragmentation &#8211; Introduction</a>, <a href="http://blog.developpez.com/pachot/p9126/auteurs/jl-fragmentation-2/">Fragmentation &#8211; Disque et Tablespace</a></ins></p></blockquote>
<h2> Fragmentation Table</h2>
<p>Dans l&rsquo;<a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">introduction</a> nous avons parlé d&rsquo;un type de fragmentation au niveau table qui, en général, ne pose pas de problème: la fragmentation d&rsquo;une table en plusieurs <em>extents</em>. Et il y a une chose amusante, c&rsquo;est que ASSM (<em>Automatic Segment Space Management</em> &#8211; la gestion automatique de l&rsquo;espace libre dans les segments) a introduit une nouvelle forme de fragmentation, mais qui ne pose généralement pas de problème non plus.<br />
<span id="more-6"></span></p>
<p>En ASSM, lorsqu&rsquo;un processus Oracle vérifie le <em>bitmap</em> qui garde la trace de l&rsquo;espace libre d&rsquo;un objet, et qu&rsquo;il ne trouve pas assez d&rsquo;espace libre pour insérer de nouvelles données, alors il va formater 16 blocs <u>quelque part</u> dans l&rsquo;<em>extent</em> en cours (après avoir alloué un nouvel <em>extent</em> si nécessaire). Ces 16 blocs peuvent être n&rsquo;importe où dans l&rsquo;<em>extent</em> (du moment que le numéro du bloc de départ par rapport à l&rsquo;<em>extent</em> soit un multiple de 16).<br />
L&rsquo;emplacement de cet ensemble de 16 blocs est déterminé par l&rsquo;identifiant du processus (le PID &#8211; <em>process id</em>), de même que le choix du bloc à utiliser parmi ces 16. Ce qui veut dire si vous créez une table dans un tablespace qui a des extents uniformes de 1Mo, vous pouvez vous  retrouver à ce que la première ligne que vous insérez se retrouve dans le tout dernier bloc de son extent.<br />
Cela n&rsquo;a généralement pas d&rsquo;importance parce que:</p>
<ul>
<li>la plupart des accès I/O se font bloc par bloc plutot que par full scan, et donc l&rsquo;emplacement du bloc dans l&rsquo;extent n&rsquo;a pas beaucoup d&rsquo;importance.</li>
<li>ce &lsquo;désordre&rsquo; ne se retrouve en principe que sur le dernier extent de la table</li>
<li>en cas de full scan, pour savoir quel morceaux de 16 blocs doivent être traités de manière spéciale, Oracle utilise un mécanisme qui minimise le surcoût de cette vérification, en utilisant les LHWM (<em>low high water mark</em>) and HHWM (<em>high high water mark</em>).  </li>
</ul>
<p>Le type de fragmentation de table le plus important, et le plus courant, vient des données qui sont supprimées, et on peut alors se préoccuper des blocs qui ont un faible taux de remplissage. Lorsqu&rsquo;on crée une table, on précise l&rsquo;espace qui doit être réservé dans chaque bloc afin de garder une marge pour les lignes qui vont être modifiées (UPDATE) et &#8211; explicitement en <em>freelist </em>ou implicitement en ASSM &#8211; on précise aussi l&rsquo;espace libre que doit contenir un bloc pour qu&rsquo;il puisse être à nouveau la cible d&rsquo;une nouvelle insertion de données (INSERT).  </p>
<p>Si on regarde tous les scénarios possibles qui doivent être pris en compte avec avec des insertions de données, des modifications et des suppressions, on se rend compte à quel point il peut être difficile pour Oracle d&rsquo;écrire du code qui gère l&rsquo;espace libre de façon efficace et opportune. On voit aussi à quel point il est difficile, en tant que développeur ou DBA, de préciser des limites raisonnables pour la gestion de l&rsquo;espace afin de minimiser les problèmes de performances dans des cas extrêmes. Au final, il est possible de se retrouver avec une table  qui a une quantité importante d&rsquo;espace libre dans chaque bloc, et il faut alors se poser les questions suivantes: Comment ce espace libre est arrivé là ? Est-ce qu&rsquo;il pose un problème de performance ? Est-ce que vous allez pouvoir le réutiliser ? Est-ce que vous allez pouvoir le réutiliser en temps opportun ? Si vous arrivez à le réutiliser, est-ce que ca n&rsquo;introduit pas un autre type de problème de performance ?</p>
<p>Prenez le cas où vous purgez la première année de données après que votre système ait tourné pendant 5 ans. Cela va probalement faire que les 20% premiers blocs de la table seront complètement vides. Il iront en <em>freelist</em>. Ou en ASSM, à l&rsquo;exception de quelques bugs, ils seront marqués comme ayant de l&rsquo;espace libre. Et cet espace libre pourra donc être réutilisé plus tard. De plus, si les performances de votre application dépendent du fait que les données qui arrivent au même moment se retrouvent ensembles (<em>clustering</em> par rapport à la date/heure d&rsquo;arrivée) alors de la manière dont l&rsquo;espace libre est rempli, le <em>clustering</em> va normalement rester intact. <br />
Par une bizarrerie de l&rsquo;implémentation, les blocs vont être utilisés dans l&rsquo;ordre inverse en gestion <em>freelist </em>et dans l&rsquo;ordre normal en ASSM (et une question me traverse l&rsquo;esprit à ce moment à propos de l&rsquo;impact de cet ordre inverse sur les indexes non-uniques qui ont peu de valeurs distinctes).</p>
<p>Cependant, pour un bon moment, avant cette réutilisation, les <em>full scan</em> de la table vont prendre 20% de temps en plus de ce qui est nécessaire. Et vous pouvez vous retrouver à faire un <em>backup </em>d&rsquo;un fichier plus gros que ce dont vous avez réellement besoin. Ces considérations peuvent alors vous décider à faire une réorganisation de la table (et d&rsquo;un <em>rebuild </em>des index) en la déplaçant vers un autre tablespace. Et elles peuvent aussi vous amener a réfléchir au partitionnement de la table.</p>
<p>Imaginez le cas d&rsquo;un système de vente par internet où les utilisateurs remplissent leur panier, paniers qui à la fin deviennent une commande. Inévitablement, certains utilisateurs vont remplir un panier sans passer la commande par la suite. Alors vous avez peut-être un programme en tâche de fond qui regarde les vieux paniers et le supprime de la base. Il y a deux imperfections dans cette implémentation, mais ce genre d&rsquo;erreur se voit souvent sur des systèmes en production. Alors dans ce cas, vous vous retrouvez avec une table qui subit constamment des <em>delete</em> sur le passé récent, de telle sorte que si vous analysez la table, vous verrez qu&rsquo;en moyenne elle a, disons, 20% d&rsquo;espace libre dans chaque bloc, à l&rsquo;exception des quelques blocs qui contiennent les paniers récents. </p>
<p>Si vous êtes en ASSM, ces 20% ne seront jamais réutilisés (sauf si en recréant la table) parce que la granularité de ASSM fait qu&rsquo;il faut au moins 25% d&rsquo;espace libre dans un bloc avant que son statut ne passe de <em>&lsquo;plein&rsquo;</em> à <em>&lsquo;0% – 25% libre&rsquo;</em>. Mais si vous êtes en <em>freelist</em> alors vous avez peut-être prévu le coup en mettant PCTUSED à un peu plus que 80. Dans ce cas, les blocs reviennent en <em>freelist</em> dès que l&rsquo;espace libre atteint 20%, et l&rsquo;espace va être réutilisé. <br />
Cela semble être une bonne idée, sauf qu&rsquo;il y a un autre problème à prendre en considération.</p>
<p>Peut-être que les bonnes performances de votre application venaient du fait que les paniers des derniers jours sont stockés dans les N derniers blocs de la table. Du fait que vous libérez 20% de l&rsquo;espace des blocs du passé, alors les paniers qui étaient stockés sur N blocs sont maintenant répartis sur 5 fois plus de blocs. Et cela veut dire que, si vous voulez garder le même niveau de performance, vous allez avoir besoin de plus de mémoire en <em>buffer cache</em>.</p>
<p>La conclusion générale est la suivante. Une fois que vous avez résolu une fois pour toutes le problème de la taille des <em>extents </em> d&rsquo;une table, le seul problème de &lsquo;fragmentation&rsquo; qui vous reste à prendre en compte est celui des blocs qui ne sont que partiellement remplis. Et il y a des patterns de suppression de données qui peuvent amener à cette situation de blocs sous-utilisés. Dans certains cas, il n&rsquo;est possible de récupérer cet espace que par une réorganisation de la table (et dans tous les cas, c&rsquo;est le partitionnement de la table qui peut faire que cette option est viable). Mais il y a aussi des patterns de suppression de données qui font que l&rsquo;espace libre est réutilisable, mais que vous ne voulez pas toujours réutiliser cet espace, car cela peut amener un autre type de problème.</p>
<p><ins>La suite: <a href="http://blog.developpez.com/pachot/p9194/auteurs/jl-fragmentation-4/">Fragmentation &#8211; Index</a></ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fragmentation &#8211; Introduction, par Jonathan Lewis</title>
		<link>https://blog.developpez.com/pachot/jl_fragmentation_1/</link>
		<comments>https://blog.developpez.com/pachot/jl_fragmentation_1/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 19:53:00 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>
		<category><![CDATA[Traductions]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ceci est une traduction d&#8217;un post de Jonathan Lewis sur son blog &#8211; la première partie d&#8217;une série de quatre sur la fragmentation (original en anglais) Cet article a commencé comme une note brève, jusqu&#8217;à ce que je réalise que &#8230; <a href="https://blog.developpez.com/pachot/jl_fragmentation_1/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Ceci est une traduction d&rsquo;un post de Jonathan Lewis sur son blog &#8211; la première partie d&rsquo;une série de quatre sur la fragmentation (<a href="http://jonathanlewis.wordpress.com/2010/07/13/fragmentation-1/">original en anglais</a>)</ins></p></blockquote>
<p>Cet article a commencé comme une note brève, jusqu&rsquo;à ce que je réalise que ça allait être plus important, et que j&rsquo;en fasse plutôt une série de quatre articles:</p>
<ul>
<li><a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">Fragmentation &#8211; Introduction</a></li>
<li><a href="http://blog.developpez.com/pachot/p9126/auteurs/jl-fragmentation-2/">Fragmentation &#8211; Disque et Tablespace</a></li>
<li><a href="http://blog.developpez.com/pachot/p9193/auteurs/jl-fragmentation-3/">Fragmentation &#8211; Table</a> <ins></ins></li>
<li><a href="http://blog.developpez.com/pachot/p9194/auteurs/jl-fragmentation-4/">Fragmentation &#8211; Index</a> <ins></ins></li>
</ul>
<h2>Introduction</h2>
<p>Le mot &lsquo;<strong>fragmentation</strong>&lsquo; donne l&rsquo;idée de quelque chose qui est cassé en plusieurs morceaux, mais il a aussi une connotation émotionnelle qui fait penser qu&rsquo;il y a beaucoup de petits morceaux. Dans le contexte d&rsquo;une base Oracle, vous devez savoir ce que vous entendez par &lsquo;morceau&rsquo;, ainsi que la granularité de ces morceaux, et leur impact possible sur les performances.<br />
<span id="more-4"></span><br />
Vu qu&rsquo;il est possible de parler de fragmentation au niveau disque (disque logique), ou au niveau fichier, niveau tablespace, niveau segment, niveau extent ou niveau block, il est important de savoir très clairement ce que vous essayez de dire lorsque vous faites un commentaire du genre &lsquo;Mon tablespace est fragmenté&rsquo; ou &lsquo;Mon index est fragmenté&rsquo;</p>
<p>Partons sur un exemple: Je crée un nouveau tablespace et je déplace une table dedans (ALTER TABLE &#8230; MOVE).<br />
Lorsque je regarde <strong>DBA_EXTENTS</strong>, ma table a 100 extents. Il est évident qu&rsquo;il y a &lsquo;fragmentation&rsquo; dans le sens premier de ce mot, puisque j&rsquo;ai 100 différents morceaux. Mais d&rsquo;autre part, puisque cette table est la première chose que j&rsquo;ai créé dans ce tablespace, je vois que ces extents sont adjacents. On pourrait alors dire que la table est &lsquo;<strong>logiquement fragmentée</strong>&lsquo; mais &lsquo;<strong>physiquement contiguë </strong>&lsquo;.</p>
<p>Est-ce que ce type de fragmentation a un impact sur les performances du système ? </p>
<p>Vu qu&rsquo;Oracle fait la plupart des I/O par bloc (nous lisons des blocs vers le buffer cache, nous écrivons des blocs dans les fichiers), et vu qu&rsquo;il n&rsquo;y a pas de conséquences au fait qu&rsquo;un bloc appartienne à <em>extent </em>plutôt qu&rsquo;un autre, alors la réponse est probablement: non.<br />
Cependant, il y a des fois où on essaie de lire plusieurs blocs contigus en un seul I/O (<em>full table</em> scan et <em>index fast full scan</em>), alors y a-t-il des conséquences au fait que notre table &lsquo;physiquement contiguë&rsquo; soit &lsquo;logiquement fragmentée&rsquo; en un grand nombre d&rsquo;extents ?</p>
<p>Que se passe-t-il si les extents font, disons, 64Ko chacun. Est-ce que cela limite la taille d&rsquo;une lecture multi-bloc (<em>db file multiblock read</em>) ? Ou bien ces lectures peuvent-elles être à cheval sur deux extents ? Et si le tablespace a deux datafiles ou plus, dans ce cas l&rsquo;allocation des extents se fait généralement en alternant les datafiles (<em>round-robin</em>), est-ce que cela affecte la manière dont les lectures pourront se faire ? Et si on fait des <em>full table scan</em> en <em>parallel query</em> (<em>parallel tablescan</em>), est-ce qu&rsquo;il y a des restrictions différentes pour les lectures directes (<em>direct-path reads</em>) ? </p>
<p>Si vous faites tourner un <em>datawarehouse </em>qui passe beaucoup de son temps à faire ce type d&rsquo;opérations, alors ce sont quelques unes des questions auquelles vous devrez savoir répondre. Voir, par exemple, une <a href="http://jonathanlewis.wordpress.com/2007/05/29/autoallocate-and-px/">note</a> que j&rsquo;ai écrit il y a trois ans à propos d&rsquo;anomalies dans les tailles d&rsquo;I/O lorsqu&rsquo;on est en <em>parallel query</em>, et l&rsquo;amélioration faite là dessus en 11G qui a été décrite <a href="http://go2.wordpress.com/?id=725X1342&amp;site=jonathanlewis.wordpress.com&amp;url=http%3A%2F%2Fantognini.ch%2F2009%2F08%2Fsystem-managed-extent-size-11g-improvements%2F&amp;sref=http%3A%2F%2Fjonathanlewis.wordpress.com%2F2010%2F07%2F13%2Ffragmentation-1%2F">ici</a> par Christian Antognini il y a quelques années.</p>
<p>Vous ne pouvez commencer à comprendre les problèmes posés par la fragmentation, et si elle a un impact <strong>- ou non -</strong> sur les performances, que lorsque vous aurez commencé à définir de manière claire ce que vous entendez par &lsquo;fragmentation&rsquo;. Dans la deuxième partie, je vais faire quelques commentaires sur la manière de réfléchir à la fragmentation au niveau disque et au niveau tablespace.</p>
<p><ins>La suite: <a href="http://blog.developpez.com/pachot/p9126/auteurs/jl-fragmentation-2/">Fragmentation &#8211; Disque et Tablespace</a></ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fragmentation &#8211; Disque et Tablespace, par Jonathan Lewis (2ème partie)</title>
		<link>https://blog.developpez.com/pachot/jl_fragmentation_2/</link>
		<comments>https://blog.developpez.com/pachot/jl_fragmentation_2/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 19:54:00 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Jonathan Lewis]]></category>
		<category><![CDATA[Traductions]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ceci est une traduction de d&#8217;un post de Jonathan Lewis sur son blog &#8211; la deuxième partie d&#8217;une série de quatre sur la fragmentation (original en anglais). Il est conseillé de lire avant: Fragmentation &#8211; Introduction Fragmentation Disque Les tablespaces &#8230; <a href="https://blog.developpez.com/pachot/jl_fragmentation_2/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Ceci est une traduction de d&rsquo;un post de Jonathan Lewis sur son blog &#8211; la deuxième partie d&rsquo;une série de quatre sur la fragmentation (<a href="http://jonathanlewis.wordpress.com/2010/07/16/fragmentation-2/">original en anglais</a>). Il est conseillé de lire avant: <a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">Fragmentation &#8211; Introduction</a></ins></p></blockquote>
<h2>Fragmentation Disque</h2>
<p>Les tablespaces sont composés de fichiers, et les fichiers sont stockés sur disque. Il s&rsquo;agit la plupart du temps de disques logiques (<em>logical volumes</em>) plutôt que de vrais disques directement (<em>real devices</em>).<br />
Lorsqu&rsquo;on fait une lecture sur un vrai disque, la taille des données qu&rsquo;on peut lire en une seule opération physique est quelque chose comme 400Ko ou 500Ko. C&rsquo;est le contenu d&rsquo;une seule piste sur un seul plateau d&rsquo;un disque physique. Une lecture plus large continue en passant sur un autre plateau (ce n&rsquo;est pas un mouvement physique des têtes, mais une commutation &lsquo;électronique&rsquo;) , ou bien en passant sur une autre piste (c&rsquo;est alors un mouvement physique, mouvement latéral de la tête), ou encore en passant sur un autre disque. Passer sur un autre disque, c&rsquo;est rejoindre une autre file d&rsquo;attente de disque, et dans ce cas le logiciel du SAN, ou l&rsquo;équivalent, aura probablement anticipé les disques dont vous aurez besoin et aura lancé en parallèle ces demandes de lectures dans les files d&rsquo;attentes correspondantes.<br />
<span id="more-5"></span><br />
Lorsque vous créez un datafile sous Oracle, vous ne savez pas à quel point le fichier est dispersé sur les disques physiques du système. <strong>Au mieux</strong>, une lecture de 1Mo va impliquer 3 ou 4 rotations d&rsquo;un même disque, avec seulement des passage d&rsquo;un plateau à l&rsquo;autre (commutations &lsquo;électroniques&rsquo;). <strong>Et au pire</strong>, j&rsquo;ai déjà vu un seul I/O impliquer jusqu&rsquo;à 32 opérations différentes sur les disques, à cause des nombreuses couches de logicielles utilisés pour stripper sur les disques, puis sur les groupes de disques (<em>diskgroup</em>), puis sur les volumes logiques (<em>logical volumes</em>), etc.<br />
Si on est tout seul sur le SAN, ce dernier cas où la lecture est parallélisée sur tous les disques est vraiment optimal pour les performances. Mais sur un système en production, c&rsquo;est une calamité pour les files d&rsquo;attentes. C&rsquo;est pour cette raison que c&rsquo;est une bonne stratégie de présenter des disques &lsquo;bruts&rsquo; à ASM, en ayant une seule couche logicielle entre Oracle et les disques, et il s&rsquo;agit en plus d&rsquo;une couche logicielle qui connaît le comportement et les données d&rsquo;Oracle.</p>
<p>A retenir: Ne pas mettre trop de couches de logiciels &lsquo;intelligents&rsquo; entre Oracle et les lecteurs de disque.</p>
<h2>Fragmentation Tablespace</h2>
<p>Bien sûr, vous pouvez créer un tablespace avec plusieurs fichiers. Alors, par définition, le tablespace est fragmenté, même si il n&rsquo;y a à la base rien de négatif avec ce type de fragmentation. Mais comme je l&rsquo;ai précisé dans la note précédente (<a href="http://blog.developpez.com/pachot/p9125/auteurs/jl-fragmentation-1/">introduction</a>), cela peut avoir des effets de bord sur la disposition des extents d&rsquo;un segment, et arriver à des cas où vous voulez faire une seule lecture d&rsquo;un gros volume de données, et vous retrouver en fait à faire plusieurs I/O plus petits &#8211; avec pour conséquence une augmentation de l&rsquo;attente sur les I/O.</p>
<p>Le cas de fragmentation que la plupart des gens ont à l&rsquo;esprit quand ils parlent de fragmentation de tablespace, c&rsquo;est à dire le fait qu&rsquo;il y ait des &lsquo;trous&rsquo; d&rsquo;espace libre au milieu de l&rsquo;espace alloué, est quelque chose qui a aussi été appelé &lsquo;gruyèrisation&rsquo; (ou en anglais <em>honey-combing</em> ou <em>bubbling</em>). C&rsquo;est un effet de bord lorsqu&rsquo;on supprime (<em>DROP</em>) ou réduit (<em>SHRINK</em>) des objects, qu&rsquo;on déplace des tables (<em>MOVE</em>) ou qu&rsquo;on reconstruit des indexes (<em>REBUILD</em>). On finit par avoir des morceaux d&rsquo;espace libre dispersés sur tout le tablespace. Chaque fois que vous réorganisez un objet, vous allez probablement remplir certains de ces morceaux, mais en laisser d&rsquo;autres vides là où se trouvait l&rsquo;objet avant.</p>
<p>Fondamentalement, il est rare que ce type de fragmentation pose un problème, parce que cet espace vide n&rsquo;entraîne pas de travail supplémentaire, sauf lorsque on fait un <i>backup</i> du fichier. Si vous pensez que le temps passé à copier cet espace vide lors d&rsquo;un <i>backup</i> a un impact important sur la durée de la sauvegarde (dans le cas où le backup dépasse la fenêtre de temps permise avant le prochain cycle de chargement de données, par exemple), alors vous pouvez prévoir de déplacer des objets de telle sorte que l&rsquo;espace libre se trouve à la fin de fichiers. Cela permet ensuite de réduire la taille des fichiers: voir par exemple cette note sur la <a href="http://jonathanlewis.wordpress.com/2010/02/06/shrink-tablespace/">réduction de la taille des tablespaces</a> <ins>(en anglais)</ins>.<br />
Par contre, il faut garder à l&rsquo;esprit qu&rsquo;il peut y avoir des effets indésirables lors de cette réorganisation. Il y avait cette question sur le forum OTN il y a quelques années où un DBA s&rsquo;est aperçu que déplacer des tables les a rendu plus volumineuses. j&rsquo;ai écrit une <a href="http://jonathanlewis.wordpress.com/2007/11/23/table-rebuilds/">note</a> <ins>(en anglais)</ins> à propose de cela, en reprenant la question et la réponse (réponse que j&rsquo;avais publiée dans &lsquo;Practical Oracle 8i&rsquo;).</p>
<p>Les difficultés liées à cette fragmentation &lsquo;gruyère&rsquo; on été en grande partie un effet secondaire du paramètre PCTINCREASE d&rsquo;Oracle qu&rsquo;on pouvait spécifier pour les segments de données, amplifié par l&rsquo;idée reçue qu&rsquo;il vaut mieux réduire les objets à un seul <em>extent</em>. Mais depuis l&rsquo;introduction des tablespaces dont l&rsquo;espace libre est géré localement (<em>LMT &#8211; Locally Managed Tablespaces</em>), qui simplifient les options de dimensionnement des <em>extents</em> (surtout pour la taille d&rsquo;extent UNIFORM), la seule question est <strong>quand</strong> l&rsquo;espace libéré va être réutilisé et non <strong>comment</strong> est gérée cette réutilisation.</p>
<p>Pour en lire un peu plus là dessus: une <a href="http://jonathanlewis.wordpress.com/2008/07/01/ancient-history/">histoire ancienne</a> que j&rsquo;ai publié bien avant qu&rsquo;Oracle n&rsquo;introduise les <em>Locally Managed Tablespaces</em> avec une taille d&rsquo;extent uniforme, republié il y a 2 ans.</p>
<p><ins>La suite: <a href="http://blog.developpez.com/pachot/p9193/auteurs/jl-fragmentation-3/">Fragmentation &#8211; Table</a> </ins></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Niveaux d&#8217;isolations, par Tom Kyte</title>
		<link>https://blog.developpez.com/pachot/tk_isolation_levels/</link>
		<comments>https://blog.developpez.com/pachot/tk_isolation_levels/#comments</comments>
		<pubDate>Sat, 24 Apr 2010 20:35:00 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Tom Kyte]]></category>
		<category><![CDATA[Traductions]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;un article de Tom Kyte publié dans Oracle Magazine en Novembre 2005. L&#8217;article original en anglais se trouve ici. Il peut être utile de lire avant Lectures cohérentes et multi-versionnage (traduit aussi de Tom Kyte). &#8230; <a href="https://blog.developpez.com/pachot/tk_isolation_levels/">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<blockquote><p><ins>Cet article est la traduction d&rsquo;un article de Tom Kyte publié dans Oracle Magazine en Novembre 2005. L&rsquo;article original en anglais se trouve <a href="http://www.oracle.com/technetwork/oramag/2005/05-nov/o65asktom-082389.html">ici</a>.<br />
Il peut être utile de lire avant <a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">Lectures cohérentes et multi-versionnage</a> (traduit aussi de Tom Kyte).</p>
<p></ins></p></blockquote>
<p><ins>Question posée sur <a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:42560223798158">AskTom</a>:</ins></p>
<p><em>J&rsquo;ai lu le manuel &lsquo;Database Concepts&rsquo; de la documentation Oracle, au chapitre <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10713/consist.htm#CNCPT020">&laquo;&nbsp;Data Concurrency and Consistency&nbsp;&raquo;</a> mais je n&rsquo;ai pas vraiment compris la différence entre les niveaux d&rsquo;isolation <strong>serializable</strong> et <strong>read-committed</strong>. Pouvez-vous donner des exemples qui expliquent cela clairement ?</em></p>
<p><ins>Réponse de Tom Kyte</ins>:</p>
<p>Avant de lire ce qui suit, vous pouvez aller voir l&rsquo;article d&rsquo;Oracle Magazine de Mai/Juin 2005 <ins>(<a href="http://www.oracle.com/technology/oramag/oracle/05-may/o35asktom.html">en anglais</a>)</ins> où je décris la fonctionnalité que j&rsquo;ai toujours préféré dans Oracle: le multi-versioning. Sa compréhension est cruciale pour réussir avec Oracle, mais il vous aidera aussi à comprendre les concepts décrits ci dessous. (<ins>Voir la traduction d&rsquo;un article similaire <a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">ici</a></ins>)</p>
<p>[La suite est un extrait de &lsquo;<a href="http://apress.com/book/view/9781590595305">Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions</a>&lsquo;]</p>
<p><span id="more-28"></span></p>
<p>Le standard SQL ANSI/ISO SQL définit 4 niveaux d&rsquo;isolation des transactions (<em>transaction isolation levels</em>), qui peuvent donner des résultats différents pour le même scénario de transactions. Cela veut dire que le même travail, effectué de la même manière, avec les mêmes données, peut avoir des résultats différents, en fonction du niveau d&rsquo;isolation. Ces niveaux sont définis par les 3 phénomènes qui sont autorisés ou non pour un certain niveau d&rsquo;isolation:</p>
<p><strong>Lecture &lsquo;sale&rsquo; (<em>Dirty read</em>)</strong>: Sa signification est aussi négative que son nom. Vous avez le droit de lire des données non-committées, ou &lsquo;sales&rsquo;. Vous pouvez avoir le même effet en ouvrant un fichier du système d&rsquo;exploitation sur lequel quelqu&rsquo;un est en train d&rsquo;écrire, et lire n&rsquo;importe quelle donnée qui s&rsquo;y trouve. L&rsquo;intégrité des données est compromise, les clés étrangères sont violées, et les contraintes d&rsquo;unicité sont ignorées.</p>
<p><strong>Lecture &lsquo;non-répétable&rsquo; (<em>Nonrepeatable read</em>)</strong>: Cela veut simplement dire que si vous lisez un enregistrement à un temps t1, et essayez de le relire à un temps t2, l&rsquo;enregistrement peut avoir changé. Il a pu disparaître, il a pu être modifié, etc.</p>
<p><strong>Lecture &lsquo;fantôme&rsquo; (<em>Phantom read</em>)</strong>: Cela veut dire que si vous faites une requête à un temps t1, et que vous la relancez à un temps t2, des nouveaux enregistrements ont pu être ajoutés dans la base, ce qui peut modifier votre résultat. C&rsquo;est différent de la lecture non-répétable dans le sens où les enregistrements que vous avez lu précédemment n&rsquo;ont pas changé. Mais de nouveaux enregistrements répondent à votre critère de requête.</p>
<p>Il faut noter que le standard ANSI/ISO définit les caractéristiques des niveaux d&rsquo;isolation au niveau des transactions, et pas seulement au niveau de chaque requête. Je parlerai de l&rsquo;isolation au niveau transaction, et pas seulement au niveau requête.</p>
<p>Les niveaux d&rsquo;isolations SQL sont définis par leur le fait d&rsquo;autoriser ou non chacun des phénomènes précédents. Il est important de noter que le standard SQL n&rsquo;impose aucun système de verrouillage spécifique, et ne demande aucun comportement particulier, mais il décrit plutôt les niveaux d&rsquo;isolation en termes de phénomènes permis, afin que puissent exister différentes implémentations pour les mécanismes de verrouillage et de concurrence d&rsquo;accès).</p>
<p><strong>Les niveaux d&rsquo;isolation ANSI:</strong></p>
<table border="1">
<tr>
<td><strong>Niveau d&rsquo;isolation</strong></td>
<td><strong>Lecture &lsquo;sale&rsquo; <br />(<em>Dirty read</em>)</strong></td>
<td><strong>Lecture &lsquo;non-répétable&rsquo; <br />(<em>Nonrepeatable read</em>)</strong></td>
<td><strong>Lecture &lsquo;fantôme&rsquo; <br />(<em>Phantom read</em>)</strong></td>
</tr>
<tr>
<td>READ UNCOMMITTED</td>
<td>Permise</td>
<td>Permise</td>
<td>Permise</td>
</tr>
<tr>
<td>READ COMMITTED</td>
<td>&#8211;</td>
<td>Permise</td>
<td>Permise</td>
</tr>
<tr>
<td>REPEATABLE READ</td>
<td>&#8211;</td>
<td>&#8211;</td>
<td>Permise</td>
</tr>
<tr>
<td>SERIALIZABLE</td>
<td>&#8211;</td>
<td>&#8211;</td>
<td>&#8211;</td>
</tr>
</table>
<p>Oracle supporte explicitement les niveaux d&rsquo;isolation <code class="codecolorer text default"><span class="text">READ COMMITTED</span></code> et <code class="codecolorer text default"><span class="text">SERIALIZABLE</span></code> tels qu&rsquo;ils sont définis dans le standard. Mais ce n&rsquo;est pas suffisant de dire cela. Les standard SQL voulaient essayer de permettre des degrés de cohérence différents en fonction du niveau d&rsquo;isolation où la requête est exécutée. <code class="codecolorer text default"><span class="text">REPEATABLE READ</span></code> est le niveau à partir duquel, d&rsquo;après le standard SQL, le résultat d&rsquo;une requête est censé être cohérent. <code class="codecolorer text default"><span class="text">READ COMMITTED</span></code>, pour le standard SQL, ne garantit pas un résultat cohérent en lecture, et  <code class="codecolorer text default"><span class="text">READ UNCOMMITTED</span></code> est supposé être le niveau qui utilise des lectures non-bloquantes.</p>
<p>Mais sous Oracle, <code class="codecolorer text default"><span class="text">READ COMMITTED</span></code> a tous les attributs nécessaires pour avoir une lecture cohérente au niveau de la requête. Dans les autres SGBD, <code class="codecolorer text default"><span class="text">READ COMMITTED</span></code> peut et va retourner des résultats qui n&rsquo;ont jamais existé en base de donnée. Et en plus, Oracle supporte l&rsquo;idée de <code class="codecolorer text default"><span class="text">READ UNCOMMITTED</span></code>. La lecture &lsquo;sale&rsquo; a pour but de fournir une lecture non-bloquante, où les requêtes ne bloquent pas et ne sont pas bloquées par les mises à jour concurrentes des mêmes données. Mais Oracle na pas besoin de lectures &lsquo;sales&rsquo; pour atteindre ce but, et il ne les implémente pas. La lecture sale (&lsquo;<em>dirty reads</em>&lsquo;) est une implémentation que les autre SGBD doivent utiliser pour permettre des lectures non-bloquantes.</p>
<p>En plus des 4 nivaux d&rsquo;isolation définis par le standard SQL, le SGBD Oracle en fournit un autre: <code class="codecolorer text default"><span class="text">READ ONLY</span></code> (lecture seule). Une transaction <code class="codecolorer text default"><span class="text">READ ONLY</span></code> est equivalente à une transaction <code class="codecolorer text default"><span class="text">REPEATABLE READ</span></code> ou <code class="codecolorer text default"><span class="text">SERIALIZABLE</span></code>, mais qui ne peut faire aucune mise à jour. Une transaction qui utilise le niveau d&rsquo;isolation <code class="codecolorer text default"><span class="text">READ ONLY</span></code> ne voit que les modifications qui ont été committées avant que la transaction ne commence. Les <em>insert</em>, <em>update</em> et <em>delete</em> ne sont pas permis dans ce mode (les autres sessions peuvent mettre à jour les données, mais pas la transaction <code class="codecolorer text default"><span class="text">READ ONLY</span></code>). En utilisant ce mode, vous avez les niveaux d&rsquo;isolation <code class="codecolorer text default"><span class="text">REPEATABLE READ</span></code> et <code class="codecolorer text default"><span class="text">SERIALIZABLE</span></code>.</p>
<p>Je vais maintenant expliquer le rôle du multi-versioning et de la lecture cohérente (<em>read consistency</em>) dans le système d&rsquo;isolation, et comment les SGBD qui ne font pas du multi-versioning arrivent au même résultat. Cette explication est utile pour tous ceux qui ont utilisé un autre SGBD et qui croient qu&rsquo;ils ont compris comment les niveaux d&rsquo;isolation doivent fonctionner. C&rsquo;est aussi intéressant pour voir comment le standard SQL ISO/ANSI , qui était supposé supprimer les différences entre les SGBD, au contraire les permet. Le standard, même s&rsquo;il est très détaillé, peut être implémenté de différentes manières.</p>
<p><strong>READ UNCOMMITTED.</strong></p>
<p>Le niveau d&rsquo;isolation READ UNCOMMITTED permet les lectures &lsquo;sales&rsquo; (<em>dirty reads</em>). Le SGBD Oracle n&rsquo;utilise pas ces <em>dirty reads</em>, et ne les permet même pas. READ UNCOMMITTED est défini dans le standard pour permettre les lectures non bloquantes. Comme vous avez vu, Oracle fait des lectures non bloquantes par défaut. Vous auriez du mal à faire un SELECT en base qui bloque et attends (il y a un cas particulier pour les transactions distribuées). Chaque requête, que ce soit un SELECT, INSERT, UPDATE, MERGE, ou DELETE, fait ses lecture de manière cohérente. Cela peut sembler amusant de dire que l&rsquo;UPDATE est une requête qui lit, mais c&rsquo;est le cas. UPDATE a 2 composants: une lecture, qui est définie par la clause WHERE, et une écriture qui est définie par la clause SET. UPDATE lit et écrit dans la base de donnée, comme tous les ordres DML. La seule exception est l&rsquo;INSERT d&rsquo;une ligne unique, avec la clause VALUES, et qui n&rsquo;a pas de composante de lecture, mais seulement d&rsquo;écriture.</p>
<p>Dans le chapitre 1 de &lsquo;Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions&rsquo; <ins>(ou en français <a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">ici</a>)</ins>, J&rsquo;ai montré comment Oracle fait une lecture cohérente: avec un requête, sur une seule table, qui retourne pourtant les lignes qui ont été supprimées depuis l&rsquo;ouverture du curseur. Je vais maintenant explorer un exemple du monde réel pour voir ce qu&rsquo;il se passe sous Oracle avec le multi-versioning, et aussi ce qu&rsquo;il se passe dans un grand nombre d&rsquo;autres SGBD.</p>
<p>Je vais commencer avec la même table et encore une requête très simple:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">create table comptes &nbsp;<br />
( numero_compte number primary key, &nbsp;<br />
&nbsp; montant_compte number not null <br />
); <br />
&nbsp;<br />
select sum(montant_compte) from comptes;</div></div>
<p>Avant de lancer la requête, j&rsquo;affiche les données:</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Ligne &nbsp; &nbsp; &nbsp;Numéro du Compte &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Montant du Compte <br />
----- &nbsp; &nbsp; &nbsp;---------------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;----------------- <br />
1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;123 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 500,00 € <br />
2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;456 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 240,25 € <br />
... &nbsp; &nbsp; &nbsp; &nbsp;... &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ... <br />
342023 &nbsp; &nbsp; 987 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 100,00 €</div></div>

</pre>
<p>A ce moment, mon SELECT démarre et va lire la ligne 1, puis la ligne 2, etc. A un certain moment lorsque cette requête est en cours d&rsquo;exécution, une transaction fait un virement de 400€ du compte 123 vers compte 987. Cette transaction a fait les deux UPDATE mais ne fait pas encore le COMMIT. La table COMPTES ressemble maintenant à cela:</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Ligne &nbsp; &nbsp; &nbsp;Numéro du Compte &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Montant du Compte &nbsp;<br />
----- &nbsp; &nbsp; &nbsp;---------------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;---------------------- <br />
1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;123 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (500,00 €) -&gt; 100,00 € &nbsp;[vérouillé] <br />
2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;456 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 240,25 € <br />
... &nbsp; &nbsp; &nbsp; &nbsp;... &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ... <br />
342023 &nbsp; &nbsp; 987 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (100,00 €) -&gt; 500,00 € &nbsp;[vérouillé]</div></div>

</pre>
<p>Nous voyons que 2 enregistrements sont verrouillés (<em>locked</em>). Si quelqu&rsquo;un essaie de les modifier, il sera bloqué. Jusque là le comportement qu&rsquo;on voit est sensiblement le même sur tous les SGBD. La différence va se faire lorsque notre SELECT va tomber sur les données verrouillées.</p>
<p>Lorsque la requête que j&rsquo;exécute arrive au bloc qui contient la ligne verrouillée (ligne 342023) tout au bout de la table, elle voit que les données ont changé depuis le début de la requête. Pour fournir une réponse cohérente (<em>consistent</em>) et juste, le SGBD Oracle va créer une copie du bloc, mais où cette ligne sera comme elle était au moment où la requête a commencé. C&rsquo;est à dire qu&rsquo;on va lire la valeur de 100€, qui était le montant du compte à ce moment là. En effet, au lieu de lire l&rsquo;information modifiée, Oracle utilise l&rsquo;undo (aussi appelé <em>rollback segment</em>) pour reconstruire l&rsquo;information. Une réponse cohérente et juste est alors retournée, sans avoir à attendre que la transaction concurrent ne soit committée.</p>
<p>Par contre, un SGBD qui autorise les lectures &lsquo;sales&rsquo; va simplement retourner la valeur qu&rsquo;il voit pour le compte 987 au moment où il la lit, c&rsquo;est à dire 500€. La requête va donc compter deux fois les 400€ du montant du virement. Non seulement il retourne la mauvaise réponse, mais en plus il renvoi un total qui n&rsquo;a jamais existé dans la base. Dans un environnement multi-utilisateur, une lecture &lsquo;sale&rsquo; peut être une fonctionnalité très dangereuse. Personnellement, je n&rsquo;en ai jamais vu l&rsquo;utilité.<br />
Et si l&rsquo;on prends le cas où, au lieu d&rsquo;un virement, la transaction fait juste un dépôt de 400€ sur le compte 987. La lecture sale va compter les 400€ et retourner alors une réponse juste ? Et bien non. Supposons que la transaction fasse un rollback. Alors j&rsquo;aurais compté les 400€ qui n&rsquo;ont jamais été réellement en base.</p>
<p>L&rsquo;idée ici, c&rsquo;est que la lecture &lsquo;sale&rsquo; n&rsquo;est pas une fonctionnalité, mais un handicap. Et sous Oracle, elle n&rsquo;est simplement pas nécessaire. Vous avez tous les avantages d&rsquo;une lecture non-bloquante, mais sans avoir des résultats faux.</p>
<p><strong>READ COMMITTED.</strong></p>
<p>Le niveau d&rsquo;isolation READ COMMITTED précise que la transction ne doit lire que les données qui ont été committées. Il n&rsquo;y a pas de lectures &lsquo;sales&rsquo; (qui sont les lectures de données non committées). Il peut y avoir des lectures &lsquo;non-répétable&rsquo; (<em>nonrepeatable read</em>), ce qui veut dire que la relecture de la même ligne dans la même transaction peut donner un résultat différent. Il peut y avoir des  Lecture &lsquo;fantôme&rsquo; (<em>phantom read</em>), ce qui veut dire que, dans la même transaction, une ligne nouvellement insérée et committée devient visible par une requête alors qu&rsquo;elle ne l&rsquo;était pas avant.<br />
READ COMMITTED est probablement le niveau d&rsquo;isolation le plus courant dans les applications de base de données, et c&rsquo;est le mode par défaut pour Oracle. Il est rare de voir un autre niveau d&rsquo;utilisation utilisé dans une base Oracle.</p>
<p>Cependant, arriver au niveau d&rsquo;isolation READ COMMITTED est plus compliqué qu&rsquo;il n&rsquo;y parait. Si vous regardez le premier tableau (Les niveaux d&rsquo;isolation ANSI), cela semble simple. On a l&rsquo;impression qu&rsquo;une requête qui utilise READ COMMITTED se comporte de la même manière dans n&rsquo;importe quel SGBD, non ? Et bien ce n&rsquo;est pas le cas. Dans la pluspart des autres SGBD, si vous lisez plusieurs lignes en une seule requête, le niveau READ COMMITTED peut être aussi mauvais que que des lectures &lsquo;sales&rsquo;, en fonction de la manière dont il est implémenté.</p>
<p>Sous Oracle, en utilisant le système de multi-versionnage et les lectures cohérentes, la requête sur la table COMPTES donne le même résultat en READ COMMITTED que dans l&rsquo;exemple du READ UNCOMMITTED. Oracle va reconstruire les données modifiées pour qu&rsquo;elles apparaissent telles qu&rsquo;elles étaient lorsque la requête a commencé, retournant la réponse qui était en base de donnée à ce moment là.</p>
<p>Maintenant, je vais regarder comment mon exemple précédant fonctionne en READ COMMITTED dans les autres SGBD. Je reprends au même endroit:<br />
&#8211; Je suis au milieu de la table. J&rsquo;ai lu et additionné les N premières lignes.<br />
&#8211; L&rsquo;autre transaction a viré 400€ du compte 123 vers le compte 987.<br />
&#8211; Cette autre transaction n&rsquo;a pas encore été committée, donc les lignes contenant les informations des comptes 123 et 987 sont verrouillées.</p>
<p>Je sais ce qu&rsquo;il arrive sous Oracle lorsqu&rsquo;on arrive au compte 987: il voit la modification, calcule que le montant devait être 100€ et c&rsquo;est terminé. Voici comment un autre SGBD, dans un mode READ COMMITTED par défaut, peut arriver à donner la réponse.</p>
<table border="1">
<tr>
<th>Temps</th>
<th>Requête du SELECT</th>
<th>Transaction du virement</th>
</tr>
<tr>
<th>t1</th>
<td>Lit la ligne 1, somme=500€</td>
<td></td>
</tr>
<tr>
<th>t2</th>
<td>Lit la ligne 2, somme=740,25€</td>
<td></td>
</tr>
<tr>
<th>t3</th>
<td></td>
<td>UPDATE de la ligne 1, verrou exclusif sur cette ligne. <br />Son montant est maintenant de 100€</td>
</tr>
<tr>
<th>t4</th>
<td>Lit la ligne N, somme=&#8230;</td>
<td></td>
</tr>
<tr>
<th>t5</th>
<td></td>
<td>UPDATE de la ligne 342023, verrou exclusif sur cette ligne. <br />Son montant est maintenant de 500€</td>
</tr>
<tr>
<th>t6</th>
<td>Essaie de lire la ligne 342023 et voit qu&rsquo;elle est verrouillée.<br />La session va attendre que elle redevienne disponible. <br /><strong>Tout le travail de cette requête s&rsquo;arrête.</strong></td>
<td></td>
</tr>
<tr>
<th>t7</th>
<td></td>
<td>COMMIT</td>
</tr>
<tr>
<th>t8</th>
<td>lit la ligne 342023, voit 500€ et donne le résultat final (qui inclut 2 fois deux fois les 400€)</td>
<td></td>
</tr>
</table>
<p>La première chose à remarquer, c&rsquo;est que cet autre SGBD va bloquer ma requête lorsque elle lit le compte 987. Ma session doit attendre que la transaction qui détient le verrou exclusif fasse un commit. C&rsquo;est pourquoi beaucoup de gens ont la mauvaise habitude de faire un <em>commit</em> à chaque requête, au lieu de faire des transaction bien construites, regroupant toutes les requêtes qui font passer la base de donnée d&rsquo;un état cohérent à un autre. Dans beaucoup de SGBD les mises à jour interfèrent avec les lectures. Mais le pire dans ce scénario, c&rsquo;est que l&rsquo;on fait attendre l&rsquo;utilisateur pour lui donner au final un mauvais résultat. J&rsquo;ai encore un résultat qui n&rsquo;a jamais existé en base de donnée, tout comme avec les lectures &lsquo;sales&rsquo;, mais cette fois je fais attendre l&rsquo;utilisateur pour lui donner cette mauvaise réponse.</p>
<p>Dans la suite, j&rsquo;étudierai ce que doivent faire les autres SGBD pour arriver à un résultat cohérent en lecture, un résultat juste.</p>
<p>Ce qu&rsquo;il faut retenir, c&rsquo;est que avec le même niveau d&rsquo;isolation, apparemment sans risque, des SGBD différent peuvent donner des résultats très différents dans les mêmes circonstances. Et ce cas n&rsquo;est pas seulement une possibilité, ce cas arrivera forcément. Il est important de comprendre qu&rsquo;avec le SGBD Oracle, les lectures non bloquantes ne se font pas au prix de mauvais résultats. Vous pouvez avoir le beurre et l&rsquo;argent du beurre, parfois.</p>
<p><strong>REPEATABLE READ. </strong></p>
<p>Le but de REPEATABLE READ est de fournir un niveau d&rsquo;isolation qui donne des résultats cohérents et justes, et qui évite les mises à jour perdues (<em>lost updates</em>). Je vais donner des exemples de ce que vous devez faire sous Oracle pour atteindre ce but, et étudier ce qu&rsquo;il se passe dans les autres SGBD. Si vous êtes en niveau d&rsquo;isolation REPEATABLE READ, le résultat d&rsquo;une requête donnée doit être cohérente par rapport à un instant donné. La plupart des SGBD (mais pas Oracle) accomplissent cela en utilisant des verrous partagés en lecture, au niveau enregistrement (<em>row-level shared read locks</em>). Un verrou partagé en lecture empêche les autres session de modifier les données que vous avez lu. Bien sûr, cela diminue la capacité multi-utilisateur (<em>concurrency</em>). Le SGBD Oracle a choisi le système de multi-versionnage, meilleur pour la concurrence d&rsquo;accès, pour fournir des résultats cohérents en lecture.</p>
<p>Avec le multi-versionnage d&rsquo;Oracle, le résultat de la requête est cohérent par rapport à l&rsquo;instant où la requête a démarré. Dans les autres SGBD, avec les verrous partagés en lecture, vous avez une réponse qui est cohérente par rapport à l&rsquo;instant de fin de la requête, c&rsquo;est à dire au moment où vous pouvez avoir le résultat complet (plus de détail là dessus un peu plus loin).</p>
<p>Dans un SGBD qui utilise les verrous partagés en lecture pour permettre les lectures répétables, vous verrez que les lignes des tables sont verrouillées au fur et à mesure que la requête les lit. Donc, en utilisant l&rsquo;exemple précédant, je vais laisser des verrous partagés en lecture (<em>shared read locks</em>) sur chaque ligne, comme le montre le scénario suivant:</p>
<table border="1">
<tr>
<th>Temps</th>
<th>Requête du SELECT</th>
<th>Transaction du virement</th>
</tr>
<tr>
<th>t1</th>
<td>Lit la ligne 1, somme=500€, Verrou partagé sur le bloc 1</td>
<td></td>
</tr>
<tr>
<th>t2</th>
<td>Lit la ligne 2, somme=740,25€, Verrou partagé sur le bloc 2</td>
<td></td>
</tr>
<tr>
<th>t3</th>
<td></td>
<td>Essaie l&rsquo;<em>update</em> de la ligne 1, mais voit qu&rsquo;elle est verrouillée. <br />La transaction doit attendre jusqu&rsquo;à ce qu&rsquo;elle puisse obtenir un verrou exclusif.</td>
</tr>
<tr>
<th>t4</th>
<td>Lit la ligne N, somme=&#8230;</td>
<td></td>
</tr>
<tr>
<th>t5</th>
<td>Lit la ligne 342023, voit 100€ et donne le résultat final.</td>
<td></td>
</tr>
<tr>
<th>t6</th>
<td>COMMIT</td>
<td></td>
</tr>
<tr>
<th>t7</th>
<td></td>
<td>UPDATE de la ligne 1, verrou exclusif sur ce bloc.<br />Son montant est maintenant de 100€</td>
</tr>
<tr>
<th>t8</th>
<td></td>
<td>UPDATE de la ligne 342023, verrou exclusif sur ce bloc. <br />Son montant est maintenant de 500€<br />COMMIT</td>
</tr>
</table>
<p>Avec ce scénario, j&rsquo;ai maintenant le résultat correct, mais au prix d&rsquo;avoir bloqué une transaction, et d&rsquo;avoir dû exécuter les transactions, l&rsquo;une après l&rsquo;autre. C&rsquo;est l&rsquo;un des effets de bord des verrous partagés en lecture: ceux qui lisent bloquent ceux qui écrivent. Cela se rajoute au fait que, dans ces systèmes, ceux qui écrivent bloquent ceux qui lisent. Imaginez si les Guichet Automatiques Bancaires fonctionnaient comme çà.</p>
<p>Vous voyez donc comment les verrous partagés en lecture inhibent la concurrence d&rsquo;accès, mais ils peuvent aussi être à l&rsquo;origine de fausses erreurs. Dans le scénario suivant, je part de la table originale, mais cette fois dans le but de transférer 50€ du compte 987 vers le compte 123.</p>
<table border="1">
<tr>
<th>Temps</th>
<th>Requête du SELECT</th>
<th>Transaction du virement</th>
</tr>
<tr>
<th>t1</th>
<td>Lit la ligne 1, somme=500€, Verrou partagé sur le bloc 1</td>
<td></td>
</tr>
<tr>
<th>t2</th>
<td>Lit la ligne 2, somme=740,25€, Verrou partagé sur le bloc 2</td>
<td></td>
</tr>
<tr>
<th>t3</th>
<td></td>
<td>UPDATE de la ligne 342023, verrou exclusif sur ce bloc, <br />bloquant les <em>update</em> et le verrous en lecture concurrents. <br />Son montant est maintenant de 50€</td>
</tr>
<tr>
<th>t4</th>
<td>Lit la ligne N, somme=&#8230;</td>
<td></td>
</tr>
<tr>
<th>t5</th>
<td></td>
<td>Essaie l&rsquo;<em>update</em> de la ligne 1, mais voit qu&rsquo;elle est verrouillée. <br />La transaction doit attendre jusqu&rsquo;à ce qu&rsquo;elle puisse obtenir un verrou exclusif.</td>
</tr>
<tr>
<th>t6</th>
<td>Essaie de lire la ligne 1, mais voit qu&rsquo;elle est verrouillée. <br />La transaction doit attendre jusqu&rsquo;à ce qu&rsquo;elle puisse obtenir un verrou exclusif.</td>
<td></td>
</tr>
</table>
<p>Et je suis arrivé dans la situation classique du verrou mortel (<em>deadlock</em>). Ma requête bloque des ressources dont l&rsquo;update a besoin, et vice versa. Ma requête est en <em>deadlock</em> avec la transaction. Une des deux doit être choisie pour être tuée. J&rsquo;ai attendu un long moment, et j&rsquo;ai utilisé beaucoup de ressources, juste pour être en erreur et finir par un <em>rollback</em>. C&rsquo;est le deuxième effet de bord des verrous partagés en lecture: ceux qui écrivent et ceux qui lisent peuvent être en <em>deadlock</em>, et c&rsquo;est le cas assez souvent.</p>
<p>Comme on l&rsquo;a vu, sous Oracle, vous avez une lecture cohérente au niveau de chaque requête sans que les lectures ne bloquent les écritures, ou ne fassent de <em>deadlock</em>. Le SGBD Oracle n&rsquo;utilise jamais de verrous partagés en lecture au niveau des lignes. Oracle a choisi le système de multi-versionnage, plus difficile à implémenter, mais infiniment meilleur en capacité de concurrence d&rsquo;accès.</p>
<p><strong>SERIALIZABLE</strong></p>
<p>C&rsquo;est le niveau généralement considéré comme le plus restrictif, mais c&rsquo;est celui qui fournit le plus haut degré d&rsquo;isolation. Une transaction SERIALIZABLE opère dans un environnement qui lui apparaît comme si il n&rsquo;y avait uncun autre utilisateur en train de modifier la base de données. Vous êtes garantis que chaque ligne que vous lisez sera la même si vous la relisez, et que chaque requête que vous exécutez aura le même résultat durant toute la vie de la transaction.</p>
<p>Si vous exécutez par exemple:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">select * from T; <br />
begin dbms_lock.sleep( 60*60*24 ); end; <br />
select * from T;</div></div>
<p>Les 2 réponses seront les mêmes, alors que vous avez attendu 24 heures (Mais vous pouvez aussi avoir une erreur ORA-1555, <em>snapshot too old</em>). Ce niveau d&rsquo;isolation vous assure que les 2 requêtes retournent le même résultat. Votre requête ne voit pas les effets de bord, ni les mises à jour faites par les autres transactions, et ce quel que soit la durée de son exécution.</p>
<p>Sous Oracle, l&rsquo;implémentation des transactions SERIALIZABLE est faite de telle sorte que la lecture cohérente que vous avez au niveau de la requête est étendue au niveau de la transaction. (Comme il est noté plus haut, il y a une niveau d&rsquo;isolation sous Oracle qui est appelé READ ONLY. Il a toutes les caractéristiques de SERIALIZABLE, sauf qu&rsquo;il empêche les mises à jour. Il faut noter aussi que le <em>user</em> SYS (ou les utilisateurs connectés en SYSDBA) ne peuvent pas faire des transactions READ ONLY ou SERIALIZABLE. SYS est spécial pour cela).</p>
<p>Au lieu que les résultats soient cohérents par rapport au début de la requête, ils sont déterminés par le début de la transaction. Autrement dit, Oracle va utiliser les <em>rollback segments</em> pour reconstruire les données telles qu&rsquo;elles étaient avant que votre transaction ne commence (le début de la transaction au lieu du début de la requête). C&rsquo;est une pensée assez profonde: la base connait par avance les réponses à toutes les questions que vous pouvez poser, avant que vous le les posiez.</p>
<p>Mais ce degré d&rsquo;isolation a un prix, vous pouvez vous retrouver avec l&rsquo;erreur suivante:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ERROR a la line 1: <br />
ORA-08177: Impossible de sérialiser l'accès pour cette transaction</div></div>
<p>Vous aurez ce message à chaque fois que vous essaierez de mettre à jour une ligne qui a été modifiée depuis le début de votre transaction. (Notez que Oracle essaie de faire cela au niveau ligne, mais vous pouvez recevoir l&rsquo;erreur ORA-08177 même si la ligne que vous voulez mettre à jour n&rsquo;a pas été modifiée. Cette erreur peut arriver si une autre ligne du même bloc a changé)</p>
<p>Oracle choisit une approche optimiste de la sérialisation: il parie sur le fait que les données que vous voulez mettre à jour dans votre transaction ne seront pas modifiées par une autre transaction. C&rsquo;est en principe le cas, et le pari est payant, surtout pour des transactions courtes, dans les systèmes transactionnels (OLTP). Si personne ne modifie vos données durant votre transaction, ce niveau d&rsquo;isolation garde le même degré de concurrence que sans transactions SERIALIZABLE, alors que dans les autres SGBD, il diminue souvent la capacité en concurrence d&rsquo;accès. Le revers de la médaille, c&rsquo;est que vous pouvez avoir l&rsquo;erreur ORA-08177 si le pari ne paie pas. Mais en y réfléchissant, c&rsquo;est un risque qui vaut le coup d&rsquo;être pris. Si vous utilisez le niveau SERIALIZABLE, vous ne devriez pas vous attendre à modifier les mêmes données que les autres transactions.</p>
<p>Si c&rsquo;est le cas, alors vous devriez utiliser <code class="codecolorer text default"><span class="text">SELECT ... FOR UPDATE</span></code> comme décrit dans le Chapitre 1 [de Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions]. Cela permet de sérialiser l&rsquo;accès. </p>
<p>Donc vous pouvez utiliser le niveau d&rsquo;isolation SERIALIZABLE si:</p>
<ul>
<li>il y a une forte probabilité que personne d&rsquo;autre ne modifie les mêmes données que vous</li>
<li>vous avez besoin de lecture cohérente au niveau de toute la transaction</li>
<li>vous faites des transactions courtes (pour avoir plus de chance de vérifier le 1er point)</li>
</ul>
<p>Mais, car il y a toujours un &lsquo;mais&rsquo;, vous devez comprendre ces différents niveaux d&rsquo;isolation et leur conséquences. Souvenez-vous, avec le niveau SERIALIZABLE, vous ne verrez aucune modification faite dans la base depuis le début de votre transaction, jusqu&rsquo;à ce que vous ne committiez. Les applications qui cherchent à vérifier leurs propres contraintes d&rsquo;intégrité, comme le planificateur de ressources du chapitre 1 [de Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions], doivent faire très attention à cela. Dans le Chapitre 1 le problème était que vous ne pouviez pas vérifier vos contrainte d&rsquo;intégrité dans un système multi-utilisateur, puisque vous ne pouvez pas voir les modifications faites par les sessions concurrentes, avant qu&rsquo;elles ne soient committées. En SERIALIZABLE, vous ne verrez toujours pas les modifications non committées, mais vous ne verrez pas non plus les modifications committées depuis le début de votre transaction.</p>
<p>Et pour finir, soyez prévenus que SERIALIZABLE ne veut pas dire que toutes les transactions des utilisateurs vont se comporter comme si elles avaient été exécutées les une après les autres, séquentiellement. Le niveau d&rsquo;isolation n&rsquo;impose pas qu&rsquo;il y ait une sorte d&rsquo;ordonnancement pour arriver au résultat de SERIALIZABLE. Les phénomènes expliqués précédemment, décrits par le standard SQL, n&rsquo;imposent pas cela. Ce dernier point est un concept qui est souvent mal compris, et une petite démonstration permet de l&rsquo;éclaircir.<br />
Le scénario suivant montre comment deux sessions se comportent au cours du temps. Les tables A et B sont vides au démarrage, créées par:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ops$tkyte@ORA10G&gt; create table a ( x int ); <br />
Table created. <br />
ops$tkyte@ORA10G&gt; create table b ( x int ); <br />
Table created.</div></div>
<p>Et voici le scénario:</p>
<table border="1">
<tr>
<th>Temps</th>
<th>Session 1</th>
<th>Session2</th>
</tr>
<tr>
<th>t1</th>
<td>alter session set isolation_level=serializable;</td>
<td></td>
</tr>
<tr>
<th>t2</th>
<td></td>
<td>alter session set isolation_level=serializable;</td>
</tr>
<tr>
<th>t3</th>
<td>insert into a select count(*) from b;</td>
<td></td>
</tr>
<tr>
<th>t4</th>
<td></td>
<td>insert into b select count(*) from a;</td>
</tr>
<tr>
<th>t5</th>
<td>commit;</td>
<td></td>
</tr>
<tr>
<th>t6</th>
<td></td>
<td>commit;</td>
</tr>
</table>
<p>A la fin de ce scénario, les tables A et B ont chacune une ligne avec la valeur 0. S&rsquo;il y avait eu un ordonnancement en série des transactions, je ne pourrais pas avoir 0 sur chaque table. Si la session 1 s&rsquo;était exécutée avant la session 2, la table B aurait la valeur 1. Et dans le cas contraire, c&rsquo;est la table A qui aurait eu la valeur 1. Mais ici, les deux tables ont la valeur zéro. Chaque transaction s&rsquo;est exécutée comme si elle était la seule transaction s&rsquo;exécutant à ce moment là dans la base. Quelle que soit le nombre de fois à la session 1 exécute sa requête, le résultat du count() est celui qui correspond aux données committées en base au moment t1. Même chose pour la session 2 où le résultat du count() sera toujours celui qui correspond aux données committées en base au moment t2.</p>
<blockquote>
<table>
<tr>
<td>
<a href="http://www.amazon.com/dp/1590595300?tag=seainoradbaon-20&amp;camp=14573&amp;creative=327641&amp;linkCode=as1&amp;creativeASIN=1590595300&amp;adid=1RSP51R8PA660WYGTYMH&amp;"><br />
<img src="http://asktom.oracle.com/pls/asktom/wwv_flow_file_mgr.get_file?p_security_group_id=822925097021874&amp;p_flow_id=100&amp;p_fname=expert_oracle_database_arch.jpg" /><br />
</a>
</td>
<td>
<ins>Le livre en Anglais de Tom Kyte dont est extrait l&rsquo;article original:<br />
Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions<br />
Apress (September 15, 2005)</p>
<p>La <a href="http://apress.com/book/view/1430229462">seconde édition</a> (mise à jour avec la 11g) va paraître bientôt.</ins>
</td>
</tr>
</table>
</blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
