<?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; Tom Kyte</title>
	<atom:link href="https://blog.developpez.com/pachot/category/traductions/tom-kyte/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>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>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>
		<item>
		<title>Fonctionnalités Objet-Relationnel d&#8217;Oracle, par Tom Kyte</title>
		<link>https://blog.developpez.com/pachot/tk_object_relational_features/</link>
		<comments>https://blog.developpez.com/pachot/tk_object_relational_features/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 21:09:49 +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 Tom Kyte à des questions sur les fonctionnalités Objet-Relationnel d&#8217;Oracle (L&#8217;article original en anglais se trouve sur AskTom ici). Tom Kyte donne son avis sur l&#8217;utilisation de ces fonctionnalités (Tables Relationnelles, vues Objet-Relationnel, &#8230; <a href="https://blog.developpez.com/pachot/tk_object_relational_features/">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 Tom Kyte à des questions sur les fonctionnalités Objet-Relationnel d&rsquo;Oracle <br />(L&rsquo;article original en anglais se trouve sur AskTom <a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2556347100346178079">ici</a>).</p>
<p> <strong>Tom Kyte</strong> donne son avis sur l&rsquo;utilisation de ces fonctionnalités (<strong>Tables Relationnelles</strong>, <strong>vues Objet-Relationnel</strong>, <strong>Tables Objet-Relationnel</strong>)<br />
</ins></p></blockquote>
<p><strong>Question:</strong></p>
<p>Bonjour Tom, </p>
<p>J&rsquo;ai quelques questions sur les fonctionnalités Objet-Relationnel d&rsquo;Oracle.</p>
<p>J&rsquo;ai la structure suivante:<br />
<span id="more-31"></span></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 TYPE adresse_typ AS OBJECT (ligne1 VARCHAR2(20), ville VARCHAR2(20), pays VARCHAR2(20)); <br />
&nbsp;<br />
CREATE TYPE telephone_typ AS OBJECT (prefixe VARCHAR2(10), num_tel VARCHAR2(10)); <br />
&nbsp;<br />
CREATE TYPE telephone_liste_typ AS TABLE OF telephone_typ; <br />
&nbsp;<br />
CREATE TYPE personne_typ AS OBJECT &nbsp;(nom VARCHAR2(20), prenom VARCHAR2(20), adresse adresse_typ, telephones telephone_liste_typ) NOT FINAL; <br />
&nbsp;<br />
CREATE TYPE personne_commercial_typ UNDER personne_typ (personne_commercial_id NUMBER, salaire NUMBER, nombre_de_ventes NUMBER); <br />
&nbsp;<br />
CREATE TYPE personne_client_typ UNDER personne_typ (personne_client_id NUMBER, total_achat NUMBER); <br />
&nbsp;<br />
CREATE TABLE personne_obj OF personne_typ NESTED TABLE telephones STORE AS telephones_tab;</div></div>
<p>Mes questions sont les suivantes: <br /><ins>[&#8230;]</ins></p>
<p><strong>Réponse:</strong></p>
<p>Je vous déconseille fortement de faire cela. Utilisez des tables relationnelles et créez des vues OR (<em>Object-Relational</em>) au dessus si vous le désirez, mais n&rsquo;utilisez pas de tables imbriquées (<em>nested tables</em>), ni de tables de type objet. Il y a une surcharge énorme (<em>overhead</em>) pour pouvoir fournir cette syntaxe alléchante.</p>
<p>Une table imbriquée (<em>nested table</em>), par exemple, ajoute 16 octets supplémentaires à la fois sur la table parent et la table fille, avec une contrainte unique sur le parent pour cette colonne de type RAW, et vous force donc à créer un index sur la colonne RAW de la table fille <ins>(voir commentaire de Mohamed Houri ci-dessous)</ins>. Et pourtant vous n&rsquo;avez besoin de rien de tout ça.</p>
<p>Donc, faites un modèle relationnel approprié, et utilisez-le.</p>
<p>Je suis sûr 100% que vous voudrez un jour interroger la table des adresses, pour des requêtes du genre &lsquo;trouver tous ceux qui vivent dans le pays XXX&rsquo;. Si vous utilisez une <em>nested table</em>, vous n&rsquo;avez plus de vraie table. Les modèles objet ont une portée excessivement réduite. Ils présentent un unique point de vue sur les données, et il y a toujours de nombreuses vues possibles sur les données.</p>
<p>Et pour faire une recherche genre &lsquo;annuaire inversé&rsquo; à partir d&rsquo;un numéro de téléphone ? Pas si facile avec un modèle Object-Relationnel, et pourtant banal avec un modèle relationnel approprié.</p>
<p>C&rsquo;est donc un grande non-réponse à vos questions, mais il faut voir le côté positif: d&rsquo;après moi, vous étiez en train de faire une grosse erreur.</p>
<p><strong>Autre question:</strong></p>
<p><ins>&#8230;</ins>J&rsquo;ai l&rsquo;impression que que le modèles Orienté Objet et le modèle Relationnel ne se mélangent pas très bien<ins>&#8230;</ins></p>
<p><strong>Réponse:</strong></p>
<p>Non, pas du tout. Au contraire.</p>
<p>Mais ce qui est très important, c&rsquo;est que vous devez commencer par bâtir une bonne base relationnelle, et construire les vues orienté objet <strong>par dessus</strong>.</p>
<p>Je dis que les vues orientées objets sont de faibles portées (<em>&lsquo;myopic&rsquo;</em>). Pourquoi ? Parce qu&rsquo;elles ne sont focalisées que sur l&rsquo;application et non pas sur les données.</p>
<blockquote><p><ins>La suite est sur une autre page de AskTom (<a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1545206281987">ici</a>) sur la comparaison entre le Relationnel et l&rsquo;Orienté Objet.<br />
</ins></p></blockquote>
<p>Tout d&rsquo;abord, sous Oracle, vous devez comparer le Relationnel avec l&rsquo;Objet-Relationnel, et non pas avec l&rsquo;Orienté Objet. Il y a des extensions objet au relationnel, mais ce n&rsquo;est pas une base orientée objet.</p>
<p>Pour répondre à cette question, il faut regarder les données avec lesquelles vous travaillez. Disons que vous êtes en train de construire une application bancaire. Vous faites cette application pour le caissier qui s&rsquo;occupe du client quand il rentre dans la banque. L&rsquo;objet parfait pour cette application est &lsquo;Client&rsquo;, identifié par un nom ou un numéro de compte. Le client a tous les attributs d&rsquo;un client de banque: un nom, une adresse, une liste de comptes avec tous les attributs des comptes, une liste des dernières opérations bancaires, etc. Un gros paquet de données qui représentent toutes les informations d&rsquo;un client.<br />
C&rsquo;est parfait du point de vue du caissier. MAIS, à la fin de la journée, il y a quelqu&rsquo;un qui a une vision complètement différente des données, et qui a besoin de compter le nombre d&rsquo;opérations bancaires qu&rsquo;il y a eu dans la journée, ou combien d&rsquo;argent il y a dans la banque. Sa perspective sur les données est orientée sur les opérations et non sur les clients.<br />
Dans ce cas, le modèle parfait est un modèle RELATIONNEL, avec éventuellement des vues spécifiques objet-relationnel. Vous avez besoin d&rsquo;une table des comptes, qui est liée à une table des opérations et à une table des clients. Avec ça, celui qui veut compter les opérations est aussi satisfait que celui qui a une vue orienté client.</p>
<p>C&rsquo;est cela la puissance du modèle relationnel: il est capable de donner des vues différentes sur les mêmes données. Pensez aux tables EMP et DEPT de scott/tiger. Il peut paraître censé de créer un <em>type</em> objet EMP, une <em>nested table</em> de type EMP, un objet DEPT avec un attribut <em>nested table</em> de EMP et enfin une table des types DEPT. Et maintenant, toutes les données de démo scott/tiger tiennent dans une table de 4 lignes.<br />
Cela parait formidable. Mais le problème est que des gens vont poser les questions suivantes:</p>
<ul>
<li>Combien ai-je d&rsquo;employés ?</li>
<li>KING est là devant la porte, est-ce un employé ou pas ?</li>
<li>Quelle est somme des salaires des comptables sur l&rsquo;année dernière ?</li>
</ul>
<p>etc.</p>
<p>Avec une vision des données orientée sur DEPT, on ne peut répondre facilement à aucune de ces questions.</p>
<p>Juste de quoi y réfléchir. Je n&rsquo;ai jamais utilisé les concepts objet-relationnel comme un mécanisme de STOCKAGE. Je les ai beaucoup utilisé dans mes programmes PL/SQL. Je tend à rester sur des vraies tables et à utiliser des objets fréquemment dans le code.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ecritures cohérentes &#8211; conséquences pour le développeur, par Tom Kyte (3ème partie)</title>
		<link>https://blog.developpez.com/pachot/tk_consistent_write_3/</link>
		<comments>https://blog.developpez.com/pachot/tk_consistent_write_3/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 19:03:00 +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;un article de Tom Kyte sur son blog (L&#8217;article original en anglais se trouve ici). Lectures cohérentes et multi-versionnage, par Tom Kyte Il est conseillé lire préalablement les premières partie: Ecritures cohérentes, par Tom Kyte &#8230; <a href="https://blog.developpez.com/pachot/tk_consistent_write_3/">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 sur son blog (L&rsquo;article original en anglais se trouve <a href="http://tkyte.blogspot.com/2005/09/part-iii-why-is-restart-important-to.html">ici</a>).<br />
<a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">Lectures cohérentes et multi-versionnage, par Tom Kyte</a><br />
<br />
</ins><br />
Il est conseillé lire préalablement les premières partie:
<ul>
<li><a href="http://blog.developpez.com/pachot/p9043/auteurs/tom-kyte/tk-consistent-write/">Ecritures cohérentes, par Tom Kyte (1ère partie)</a></li>
<li><a href="http://blog.developpez.com/pachot/p9071/auteurs/tom-kyte/tk-consistent-write-2/">Ecritures cohérentes &#8211; observation d&rsquo;un redémarrage (2ème partie)</a></li>
</ul>
<p>
</p></blockquote>
<p>La première chose qui vient à l&rsquo;esprit, c&rsquo;est que le <em>trigger</em> se déclenche deux fois. On avait une table avec une seule ligne, et un <em>trigger</em>  BEFORE FOR EACH ROW qui se déclanche pour chaque ligne. Un a fait un update sur une seule ligne et le trigger s&rsquo;est déclanché deux fois.</p>
<p>Vous devez penser aux conséquentes de cela. Si vous faites dans votre trigger quoi que ce soit qui ne soit pas transactionnel, vous pouvez avoir un problème assez sérieux.<br />
<span id="more-33"></span><br />
Par exemple, imaginez un trigger qui envoie un e-mail avec dans le texte de l&rsquo;e-mail quelque chose comme: &laquo;&nbsp;Voilà comment étaient les données, elles ont été mises à jour et voici la nouvelle valeur.&nbsp;&raquo; Si cet e-mail est envoyé directement à partir du trigger, en utilisant UTL_SMTP sous Oracle 9i ou UTL_MAIL depuis Oracle 10g, alors le destinataire va recevoir deux e-mails, dont un qui avertit d&rsquo;une mise à jour qui n&rsquo;a jamais eu lieu.</p>
<p>L&rsquo;<em>update restart</em> a un impact dans tout ce que vous pouvez faire de non-transactionnel dans un trigger. Réflechissez aux consequences suivantes:</p>
<ul>
<li>Prenons un trigger qui maintient quelques variables globales PL/SQL, du genre le nombre de lignes traitées. Lorsque un update qui redémarre fait son rollback, les modifications faites sur les variables PL/SQL ne sont pas rollbackées, elles.</li>
<li>Pratiquement toutes les fonctions qui commencent par UTL_ (UTL_FILE, UTL_HTTP, UTL_SMTP etc) doivent être vues comme sensibles à un redémarrage de requête. Lors du redémarrage, ULT_FILE ne va pas revenir sur ce qu&rsquo;il a écrit dans le fichier.</li>
<li>Un trigger qui fait partie d&rsquo;une transaction autonome (<em>autonomous transaction</em>) doit être suspect. Lorsque la requête redémarre, la transaction autonome ne peut pas être rollbackée.</li>
</ul>
<p>Toutes ces conséquence doivent être gérées avec attention, en prenant compte du fait qu&rsquo;elles peuvent être déclenchées plus d&rsquo;une fois par ligne, ou être déclenchées par une ligne qui n&rsquo;est au final pas modifiée par la requête.</p>
<p>La deuxième raison pour laquelle vous devez prêter attention aux redémarrages potentiels est une raison de performance. Dans notre exemple, nous avons utilisé une table avec une seule ligne. Mais qu&rsquo;arrive-t-il si vous lancez un update de toute une table et qu&rsquo;il redémarre après avoir mis à jour les 100000 premiers enregistrements ? Il va faire un rollback des 100000 modifications, redémarrer en mode SELECT FOR UPDATE, puis refaire à nouveau les 100000 modifications. </p>
<p>Vous pourriez remarquer une dégradation des performance au delà de ce que vous attendiez, après avoir mis un simple trigger qui trace toutes les mises à jour à l&rsquo;aide de :old et :new. Et ce serait parce vous avez des requête qui redémarrent alors que ce n&rsquo;était pas le cas avant. Ou bien l&rsquo;ajout d&rsquo;un tout petit programme qui fait un update d&rsquo;une seule une ligne, et qui fait qu&rsquo;un batch se met à durer des heures à cause d&rsquo;un <em>restart</em> qui n&rsquo;avait pas lieu avant.</p>
<p>Ce n&rsquo;est pas une nouvelle fonctionnalité d&rsquo;Oracle. Çà existe depuis la version 4.0, lorsque la lecture cohérente (<em>read consistency</em> a été introduite. Je n&rsquo;étais moi-même pas au courant de cela avant l&rsquo;été 2003, et lorsque j&rsquo;ai découvert les conséquences, j&rsquo;ai pu amener des réponses a des questions que je m&rsquo;étais posé dans le passé.<br />
Çà m&rsquo;a fait jurer de renoncer presque complètement au transactions autonomes dans les triggers, et à repenser la manière dont j&rsquo;avais implémenté certaines de mes applications. Par exemple, je ne vais jamais envoyer un e-mail directement à partir d&rsquo;un <em>trigger</em>, mais plutôt utiliser DBMS_JOB qui va l&rsquo;envoyer après que ma transaction soit commitée. Cela rend transactionnel l&rsquo;envoi d&rsquo;e-mail car si la requête qui a déclanché le trigger doit être redémarrée, alors le <em>rollback</em> va aussi annuler la soumission du job par DBMS_JOB.<br />
J&rsquo;ai modifié tout ce qui n&rsquo;était pas transactionnel dans les <em>triggers</em> pour le faire dans un <em>job</em>, afin de le rendre transactionnellement cohérent.</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>0</slash:comments>
		</item>
		<item>
		<title>Ecritures cohérentes &#8211; observation d&#8217;un redémarrage, par Tom Kyte (2ème partie)</title>
		<link>https://blog.developpez.com/pachot/tk_consistent_write_2/</link>
		<comments>https://blog.developpez.com/pachot/tk_consistent_write_2/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 18:32:40 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Tom Kyte]]></category>
		<category><![CDATA[restart]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;un article de Tom Kyte sur son blog (L&#8217;article original en anglais se trouve ici). Il est conseillé lire préalablement la première partie: Ecritures cohérentes, par Tom Kyte (1ère partie) Voir un redémarrage d&#8217;une requête &#8230; <a href="https://blog.developpez.com/pachot/tk_consistent_write_2/">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 sur son blog (L&rsquo;article original en anglais se trouve <a href="http://tkyte.blogspot.com/2005/08/part-ii-seeing-restart.html">ici</a>).<br />
<br />
</ins><br />
Il est conseillé lire préalablement la première partie:
<ul>
<li><a href="http://blog.developpez.com/pachot/p9043/auteurs/tom-kyte/tk-consistent-write/">Ecritures cohérentes, par Tom Kyte (1ère partie)</a></li>
</ul>
</blockquote>
<p>Voir un redémarrage d&rsquo;une requête <i>update</i> est plus plus facile qu&rsquo;on ne pense.<br />
En fait, nous allons même en voir un avec une simple table d&rsquo;une seule ligne.<br />
<span id="more-34"></span><br />
Voici la table que nous allons utiliser pour notre test:</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&amp;gt; create table t ( x int, y int ); <br />
Table created. <br />
&nbsp;<br />
ops$tkyte@ORA10G&amp;gt; insert into t values ( 1, 1 ); <br />
1 row created. <br />
&nbsp;<br />
ops$tkyte@ORA10G&amp;gt; commit; <br />
Commit complete.</div></div>
<p>Pour observer le <em>restart</em>, nous avons juste besoin d&rsquo;un <em>trigger</em> qui va afficher des informations. Nous allons utiliser un trigger BEFORE UPDATE FOR EACH ROW qui va simplement afficher l&rsquo;ancienne et la nouvelle valeur de la ligne lors d&rsquo;un  update.</p>
<pre>ops$tkyte@ORA10G&gt; create or replace trigger t_bufer
  2  before update on t for each row
  3  begin
  4          dbms_output.put_line
  5          ( 'old.x = ' || :old.x ||
  6            ', old.y = ' || :old.y );
  7          dbms_output.put_line
  8          ( 'new.x = ' || :new.x ||
  9            ', new.y = ' || :new.y );
10  end;
11  /
Trigger created.</pre>
<p>Et maintenant nous faisons un update sur la ligne:</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&amp;gt; set serveroutput on <br />
ops$tkyte@ORA10G&amp;gt; update t set x = x+1; <br />
old.x = 1, old.y = 1 <br />
new.x = 2, new.y = 1 <br />
1 row updated.</div></div>
<p>jusqu&rsquo;ici, tout ce passe comme on s&rsquo;y attend: le trigger s&rsquo;est déclenché une fois, et nous voyons les anciennes et nouvelles valeurs. Remarquez que nous n&rsquo;avons pas encore fait de <em>commit</em>, la ligne est toujours vérouillée. Dans une autre session, nous allons faire l&rsquo;update suivant:</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&amp;gt; set serveroutput on <br />
ops$tkyte@ORA10G&amp;gt; update t set x = x+1 where x &amp;gt; 0;</div></div>
<p>La requête se bloque tout de suite, évidemment, puisque la première session a verrouillé la ligne. Si nous retournons dans la première session, pour faire un commit, voici ce qu&rsquo;affiche la deuxième session (j&rsquo;ai remis l&rsquo;<em>update</em> pour que ça soit plus clair)</p>
<pre>ops$tkyte@ORA10G&gt; update t set x = x+1 where x &gt; 0;
old.x = 1, old.y = 1
new.x = 2, new.y = 1
old.x = 2, old.y = 1
new.x = 3, new.y = 1
1 row updated.</pre>
<p>Comme vous pouvez le voir, le <em>trigger</em> a vu deux version de la ligne ici. Le <em>trigger</em> sur la ligne s&rsquo;est déclenché deux fois:
<ul>
<li>Une première fois avec la version d&rsquo;origine de la ligne, et les nouvelles valeurs calculées par l&rsquo;update</li>
<li>et une deuxième fois avec la version finale de la ligne telle qu&rsquo;elle a réellement été modifiée.</li>
</ul>
<p>Du fait que c&rsquo;est un trigger BEFORE FOR EACH ROW, Oracle a vu la version cohérente (<em>read-consistent</em>) de notre ligne et la mise à jour que nous souhaitons y faire. Mais c&rsquo;est après le déclenchement du trigger BEFORE FOR EACH ROW qu&rsquo; Oracle a récupéré le block en mode courant (<em>current mode</em>) pour y faire la mise à jour. Oracle doit attendre la fin du déclenchement du trigger parce que le trigger peut modifier les valeurs <strong>:new</strong>. Donc, Oracle ne peut modifier le bloc qu&rsquo;une fois que le trigger ait fini de s&rsquo;exécuter, et cela peut prendre du temps. Vu qu&rsquo;un bloc ne peut être pris en mode courant que par une seule session à la fois, Oracle doit limiter le temps passé dans ce mode.</p>
<p>Donc après que le trigger se soit exécuté, Oracle prends le bloc en mode courant et s&rsquo;aperçoit que la colonne X, qui a été utilisée pour trouver la ligne, n&rsquo;a plus la même valeur. Et comme X, qui a été utilisé pour trouver cette ligne, a été modifiée, alors la base de données décide de redémarrer notre requête.</p>
<p>Notez que le fait que la valeur de X soit passée de 1 à 2 ne fait pas sortir la ligne du périmètre de la requête. Elle va quand même être touchée par notre update. C&rsquo;est plutôt le fait que X ait été utilisé pour trouver la ligne, et que la valeur en lecture cohérente (ici 1) soit différente de la valeur en lecture courante (ici 2). Et ensuite, lors du <em>restart</em>, le trigger voit la valeur X=2 (qui vient de la modification faite par l&rsquo;autre session) comme valeur de <strong>:old</strong> et la valeur X=3 pour <strong>:new</strong></p>
<p>Donc cela prouve qu&rsquo;un <em>update restart</em> a eu lieu. Il a fallu un trigger pour le voir, sinon ils sont en généralement indétectables. Cela ne veut pas dire qu&rsquo;on ne peut pas voir d&rsquo;autres symptômes, comme un gros update qui doit faire un <em>rollback</em> de toutes les modifications effectuées lorsqu&rsquo;il a découvert qu&rsquo;une ligne provoqué le <em>restart</em>. Cela veut juste dire qu&rsquo;il est très difficile de pouvoir affirmer que ce type de symptôme vient du <em>restart</em>.</p>
<p>Il y a une autre chose intéressante, c&rsquo;est que les <em>triggers</em> eux-mêmes peuvent être la cause d&rsquo;un restart, même si la requête en elle-même ne le demande pas. Normalement, c&rsquo;est les colonnes qui sont utilisées dans la clause WHERE d&rsquo;un UPDATE ou DELETE qui sont utilisées pour déterminer si les modifications nécessitent un redémarrage de la requête. Oracle va faire une lecture en <em>consistent read</em> et lorsqu&rsquo;il prends le bloc en <em>current mode</em>, il va redémarrer la requête si l&rsquo;une de ces colonnes a été modifiée. Normalement, les autres colonnes ne sont pas comparées. Par exemple, si nous ré-exécutons la l&rsquo;exemple précédant en mettant WHERE Y&gt;0 pour trouver la ligne au lieu de X&gt;0 :</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&amp;gt; update t set x = x+1 where y &amp;gt; 0; <br />
old.x = 1, old.y = 1 <br />
new.x = 2, new.y = 1 <br />
old.x = 2, old.y = 1 <br />
new.x = 3, new.y = 1 <br />
1 row updated.</div></div>
<p>On peut se demander pourquoi Oracle encore  déclenché le trigger deux fois alors qu&rsquo;il regardait la valeur de Y. Et on pourrait même croire que toutes les colonnes de la ligne sont examinées. Comme on peut le voir, ici on a eu un update restart alors qu&rsquo;on filtrait les lignes avec Y&gt;0 et qu&rsquo;on a jamais modifié Y.</p>
<p>Mais si on recrée le trigger en affichant juste un message &lsquo;<em>fired</em>&lsquo; pour dire qu&rsquo;il a été déclanché, sans faire référence à <strong>:new</strong> et <strong>:old</strong></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&amp;gt; create or replace trigger t_bufer <br />
&nbsp; 2 &nbsp;before update on t for each row <br />
&nbsp; 3 &nbsp;begin <br />
&nbsp; 4 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dbms_output.put_line( 'fired' ); <br />
&nbsp; 5 &nbsp;end; <br />
&nbsp; 6 &nbsp;/ <br />
Trigger created. <br />
&nbsp;<br />
ops$tkyte@ORA10G&amp;gt; update t set x = x+1; <br />
fired <br />
1 row updated.</div></div>
<p>et que l&rsquo;on va à nouveau dans la deuxième session pour exécuter l&rsquo;update, on voit qu&rsquo;il bloque, bien sûr. Et après avoir fait le commit sur la première session, la deuxième session se débloque et voici ce qu&rsquo;elle affiche:</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&amp;gt; update t set x = x+1 where y &amp;gt; 0; <br />
fired <br />
1 row updated.</div></div>
<p>Le trigger ne s&rsquo;est déclenché qu&rsquo;une seule fois, pas deux.</p>
<p>Cela montre que les valeurs des colonnes référencées par :new et :old dans le trigger sont aussi utilisées par Oracle pour vérifier s&rsquo;il faut faire un restart.<br />
Lorsque le trigger référencait :new.X et :old.X alors les valeurs cohérentes et courantes de X ont été comparées. Et cela a entraîner un restart. Mais lorsqu&rsquo;on a enlevé ces références, le restart n&rsquo;a plus eu lieu.</p>
<p>La règle est que c&rsquo;est l&rsquo;ensemble des colonnes utilisées dans la clause WHERE, plus les colonnes référencées dans les triggers ligne (<em>row trigger</em>) qui sont comparées. La version cohérente et la version courante de la ligne vont être comparées, et si une de ces colonnes a changé, alors la requête va redémarrer.</p>
<p>A noter que cela peut aider à comprendre pourquoi un trigger AFTER FOR EACH ROW est plus performant qu&rsquo;un trigger BEFORE FOR EACH ROW. Le trigger AFTER n&rsquo;a pas les memes conséquences. Ce qui nous amène à la question &lsquo;En quoi est-ce que ce comportement interne nous concerne ?&rsquo;.</p>
<p>A suivre&#8230;</p>
<blockquote><p><ins>La troisième partie sera traduite prochainement:
<ul>
<li><a href="http://blog.developpez.com/pachot/p9070/auteurs/tom-kyte/tk-consistent-write-3/">Ecritures cohérentes &#8211; conséquences pour le développeur, par Tom Kyte</a></li>
</ul>
<p></ins></p></blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ecritures cohérentes, par Tom Kyte (1ère partie)</title>
		<link>https://blog.developpez.com/pachot/tk_consistent_write/</link>
		<comments>https://blog.developpez.com/pachot/tk_consistent_write/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 19:01:00 +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;un article de Tom Kyte sur son blog (L&#8217;article original en anglais se trouve ici). L&#8217;article est en 3 parties (il s&#8217;agit ici de la partie 1) et il est extrait de son livre Expert &#8230; <a href="https://blog.developpez.com/pachot/tk_consistent_write/">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 sur son blog (L&rsquo;article original en anglais se trouve <a href="http://tkyte.blogspot.com/2005/08/something-different-part-i-of-iii.html">ici</a>). L&rsquo;article est en 3 parties (il s&rsquo;agit ici de la partie 1) et il est extrait de son livre <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=14FEKWC7ZG5FTJ8QT0ZP&amp;">Expert Oracle Database Architecture</a>.<br />
<br />
Il peut être utile de (re)lire avant:
<ul>
<li><a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">Lectures cohérentes et multi-versionnage, par Tom Kyte</a></li>
<li><a href="http://blog.developpez.com/pachot/p8673/concepts/jl-consistent-gets/">Statistiques &lsquo;db block gets&rsquo; et &lsquo;consistent gets&rsquo;, par Jonathan Lewis</a></li>
</ul>
<p></ins></p></blockquote>
<p>Nous avons étudié précédemment <ins>(en français <a href="http://blog.developpez.com/pachot/p8813/auteurs/tom-kyte/tk-consistent-read/">ici</a>)</ins> la lecture cohérente: la capacité d&rsquo;Oracle à utiliser les informations d&rsquo;<em>undo</em> pour fournir des requêtes non-bloquantes et cohérentes (c&rsquo;est à dire correctes) pour une lecture. Nous savons que lorsque Oracle lit des blocs pour notre requête, dans le buffer cache, il s&rsquo;assure que la version du bloc est assez &lsquo;vieille&rsquo; pour que notre requête puisse le voir.</p>
<p>Mais cela soulève la question suivante: Qu&rsquo;en est-il des écritures/modifications ? Qu&rsquo;est-ce qui se passe lorsque vous exécutez l&rsquo;instruction UPDATE suivante: <code class="codecolorer text default"><span class="text">UPDATE T SET X = 2 WHERE Y = 5;</span></code><br />
et que, pendant que cette requête est en cours d&rsquo;exécution, quelqu&rsquo;un modifie une ligne en faisant passer la valeur de Y de 5 vers 6, et commite cette modification, avant que nôtre requête n&rsquo;ait lu cette ligne ?</p>
<p>Autrement dit, lorsque votre mise à jour a commencé, certaines lignes ont Y=5. Et comme votre mise à jour fait des lectures cohérentes, elle voit pour cette ligne Y=5, puisque c&rsquo;est la valeur qu&rsquo;il y avait au moment où votre <em>update</em> a démarré. Cependant, la valeur actuelle de Y est maintenant 6, et non plus 5, et avant de mettre à jour la valeur de X, Oracle va vérifier si la valeur de Y est toujours 5. Maintenant que doit-il se passer ? Et comment cela impacte les <em>update</em> ?</p>
<p>De toute évidence, nous ne pouvons pas modifier une ancienne version d&rsquo;un bloc: quand on va modifier une ligne, il faut modifier la version actuelle du bloc. En outre, Oracle ne peut pas simplement ignorer cette ligne, car ce serait une lecture incohérente et imprévisible. Ce que nous allons découvrir, c&rsquo;est que dans ce cas, Oracle va redémarrer à zéro l&rsquo;écriture des modification.<br />
<span id="more-32"></span></p>
<p><strong>Lectures cohérentes (<em>Consistent Reads</em>) et lectures courantes (<em>Current Reads</em>)</strong></p>
<p>Oracle peut faire 2 types de lectures de blocs (<em>block gets</em>) lorsqu&rsquo;il execute un requête de mise à jour <ins>(voir aussi cette <a href="http://blog.developpez.com/pachot/p8673/concepts/jl-consistent-gets/">traduction</a> de Jonathan Lewis)</ins>. Il fait:</p>
<ul>
<li>Des lectures cohérentes (<em>Consistent reads</em>): pour &lsquo;chercher&rsquo; les lignes à modifier</li>
<li>Des lectures courantes (<em>Current reads</em>): pour prendre le bloc dans lequel il y a les lignes à modifier.
</li>
</ul>
<p>On peut voir cela facilement avec TKPROF. Considerons le petit exemple suivant, qui lit et modifie l&rsquo;unique ligne de la table T qu&rsquo;on a déjà vu:</p>
<pre>ORA10GR1> alter session set sql_trace=true;
Session altered.

ops$tkyte@ORA10GR1> select * from t;

X
----------
10001

ops$tkyte@ORA10G> update t t1 set x = x+1;
1 row updated.

ops$tkyte@ORA10G> update t t2 set x = x+1;
1 row updated.</pre>
<p>TKPROF donne le résultat suivant (j&rsquo;ai supprimé les colonnes ELAPSED, CPU, and DISK):</p>
<pre>select * from t

call    count query current rows
------- ----- ----- ------- ----
Parse       1     0       0    0
Execute     1     0       0    0
Fetch       2     3       0    1
------- ----- ----- ------- ----
total       4     3       0    1

update t t1 set x = x+1

call    count query current rows
------- ----- ----- ------- ----
Parse       1     0       0    0
Execute     1     3       3    1
Fetch       0     0       0    0
------- ----- ----- ------- ----
total       2     3       3    1

update t t2 set x = x+1

call    count query current rows
------- ----- ----- ------- ----
Parse       1     0       0    0
Execute     1     3       1    1
Fetch       0     0       0    0
------- ----- ----- ------- ----
total       2     3       1    1</pre>
<p>Ainsi, pour un select normal, nous avons 3 lectures cohérentes (<em>query</em> ou <em>consistent gets</em>). Pour le premier UPDATE, nous avons les 3 mêmes lectures (car ici la partie de recherche des lignes doit trouver toutes les lignes qui sont dans la table au moment où la requête démarre), mais aussi 3 lectures courantes.<br />
Les 3 lectures en mode courant (<em>current mode gets</em>) servent</p>
<ol>
<li>à accéder au bloc courant, tel qu&rsquo;il existe actuellement, celui avec la ligne à modifier</li>
<li>à accéder à un bloc de segment d&rsquo;undo pour commencer notre transaction (l&rsquo;enregistrer dans la table des transactions)</li>
<li>à accéder à un bloc d&rsquo;undo (pour écrire les informations d&rsquo;undo)</li>
</ol>
<p>Le deuximème UPDATE ne fait qu&rsquo;une lecture en mode courant, car nous n&rsquo;avons pas à répeter le travail pour les blocs d&rsquo;undo. Il n&rsquo;y a que le bloc avec la ligne à mettre à jour. La présence du <em>current get</em> nous indique qu&rsquo;une modification, de quelque sorte qu&rsquo;elle soit, a eu lieu. Avant de modifier un bloc avec une nouvelle donnée, il doit avoir sa version la plus récente.</p>
<p>Alors comment la lecture cohérente peut affecter une mise à jour ? Bien, imaginons maintenant l&rsquo;UPDATE suivant:</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">Update t set x = x+1 where y = 5;</div></div>
<p>Nous savons que la clause WHERE Y=5 , qui est la partie qui doit être cohérente en lecture, va être faite en mode cohérent (<em>consistent</em>, ou <em>query</em> dans TKPROF). Les enregistrements que voit la requête sont ceux qui ont la valeur Y=5, commitée, au moment où la requête démarre (en supposant qu&rsquo;on est en niveau d&rsquo;isolation <em>READ COMMITTED</em> sinon, en <em>SERIALIZABLE</em>, ce seraient ceux qui ont la valeur Y=5, commitée, au moment où la transaction démarre). Cela signifie que si la requête dure 5 minutes, du début à la fin, et qu&rsquo;entre temps quelqu&rsquo;un insère et commite un nouvel enregistrement qui a la valeur Y=5, alors notre UPDATE ne verra pas cet enregistrement parce que la lecture cohérente ne le lira pas. C&rsquo;est le comportement attendu et normal. Mais la question qui se pose est maintenant: que se passe-t-il si deux sessions exécutent les requêtes suivantes, dans l&rsquo;ordre::</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">Update t set y = 10 where y = 5; <br />
Update t Set x = x+1 Where y = 5;</div></div>
<p>Voici ce qu&rsquo;il se passe en sequence:</p>
<ol>
<li> <strong>Session 1:</strong> <code class="codecolorer text default"><span class="text">Update t set y = 10 where y = 5;</span></code><br />
mise à jour de la ligne qui correspond au critère.</li>
<li><strong>Session 2</strong>: <code class="codecolorer text default"><span class="text">Update t Set x = x+1 Where y = 5;</span></code><br />
Du fait de la lecture cohérente, la ligne modifiée par la session 1 correspond au critère (la session 1 n&rsquo;a pas encore commité) mais ne peut pas la mettre à jour car elle est vérouillée par la session 1. La session 2 est bloquée et attends.</li>
<li><strong>Session 1</strong>: <code class="codecolorer text default"><span class="text">Commit;</span></code><br />
Cela libère le verrou sur lequel attends la session 2, qui se débloque.<br />
Elle peut alors faire la lecture courante du bloc qui contient l&rsquo;enregistrement pour lequel Y=5 au moment où la requête a démarré.</li>
</ol>
<p>Mais voilà que l&rsquo;enregistrement qui avait Y=5 au moment du démarrage de l&rsquo;UPDATE n&rsquo;a plus la valeur Y=5. La lecture cohérente nous dit qu&rsquo;il faut mettre à jour cette ligne parce que Y=5 au moment où la requête a démarrée. Mais la version courante du bloc nous dit que non, ce serait une erreur, on ne peut pas mettre à jour cette ligne puisque maintenant Y n&rsquo;est plus égal à 5.</p>
<p>Si nous décidions juste de passer cette ligne et de l&rsquo;ignorer, alors l&rsquo;UPDATE ne serait pas deterministe. Ce serait jeter à la poubelle la cohérence et l&rsquo;intégrité des données. Le résultat de l&rsquo;UPDATE (combien de lignes et lesquelles sont modifiées) dépendrait de l&rsquo;ordre dans lequel on traite les lignes de la table, et des autres activités qu&rsquo;il y a autour. Vous pourriez prendre le même jeu de données dans 2 bases différentes, chacune exécutant les mêmes transactions dans le même ordre, et vous auriez des résultats différentes, simplement parce que les enregistrements sont placés différemment sur le disque.</p>
<p>Alors dans cette situation, Oracle choisit de redémarrer l&rsquo;UPDATE. Lorsque il voit que la ligne qui avait Y=5 au début de la requête a maintenant la valeur Y=10, alors Oracle, si on est en niveau d&rsquo;isolation READ COMMITTED, va silencieusement faire un rollback des modifications faites et redémarrer l&rsquo;UPDATE. En niveau d&rsquo;isolation SERIALIZABLE, alors on aurait un message d&rsquo;erreur<code class="codecolorer text default"><span class="text">&nbsp;ORA-08177 Impossible de sérialiser l'accès pour cette transaction.</span></code> (<em>can&rsquo;t serialize access for this transaction</em>). En READ COMMITTED, après avoir rollbacké les modifications, Oracle va relancer l&rsquo;update (donc le moment du démarrage de la requête, pour la lecture cohérente, n&rsquo;est plus le même). Mais cette fois il va faire un SELECT FOR UPDATE en essayant de verrouiller toutes les lignes pour lesquelles Y=5. Une fois que c&rsquo;est fait, il va faire l&rsquo;UPDATE sur ces lignes qui sont verrouillées par notre session, étant sûr maintenant de pouvoir terminer sans avoir à redémarrer.</p>
<p>Continuons avec les questions &lsquo;que se passe-t-il ?&rsquo;. Le SELECT FOR UPDATE utilise les mêmes lectures cohérentes (pour trouver les enregistrements) et lectures courantes (pour les verrouiller) que l&rsquo;UPDATE. Alors que se passe-t-il si une ligne qui avait Y=5 au démarrage de notre update se trouve avoir maintenant la valeur Y=11 ? Alors le SELECT FOR UPDATE va lui aussi redémarrer.</p>
<p>Il ya deux questions qui seront abordées ici, deux questions qui m&rsquo;ont beaucoup intéressé, de toute façon. La première était: Peut-on observer cela ? Peut-on s&rsquo;en apercevoir lorsque çà arrive ?<br />
Et la deuxième: Et alors ? En quoi suis-je concerné en tant que développeur ?</p>
<p>Nous traiterons ces questions chacune à leur tour.<br />
A suivre&#8230;</p>
<blockquote><p><ins>Les 2 articles correspondants seront traduits prochainement:
<ul>
<li><a href="http://blog.developpez.com/pachot/p9071/auteurs/tom-kyte/tk-consistent-write-2/">Ecritures cohérentes &#8211; observation d&rsquo;un redémarrage, par Tom Kyte</a></li>
<li><a href="http://blog.developpez.com/pachot/p9070/auteurs/tom-kyte/tk-consistent-write-3/">Ecritures cohérentes &#8211; conséquences pour le développeur, par Tom Kyte</a></li>
</ul>
<p></ins></p></blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paramétrage optimal pour PCTFREE, PCTUSED et INITRANS, par Tom Kyte</title>
		<link>https://blog.developpez.com/pachot/tk_optimal_pctfree_initrans/</link>
		<comments>https://blog.developpez.com/pachot/tk_optimal_pctfree_initrans/#comments</comments>
		<pubDate>Mon, 03 May 2010 20:55:03 +0000</pubDate>
		<dc:creator><![CDATA[pachot]]></dc:creator>
				<category><![CDATA[Tom Kyte]]></category>
		<category><![CDATA[PCTFREE]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Cet article est la traduction d&#8217;une réponse Tom Kyte à une question sur le paramétrage idéal de PCTFREE, PCTUSED et INITRANS (L&#8217;article original en anglais se trouve ici). PCTFREE Si vous avez une table qui ne subit que des inserts, &#8230; <a href="https://blog.developpez.com/pachot/tk_optimal_pctfree_initrans/">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 Tom Kyte à une question sur le paramétrage idéal de PCTFREE, PCTUSED et INITRANS (L&rsquo;article original en anglais se trouve <a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2556828000346627752">ici</a>).<br />
</ins></p></blockquote>
<p><strong>PCTFREE</strong></p>
<p>Si vous avez une table qui ne subit que des <em>inserts</em>, mettez <strong>PCTFREE </strong>à 0, parce que vous n&rsquo;avez rien à réserver pour les <em>updates</em>.</p>
<p>si vous savez une table dont les lignes doublent de taille, c&rsquo;est une bonne idée de mettre <strong>PCTFREE </strong>à 50, pour réserver de la place pour les <em>updates</em>.</p>
<p>Vous devez comprendre comment les données grossissent dans le temps. Si vous faites des inserts, puis sur 10% de ces lignes vous faites des updates qui augmentent leur taille de 20%, alors vous devez:</p>
<ul>
<li>savoir combien de lignes par bloc vous avez en moyenne</li>
<li>calculer ce que les 20% d&rsquo;une ligne représentent en pourcentage de bloc.</li>
<li>utiliser ce pourcentage de bloc comme PCTFREE</li>
</ul>
<p><strong>PCTUSED</strong></p>
<p>Et utilisez ASSM (<em>automatic segment space management</em>), la gestion automatique de l&rsquo;espace libre des segments, et vous n&rsquo;avez pas à vous soucier de PCTUSED, FREELISTS, FREELIST GROUPS.</p>
<p><strong>INITRANS</strong></p>
<p>Pour INITRANS, vous devez vous baser sur le nombre maximum de modifications concurrentes qu&rsquo;il peut y avoir sur un bloc. Si vous avez une table qui ne reçoit que des inserts, et que vous êtes en ASSM, ne vous inquiétez pas et laissez la valeur par défaut. Si vous avez plutôt une petite table que vous updatez comme un fou, alors vous devez y réfléchir.<br />
<span id="more-30"></span><br />
<strong>Question supplémentaire sur PCTFREE </strong></p>
<blockquote><p><ins>Je ne comprends pas bien la raison du nombre de ligne par bloc ici. Je pensais que si 10% des lignes augmentent de 20% alors &#8211; en moyenne &#8211; l&rsquo;espace utilisé dans le bloc augment de 2%.<br />
</ins></p></blockquote>
<p>Réponse:</p>
<p>2% d&rsquo;un bloc n&rsquo;est pas vraiment de sens ici. Vous devez comprendre combien de lignes par bloc vous pouvez avoir. Par exemple, je peux avoir 7 lignes de 1024 octets, dans un bloc de 8k. Si je dois en updater 10% pour qu&rsquo;elles soient 20% plus larges, ce n&rsquo;est pas les 2% qui comptent, mais le calcul suivant:</p>
<ul>
<li>Environ 1 ligne par bloc va augmenter</li>
<li>Si elle s&rsquo;accroît de 20%, je ne peut avoir que 6 lignes par bloc</li>
<li>Je ferais mieux de mettre un PCTFREE qui fait qu&rsquo;il n&rsquo;y aura que 6 lignes en moyenne.</li>
</ul>
<p>2% serait trompeur, car la granularité des lignes est assez &laquo;&nbsp;large&nbsp;&raquo;</p>
<blockquote><p><ins>Une précision: Ces explications concernent les tables (<em>heap tables</em>) mais pas les index (ni les <em>Index Organized tables</em>)</ins><br />
<ins>Un index a une gestion différente de l&rsquo;espace libre: chaque ligne y a sa place, puisqu&rsquo;il est trié: un bloc n&rsquo;a pas le choix d&rsquo;accepter des lignes ou non. Les blocs sont remplis jusqu&rsquo;au bout, et lorsque ils sont pleins à 100%, ils peuvent être coupés (<em>split</em>) en deux blocs qui auront chacun leur part d&rsquo;espace libre.</ins><br />
<ins>Ce qui veut dire que pour les index, PCTUSED n&rsquo;a pas de sens, PCTFREE ne concerne que l&rsquo;espace libre laissé à la création de l&rsquo;index (ou au <em>rebuild</em> ou <em>coalsece</em>) pour qu&rsquo;il n&rsquo;y ait pas trop de <em>block splits</em> dès les premières mises à jour. INITRANS est ignoré, il suffit qu&rsquo;il y ait toujours une entrée possible pour la transaction qui aura à faire le <em>bloc split</em> et on pourra toujours faire de la place. Au fur et à mesure des mises à jour, l&rsquo;index s&rsquo;ajustera en fonction du besoin d&rsquo;espace libre et d&rsquo;entrées ITL.</ins></p></blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lectures cohérentes et multi-versionnage, par Tom Kyte</title>
		<link>https://blog.developpez.com/pachot/tk_consistent_read/</link>
		<comments>https://blog.developpez.com/pachot/tk_consistent_read/#comments</comments>
		<pubDate>Tue, 13 Apr 2010 22:29:47 +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 Tom Kyte à une question sur les vues cohérentes (L&#8217;article original en anglais se trouve ici). Question: J&#8217;ai lu qu&#8217;Oracle garantit une vue cohérente en lecture. Je l&#8217;ai lu, mais je n&#8217;ai pas &#8230; <a href="https://blog.developpez.com/pachot/tk_consistent_read/">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 Tom Kyte à une question sur les vues cohérentes (L&rsquo;article original en anglais se trouve <a href="http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:27330770500351">ici</a>).<br />
</p>
<p></ins></p></blockquote>
<p><strong>Question:</strong></p>
<p><i>J&rsquo;ai lu qu&rsquo;Oracle garantit une vue cohérente en lecture. Je l&rsquo;ai lu, mais je n&rsquo;ai pas l&rsquo;impression que c&rsquo;est très clair. <br />Pouvez-vous expliquer, avec vos mots, ce qu&rsquo;est une vue cohérente ?</i></p>
<p><strong>Réponse:</strong></p>
<p>J&rsquo;ai fais cela dans &laquo;&nbsp;<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;">Expert One on One Oracle</a>&laquo;&nbsp;. <br />J&rsquo;ai écrit beaucoup là dessus et voici un court extrait (il y a beaucoup plus dans le livre).</p>
<p><ins><em>Extrait de &lsquo;Expert One on One Oracle&rsquo; de Tomas Kyte &#8211; Apress</em></ins></p>
<p><strong>Multi-Versionnage et Concurrence</strong></p>
<p>Le Multi-versionnage (<em>multiversioning</em>) est un sujet lié au contrôle de concurrence d&rsquo;accès aux données, car qu&rsquo;il est à la base de ce mécanisme: Oracle implémente la concurrence d&rsquo;accès en faisant des lecture multi-versions cohérentes (<em>multi-version read consistent concurrency model)</em>. Dans le chapitre suivant &lsquo;Ce que vous devez savoir&rsquo;, nous allons parler des aspects techniques de manière plus détaillée, mais dans l&rsquo;essentiel, il s&rsquo;agit du mécanisme fourni par Oracle pour:</p>
<ul>
<li><strong>faire des lectures cohérentes <em>(Read-consistent queries)</em></strong>: les requêtes donnent un résultat cohérent à un instant donné.</li>
<li><strong>Requêtes non bloquantes <em>(Non-blocking queries)</em></strong>: les requêtes en lecture ne sont jamais bloquées par les écritures, contrairement à ce qui se passe avec d&rsquo;autres SGBD.
</li>
</ul>
<p><span id="more-29"></span><br />
Oracle utilise l&rsquo;information des <em><strong>rollback segments</strong></em> (ou <em>undo</em>) pour fournir une <strong>vue cohérente</strong> sur les données. Les <em>rollback segments</em> étant l&rsquo;endroit où Oracle stocke les anciennes valeurs (ou &lsquo;image avant&rsquo;) des données lors de l&rsquo;exécution des transactions, il peut utiliser ces anciennes valeurs pour fournir à la requête les données telles qu&rsquo;elles étaient en base au moment où la requête a été lancée. Au fur et à mesure que la requête lit les blocs de données de la table, elle voit si le bloc a été modifié depuis l&rsquo;instant où la requête a commencé. Et si le bloc a été modifié, Oracle va aller lire les <em>rollback segments</em> pour retrouver à quoi ressemblait le bloc au moment où la requête a commencé. C&rsquo;est cette version que la requête va voir. </p>
<p>C&rsquo;est aussi de cette manière que les <strong>lectures non bloquantes</strong> (<em>non blocking reads</em>) sont implémentées. Oracle va seulement regarder si les données ont été modifiées, il n&rsquo;a pas besoin de savoir si elles sont verrouillées (ce qui implique qu&rsquo;elles ont été modifiées). Il va simplement ramener les anciennes valeurs grâce aux <em>rollback segments</em> et passer au bloc de données suivant.</p>
<p>C&rsquo;est là qu&rsquo;intervient le terme <strong>multi-versionnage</strong> (<em>multiversioning</em>): chaque information a plusieurs versions en base de données, chacune correspondant à un instant donné. Ces versions multiples sont fournies par l&rsquo;undo des <em>rollback segments</em>. Oracle est capable d&rsquo;utiliser ces &lsquo;photos&rsquo; des données correspondant à différents instants (appelées <em>snapshot</em> comme dans le message d&rsquo;erreur &lsquo;ORA-15555 snapshot too old&rsquo;), pour nous donner une version cohérente avec des lectures non bloquantes.</p>
<p>Ce sont deux concepts très importants pour le SGBD Oracle. Si vous comprenez comment le multi-versionnage fonctionne, vous comprendrez toujours les réponses que vous obtiendrez de la base de données.<br />
Voici le moyen le plus simple que je connaisse pour démontrer le multi-versionnage sous Oracle:</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">tkyte@TKYTE816&gt; create table t <br />
&nbsp; 2 &nbsp;as <br />
&nbsp; 3 &nbsp;select * from all_users; <br />
Table created. <br />
&nbsp;<br />
tkyte@TKYTE816&gt; variable x refcursor <br />
&nbsp;<br />
tkyte@TKYTE816&gt; begin <br />
&nbsp; 2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;open :x for select * from t; <br />
&nbsp; 3 &nbsp;end; <br />
&nbsp; 4 &nbsp;/ <br />
PL/SQL procedure successfully completed. <br />
&nbsp;<br />
tkyte@TKYTE816&gt; delete from t; <br />
18 rows deleted. <br />
&nbsp;<br />
tkyte@TKYTE816&gt; commit; <br />
Commit complete. <br />
&nbsp;<br />
tkyte@TKYTE816&gt; print x <br />
&nbsp;<br />
USERNAME &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;USER_ID CREATED <br />
------------------------------ ---------- --------- <br />
SYS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0 04-NOV-00 <br />
SYSTEM &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;5 04-NOV-00 <br />
DBSNMP &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 16 04-NOV-00 <br />
AURORA$ORB$UNAUTHENTICATED &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 24 04-NOV-00 <br />
ORDSYS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 25 04-NOV-00 <br />
ORDPLUGINS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 26 04-NOV-00 <br />
MDSYS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;27 04-NOV-00 <br />
CTXSYS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 30 04-NOV-00 <br />
... <br />
DEMO &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 57 07-FEB-01 <br />
&nbsp;<br />
18 rows selected.</div></div>

</pre>
<p>Dans cet exemple, nous avons créé une table de test, <code class="codecolorer text default"><span class="text">T</span></code>, et avons chargé quelques données à partir de la vue ALL_USERS. </p>
<p>Nous avons ouvert un curseur sur cette table. Nous n&rsquo;avons fetché aucune donnée de ce curseur: nous l&rsquo;avons seulement ouvert. Gardez en tête que Oracle ne &lsquo;réponds&rsquo; pas à la requête, ne copie pas les données quelque part lorsque vous ouvrez le curseur. Il réponds à la requête plus tard.<br />
Mettons que la table <code class="codecolorer text default"><span class="text">T</span></code> ait 1 milliard d&rsquo;enregistrements, Oracle ne va pas &lsquo;répondre&rsquo; à la requête et copier les données quelque part. Il va seulement les lire au fur et à mesure que l&rsquo;on va faire les <em>fetch</em>. </p>
<p>Maintenant, dans la même session (mais cela pourrait être fait d&rsquo;une autre session), nous supprimons toutes les données de cette table. Nous allons même jusqu&rsquo;à commiter cette transaction. </p>
<p>Les enregistrement sont partis, mais est-ce vraiment le cas ? </p>
<p>Le fait est que le résultat retourné par la commande OPEN a été pré-établi au moment où nous avons ouvert le curseur. Nous n&rsquo;avions pas encore touché à un seul bloc de données de cette table durant l&rsquo;OPEN, mais le résultat de la requête était déjà déterminé. Nous n&rsquo;avons aucun moyen de connaître le résultat avant de faire les FETCH, mais ce résultat est pourtant immuable du point de vue du curseur.</p>
<p>Oracle n&rsquo;a pas fait une copie des données lorsque nous avons ouvert le curseur. Imaginez le temps que cela prendrait d&rsquo;ouvrir un curseur sur un milliard d&rsquo;enregistrements si c&rsquo;était le cas. Au contraire, le curseur s&rsquo;ouvre instantanément &#8211; il ne copie pas les données. C&rsquo;est en fait le <em>delete</em> qui garde les données pour nous (oui, le delete préserve les données pour nous) en les mettant dans les <em>rollback segment</em>.</p>
<p>Voilà ce qu&rsquo;est la lecture cohérente. Oracle permet des lectures cohérentes grâce au mécanisme de multi-versionnage. Dans le chapitre suivant &lsquo;Ce que vous devez savoir&rsquo;, nous allons parler des aspects techniques de manière plus détaillée. Il faut noter cependant que jusqu&rsquo;à ce que vous ne compreniez ce que cette fonctionalité implique, vous ne serez pas capable de profiter à fond d&rsquo;Oracle, ni d&rsquo;écrire des applications correctes (c&rsquo;est à dire qui assurent l&rsquo;intégrité des données).</p>
<p><ins><em>fin de l&rsquo;extrait</em></ins></p>
<p>La documentation Oracle <a href="http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96524/c21cnsis.htm#2570">Concept Guide</a> explique aussi cela très bien.</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)</ins>
</td>
</tr>
</table>
</blockquote>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
