12c: Container Database et Pluggable Databases

La 12c est sortie (Linux et Solaris):

  • La doc à lire en ligne
  • L’install à télécharger sur OTN

Au delà des nouvelles fonctionnalités, c’est un gros changement d’architecture avec la notion de CDB – Multitenant Container Database

On est habitués à ce que une instance (les process et la mémoire) travaille sur une seule base (les fichiers). En RAC on peut avoir plusieurs instances sur une seule base. Mais contrairement à d’autres SGBD, entre plusieurs bases, on ne partage aucune ressource: chacune a son dictionnaire, l’ensemble des objects Oracle (les dbms_xxx, tables systèmes), chacune a sa mémoire, ses process,…

Bien sûr on pourrait n’avoir que très peu de bases – l’idéal étant de n’avoir qu’une seule instance par serveur. Mais pour cela il faut pouvoir mettre les schémas de plusieurs applications dans une même base. C’est tout à fait possible et Oracle permet aussi bien de les séparer logiquement (par les schemas) et physiquement (par les tablespaces).

Au niveau physique, il est assez facile de gérer l’ensemble des tablespaces d’une application: on peut les backuper indépendamment, les transporter avec Transportable Tablespace, etc. et au niveau logique tout va bien si l’application accepte de ne pas être toute seule sur la base. Mais ce n’est pas le cas. Beaucoup de logiciels imposent les noms de schemas (donc on ne peut pas en avoir deux sur une même base), créent des objets publics (db links, synonymes) qui empêchent de mutualiser les ressources.

En 12c Oracle a étendu le concept de Transportable Tablespaces (ensemble de tablespaces ‘self-contained’ et export des meta-données du dictionnaire qui vont avec) avec le concept de Pluggable Databases. En y rajoutant tout pour que l’application croit être toute seule sur sa base. La Pluggable Database a son tablespace SYSTEM avec les metadonnées des tables de l’application, ses tables, ses users, etc. et des liens vers les metadonnées communes.

On a donc plusieurs containers dans une seule database gérée par une seule instance, et chacun apparaît comme une base:
Chaque Pluggable Database sur laquelle se connecte l’appli est un Container. L’application peut y faire ce qu’elle veut comme si elle était toute seule sur une base.
Les objets communs sont dans un container racine CDB$ROOT. Il y a en plus une Pluggable Database PDB$SEED en read-only qui permet de créer une nouvelle PDB facilement en la clonant – fini les longs catalog.sql/catproc.sql pour chaque création de base. Les développeurs vont pouvoir demander une nouvelle base sans avoir l’impression de demander un nouveau datacenter.

Une base qui peut contenir plusieurs conteneurs – donc plusieurs Pluggable Databases, c’est une ‘Multitenant Container Database’ ou CDB.
Bien sûr on peut continuer à créer des bases non-CDB, comme avant, sans cette notion de Pluggable Database. Elle n’ara alors qu’un seul conteneur.

c’est au CREATE DATABASE que ça se décide avec ENABLE PLUGGABLE DATABASE.

Au prochain post, la création d’une Container Database…

Requêtes de pagination ‘Top-N’

On a souvent besoin d’afficher la première page d’un résultat (par exemple les 10 premières lignes) et éventuellement les pages suivantes avec des requêtes indépendantes. En attendant la 12c (très bientôt) qui implémentera la syntaxe standard qui commence à se normaliser sur tous les SGBD (OFFSET … FETCH FIRST/NEXT … ROWS) il y a plusieurs techniques possibles mais toutes n’ont pas les mêmes performances.

La démo des différentes possibilités avec plans d’exécution: demo

Déjà commençons avec deux mauvaises idées:

  • Ne pas mettre d’ORDER BY en espérant que le résultat revient trié.
    Sans ORDER BY, l’ordre du résultat n’est pas prédictible. Même s’il on passe par un index. Il faut mettre la clause ORDER BY et le tri sera évité par Oracle si l’index correspondant le permet.
  • Faire une requête normale (avec ORDER BY) et ne limiter le résultat qu’au moment du Fetch.
    Le résultat sera juste, mais les performances moins bonne que si l’optimiseur connait à l’avance le nombre de lignes dont on a besoin.(cf. plans)

