samedi 22 décembre 2012

Index Bitmaps et problèmes de locks

Si vous utilisez des index bitmap sur une table pour laquelle de nombreuses mises à jour sont effectuées il est fort probable que vous rencontriez des problèmes de locks voir de dead locks. Les index bitmaps sur les bases de type OLTP constituent la 2ème source des problèmes de dead locks. La première étant lié aux clés étrangères non indexées.

Dans cet article on va essayer de comprendre pourquoi les index bitmaps sont sources de locks.

Tout d’abord, rappelons à quoi servent les index bitmaps.
Les index bitmaps constituent une fonctionnalité d’Oracle disponible uniquement dans l’Enterprise Edition. Si vous utilisez une Standard Edition vous ne pourrez pas utiliser les index bitmaps.

Ces index sont généralement utilisés sur des colonnes qui sont peu sélectives c'est-à-dire qui ont un relativement faible nombre de valeurs distinctes. Leur principal avantage est qu’ils peuvent être mergés très rapidement avec des index bitmaps sur d’autres colonnes, c’est pourquoi ils sont très efficaces sur des requêtes avec des combinaisons de critères. De plus ils sont très compacts par rapport aux index B-Tree et prennent donc moins de place.

Le fait que les index bitmap peuvent causer des locks sur plusieurs lignes d’une table s’explique par la manière dont ces index sont structurés. Contrairement aux index B-Tree pour lesquels une entrée d’index est associée à un seul ROWID de la table, les index Bitmap ont une entrée d’index qui pointe vers plusieurs ROWIDs. Lorsqu’on crée un index BITMAP sur la colonne OBJECT_TYPE, Oracle crée un bitmap pour chaque valeur distincte de la colonne. Chaque bitmap contient un bit pour chaque ligne de la table.

Exemple :
create bitmap index idx_bmp_t1 on t1(object_type);

L’index bitmap crée ci-dessus peut être représenté de la manière suivante :
value/Rowid    1    2    3    4    5    6    7    8    9    10
INDEX            0    0    0    0    1    0    0    0    0     1
TABLE            1    0    0    1    0    1    0    0    0     0            
SYNONYM     0    1    0    0    0    0    1    0    0     0
SEQUENCE   0    0    1    0    0    0    0    1    1     0

On voit que la ligne 5 a la valeur INDEX, alors que les lignes 1, 4 et 6 ont la valeur TABLE.

Lorsque la colonne pour laquelle existe un index Bitmap est modifiée pour une ligne donnée (via un update, insert ou delete), Oracle ne peut locker uniquement le bit correspondant à la ligne modifiée. Il lock tout le bitmap et donc par le même effet toutes les lignes adressées par le bitmap. Dans mon exemple je n’ai mis que 10 lignes mais ça peut être plusieurs centaine de milliers de lignes.

Donc si une autre session au même moment modifie une autre ligne de la table impactant la même entrée d’index elle risque fortement d’être bloquée par le lock précédent.

Illustrons cela avec un exemple.
Tout d’abord on crée une table T1 avec une colonne OBJECT_NAME et une colonne OBJECT_TYPE puis on crée un index bitmap sur la colonne OBJECT_TYPE
create table t1
as select object_name,object_type from dba_objects where 1=2;

create bitmap index idx_bmp_t1 on t1(object_type);

Dans la même session on insert une ligne dans la table T1 de type TABLE :
-- session 1
SQL> insert into t1 values ('TOTO','TABLE');

1 row created.

Dans une autre session on crée une 2ème ligne dans la table avec également OBJECT_TYPE à « TABLE » :
-- session 2
SQL> insert into t1 values ('TITI','TABLE');

Cette fois la session est lockée car la valeur « TABLE» correspond à l’entrée d’index dont le bitmap a été locké par la session 1. La session 2 doit attendre que la session 1 termine sa transaction pour pouvoir effectuer son INSERT. Avec un index B-Tree on n’aurait pas eu ce genre de problème car on aurait eu une entrée d’index pour chaque ligne.

Si à l’inverse une session 3 effectue un INSERT dans la table T1 avec par exemple la valeur « INDEX » celle-ci ne sera pas bloquée par le lock de la session 1 car il s’agit ici d’une autre entrée d’index et donc d’un autre bitmap :
-- session 3
SQL> insert into t1 values ('TUTU','INDEX');

1 row created.

Les updates sont encore plus bloquants car ils lockents 2 entrées d’index et donc 2 bitmaps : l’ancienne valeur et la nouvelle valeur.

Dans notre exemple précédent on effectue un commit sur toutes les sessions pour libérer tous les locks puis dans la session 1 on effectue l’update suivant :
-- session 1
SQL> update T1 set object_type = 'INDEX' where object_name='TOTO';

2 rows updated.

L’update ci-dessus lock le bitmap pour l’entré d’index « INDEX » mais aussi le bitmap pour l’entrée d’index « TABLE ». Si on insert dans une autre session une ligne avec une valeur TABLE et une ligne avec une valeur INDEX les 2 inserts seront blockés par l’update précédent :
-- session 2
SQL> insert into t1 values ('TATA','TABLE');

-- session 3
SQL> insert into t1 values ('TYTY','INDEX');

Un commit dans la session 1 et les session 2 et 3 sont automatiquement débloquées.

CONCLUSION :
Les index Bitmaps ont l’inconvénient de causer des locks sur un grand ensemble de lignes même si une seule ligne est modifiée.
Voilà pourquoi les index bitmap sont inappropriés pour les bases de type transactionnel. On utilise ces index essentiellement dans les bases de type décisionnel. Il est possible d'utiliser des index bitmap sur des bases transactionnelles à conditions que ce soient des tables pour lesquelles les mises à jours sont très très peu fréquentes.