rownum
La technique la plus ancienne est de faire le ORDER BY dans une sous-requêtes, puis limiter le résultat avec ROWNUM:
Exemple:
les 10 premiers enregistrements d’un contrat:

select * from (select * from TEST where contract_id=500 order by start_validity  asc) where rownum<=10;

C’est la solution qui fonctionne depuis longtemps et probablement la plus utilisée (par Hibernate par exemple).

Mais la doc 11gR2 précise cependant: The ROW_NUMBER built-in SQL function provides superior support for ordering the results of a query

row_number()
Avec la fonction analytique row_number() on compte les lignes dans une sous-requête puis on limite le résultat.

select /*+ FIRST_ROWS */ * from (select test.*,row_number()over(order by start_validity) rn from test where contract_id=500) where rn<=10  order by start_validity;

A noter:

– je lance la requête en FIRST ROWS car je je souhaite afficher rapidement ces 10 premières lignes. Avec ROWNUM, c’était implicite – le CBO passait en optimisation FIRST_ROWS(10). L’accès par INDEX RANGE SCAN est donc probablement préférable.

– j’ai utilisé FIRST_ROWS et non FIRST_ROWS(10) car sinon dans mon exemple, FULL TABLE SCAN était choisi (peut être un bug en 11.2.0.3). La bonne technique devrait être FIRST_ROWS(n).

– j’ai un index sur (contract_id,start_validity) qui peut être utilisé pour éviter un tri car ce sont des NUMBER. Avec des VARCHAR2 le plan d’exécution peut dépendre des paramètres NLS car l’ordre de l’index (binary) n’est peut-être pas le même que celui de la langue.

Le conseil: tester et vérifier les plans d’exécution et le volume nécessaire au tri sur des données représentatives afin de valider les hints et indexes.

Fonctions Analytiques: chevauchement des plages de dates

Sans les fonctions analytiques, on ferait une jointure de la table avec elle-même pour voir les lignes adjacentes. Ici on utilise LEAD() et LAG() pour les voir en une seule passe avec une bien meilleur performance.

Sur une table CONTRACTS avec un contract_id et les plages de validité start_validity,end_validity la requête suivante donne les plages de dates qui se recoupent.

select contract_id,start_validity,end_validity from (
	    select
		   contract_id,start_validity,end_validity
		   ,lag  (end_validity)  over (partition by contract_id order by start_validity,end_validity) lag_end_validity
		   ,lead (start_validity)over (partition by contract_id order by start_validity,end_validity) lead_start_validity
	    from CONTRACTS
) where start_validity <= lag_end_validity or end_validity >>= lead_start_validity
order by 1,2,3
;

Le select imbriqué rajoute les dates de de validité suivantes (LEAD) et précédentes (LAG) pour chaque contrat (PARTITION BY) dans l’ordre chronologique (ORDER BY) et le select principal peut les comparer.

Démo avec plans d’exécution: demo

Sur un gros volume, le FULL TABLE SCAN unique et le WINDOW SORT sont des opérations optimales, faisant des i/o larges, possibilité de Parallel Query, de SmartScan sur Exadata,… alors que la solution jointure fait un accès par index très peu scalable.

Invalider un cursor

Lorsqu’on a en shared pool un plan d’exécution mauvais (par exemple à cause de bind variable peeking sur des valeurs particulières) il faut pouvoir vite l’invalider afin que la prochaine exécution fasse un hard parse.
Voici comment invalider directement un cursor avec dbms_shared_pool.purge:
Je récupère le nom à passer à partir du sql_id:

select rawtohex(address)|| ','||to_char(hash_value),sql_id,child_number
from v$sql where sql_id='84vk5fwyw403g';
RAWTOHEX(ADDRESS)||’,’||TO_CHAR(HASH_VALUE) SQL_ID CHILD_NUMBER
000007FF1E4B7218,1036124271 84vk5fwyw403g 0

Et j’invalide le curseur:

exec sys.dbms_shared_pool.purge('000007FF1E4B7218,1036124271','...');

Exemple complet ici: demo

Déplacer des archivelog avec RMAN

Par manque de place dans un filesystem, on peut être amené à déplacer des archive logs.
Si on les déplace avec l’OS (mv) alors Oracle ne saura plus où ils sont. Il faudra utiliser RMAN pour supprimer les anciens (crosscheck archivelog all ; delete expired archivelog all; catalog start with ‘…’ ; )

Mais il y a plus rapide: BACKUP AS COPY fait une copie des fichiers et avec DELETE INPUT on supprime la source. Ce qui revient à les déplacer…

Exemple:

Lire la suite

Micro Learning

En passant

J’ai rajouté une nouvelle catégorie dans ce blog: MicroLearning.
Le but est d’y présenter rapidement une fonctionnalité d’Oracle, ou une astuce, en quelques lignes avec un lien sur une demo plus complète.

Bien-sûr, l’idée est de l’alimenter assez régulièrement, surtout dès que la 12c va être disponible avec son lot de nouveautés.

N’hésitez-pas à laisser des commentaires…

Afficher le plan d’exécution avec les stats de chaque étape

Pour poster un plan d’exécution dans les forums, il est important de montrer les statistiques d’exécution et pas seulement les estimations – car s’il on a un problème c’est peut-être justement que les estimations sont fausses.

Voici comment faire facilement sous sqlplus.

Les exemples complets sont ici: demo
Lire la suite

Lighty for Oracle

Un peu de pub pour un superbe outil que j’ai découvert tout récemment, pour visualiser et analyser les performances d’une base Oracle.
Beaucoup plus convivial que les pages performance de Entreprise manager.
Facile d’utilisation (appli java – simple connexion jdbc) et complet.
Basé sur AWR et ASH (donc Diagnostic Pack indispensable) mais on attend patiemment une version compatible Statspack…

On peut télécharger la version d’essai, pleinement opérationnelle: http://orachrome.com/

Le guide sur la performance SQL

Guillaume Lelarge a eu la bonne idée de se lancer dans la traduction de ‘Use The Index, Luke‘, l’excellent livre en ligne de Markus Winand qui couvre tous les aspect de la performance en SQL qu’un développeur doit connaître.

Toutes les questions que vous vous posez sur les index et sur les différentes opérations SQL y sont détaillées. Les concepts compliqués y sont expliquées de manière simple. Comprendre leur fonctionnement permet d’anticiper les problèmes de performances qui peuvent se poser, dès la conception de l’application.

Et quelque chose que je n’ai jamais vu ailleurs: les spécificités principaux SGBD sont pris en compte: Oracle, SQL Server, MySQL, PostgreSQL, DB2…

La traduction française faite par Guillaume Lelarge est publiée petit à petit sur le site de Markus, et vous pouvez commencer par la préface ici:
http://use-the-index-luke.com/fr/sql/preface

Le livre original en anglais est vraiment excellent et la traduction tout à fait à la hauteur.

Vous ne savez pas quels index créer, et lesquels sont inutiles ?
Des questions sur l’ordre des colonnes à indexes?
Vous évitez les index clusterisé ou IOT par méconnaissance ?
Vous ne savez pas voir le plan d’exécution de vos requêtes ?

Alors bonne lecture…

Annonce: Trivadis CBO Days 2012

Ma société organise une rencontre à Zurich – en anglais – avec les experts de la performance Oracle et de l’optimiseur CBO: Jonathan Lewis, Jože Senegacnik, Randolf Geist, Christian Antognini ainsi que les reponsables Produit et Développement de l’Oracle Optimizer que sont respectivement Maria Colgan et Mohamed Zait.

Rencontrez des spécialistes internationaux de la performance Oracle et approfondissez avec eux vos connaissances du Cost-Based Optimizer d’Oracle. Ce remarquable événement vous donnera non seulement un grand nombre d’informations utiles et pratiques sur l’état actuel du cost-based optimizer (CBO) d’Oracle mais il vous montrera aussi les évolutions futures.

Pour plus d’informations sur les CBO Days du 11-12 Décembre 2012 à Zurich, visitez le site www.trivadis.com/cbo

Lire la suite