Vous êtes connecté ! en tant que membre de l'équipe BD
L'objectif de ce TME est de mettre en place des situations de concurrence entre transactions et de comprendre les mécanismes de contrôle de concurrence.
Voir l'extrait de la documentation Derby DB sur la gestion des transactions
Ne pas oublier de consulter les questions fréquentes en bas de cette page
Télécharger l'archive db-derby-10.14.2.0-bin.zip et la décompresser dans votre répertoire de travail et aller dans le dossier db-derby-…. dans toutes les instructions qui suivent, ajuster le numéro de version de db-derby selon votre installation: db-derby-10.14.2.0-bin ou db-derby-10.15.2.0.
bin/startNetworkServer
et bin/ij
La BD utilisée pour ce TME est Java DB, la version implémentée par Oracle de la BD open source Derby DB d'Apache.
Vous pouvez également suivre les instructions d'installation de Derby sur la page suivante: Derby Install Software
Étape1. Connaître votre version actuelle de Java JDK:
java -version
Si Java n'est pas installé le télécharger à l'adresse suivante, sinon passer à l'étape 2: https://www.oracle.com/java/technologies/javase-downloads.html
Étape2. Télécharger Apache Derby à cette adresse http://db.apache.org/derby/derby_downloads.html (choisir une version binaire correspondante à votre version de Java déterminée à l'Étape 1, en principe https://downloads.apache.org/db/derby/db-derby-10.14.2.0/db-derby-10.14.2.0-bin.zip). Les exemples suivants supposent que vous avez téléchargé la version une des distributions suivantes appartenant à la version 10.15.2.0 (adapter ces exemples à votre propre version qui a été téléchargée):
Windows: db-derby-10.15.2.0-bin.zip UNIX, Linux, Mac: db-derby-10.15.2.0-bin.tar.gz
Créer un répertoire pour le tme, télécharger et désarchiver derby dans ce répertoire:
WINDOWS: mkdir C:\tme-transactions copy db-derby-10.15.2.0-bin.zip C:\tme-transactions cd C:\tme-transactions unzip db-derby-10.15.2.0-bin.zip
UNIX: mkdir ~/tme-transactions cp db-derby-10.15.2.0-bin.tar.gz ~/tme-transactions cd ~/tme-transactions tar xzvf db-derby-10.15.2.0-bin.tar.gz
On va utiliser pour ce TME la table Account qui contient les comptes bancaires des différents clients. Pour chaque compte bancaire, on connaît son n° (acctID), le nom du client (cname) et le solde (balance):
Lancer le serveur Java DB (à exécuter une seule fois au début du TME):
db-derby-10.15.2.0-bin/bin/startNetworkServer &
Si JAVA_HOME n'est pas défini le définir comme indiqué à l'étape 2 et lancer ensuite le serveur.
Remarque: pour arrêter le serveur à la fin du TME, utiliser l'option shutdown, voir les questions fréquentes en bas de cette page.
Créer la base de données et la table Account:
db-derby-10.15.2.0-bin/bin/ij
Si JAVA_HOME n'est pas défini le définir comme indiqué à l'étape 2.
ij> CONNECT 'jdbc:derby://localhost:1527/accountdb;create=true';
Cela aura pour effet la création d'un répertoire nommé accountdb dans votre répertoire de travail. Rmq: en cas d'erreur de connexion, vérifier que le serveur JavaDB est bien démarré (cf. 1ère étape de ce TME).
ij> autocommit off; ij> CREATE TABLE Account (acctID INTEGER NOT NULL PRIMARY KEY, cname VARCHAR(10) NOT NULL, balance INTEGER NOT NULL); ij> DESCRIBE Account; ij> Select * from Account; ij> commit;
ij> exit;
R: On fait commit pour la création de la table, même si Java DB est par défaut en mode autocommit, pour rester cohérent avec la suite du TME où l'on fait commit à la fin des transactions
Lancer deux clients Java DB qui seront associés à deux transactions:
db-derby-10.15.2.0-bin/bin/ij
Si JAVA_HOME n'est pas défini le définir comme indiqué à l'étape 2.
ij> CONNECT 'jdbc:derby://localhost:1527/accountdb;';
ij> autocommit off;
Question 1)
ij> SELECT * FROM Account; ij> INSERT INTO Account (acctID, cname, balance) VALUES (101, 'Client A', 1000); ij> INSERT INTO Account (acctID, cname, balance) VALUES (202, 'Client B', 2000); ij> SELECT * FROM Account; ij> ROLLBACK; ij> SELECT * FROM Account;
Question 2)
Étape 1:
ij> INSERT INTO Account (acctID, cname, balance) VALUES (101, 'Client A', 1000); ij> INSERT INTO Account (acctID, cname, balance) VALUES (202, 'Client B', 2000); ij> SELECT * FROM Account; ij> exit;
Étape 2:
Dans le terminal T1, se connecter à nouveau à Java DB:
db-derby-10.15.2.0-bin/bin/ij ij> CONNECT 'jdbc:derby://localhost:1527/accountdb;'; ij> autocommit off;
Une fois connecté lire le contenu de la table Account:
ij> SELECT * FROM Account;
Question 3)
Quelle commande faut-il ajouter à l'étape 1 pour que les 2 lignes qui ont été insérées dans la table Account soient affichées à l'étape 2?
R: Un commit avant exit
Étape d'initialisation:
Avant chacune des questions suivantes exécuter les instructions suivantes:
ij > commit;
ij> DELETE FROM Account; ij> INSERT INTO Account (acctID, cname, balance) VALUES (101, 'Client A', 1000); ij> INSERT INTO Account (acctID, cname, balance) VALUES (202, 'Client B', 2000); ij> INSERT INTO Account (acctID, cname, balance) VALUES (303, 'Client C', 3000); ij> SELECT * FROM Account;
ij> SET isolation <niveau-isolation>;
Où <niveau-isolation> est l'un parmi les niveaux suivants (voir les niveaux SQL-92 et le Tableau 6, page 78 dans la documentation Derby DB):
set isolation fait aussi commit;
Opérations:
Le compte du 'Client A' correspondra au granule A, le compte du 'Client B' au granule B, le compte du 'Client C' au granule C. Chacune des deux transactions T1 et T2 peut effectuer des opérations parmi les opérations suivantes:
L(g) : SELECT * FROM Account WHERE acctID = @g; E(g, v) : UPDATE Account SET balance = balance + v WHERE acctID=@g;
L(g) représente la lecture d'un granule g parmi A, B ou C et @g est la valeur de acctID correspondante. E(g,v) représente une écriture du granule g en ajoutant la valeur v (la valeur v peut être positive ou négative). À noter qu'en réalité UPDATE réalise aussi une lecture qu'on va ignorer car UPDATE demande un verrou exclusif et pas de verrou partagé). Par exemple, L(A) et E(B,-100) correspondent aux instructions suivantes:
SELECT * FROM Account WHERE acctID = 101; UPDATE Account SET balance = balance - 100 WHERE acctID = 202;
Pour chaque opération on va noter aussi la transaction qui l'exécute, par exemple L1(A) pour la lecture du compte 101 par T1. On va noter également par C1 et C2 les opérations commit réalisées par T1 et T2 et par R1 et R2 les opérations rollback réalisées par T1 et T2.
Pour cet exercice le niveau d'isolation des transactions sera SERIALIZABLE (voir tableau page 83 dans la documentation Derby DB):
Le verrouillage est strict, les verrous sont relâchés après commit ou rollback (voir la description “Shared Locks” et “Exclusive Locks” pour le niveau SERIALIZABLE, page 81 dans la documentation Derby DB).
Pour chacune des questions suivantes:
Première partie: Test du verrouillage
Question 1)
Vérifiez qu'une lecture faite par T1 ne bloque pas une lecture faite par T2
Question 2)
Vérifiez qu'une lecture faite par T1 bloque une écriture faite par T2. Quelle sera l'instruction de T1 qui va débloquer T2?
R: commit
Question 3)
Vérifiez qu'une écriture faite par T1 bloque une lecture faite par T2.
Question 4)
Vérifiez qu'une écriture faite par T1 bloque une écriture faite par T2.
Question 5)
On suppose maintenant que T1 et T2 travaillent sur des granules différentes (T1 sur A et T2 sur B par exemple). Reprendre l'exécution de la question 4 précédente en changeant le granule pour T2. T2 est-elle toujours bloquée? Pour quelle raison?
R: T2 n'est plus bloquée
Deuxième partie: Verrouillage en deux phases
Exécutions sans interblocage
Question 6)
Question 7)
Question 8 (question TD)
Exécutions avec interblocage
Vérifiez que lorsqu'un interblocage se produit, Java DB choisit d'abandonner la transaction qui détient le moins de verrous (en supposant aisi que ce sera la transaction qui aura effectué le moins de travail avant l'interblocage).
Question 9)
Question 10)
Donnez une exécution concurrente de T1 et T2 effectuant des opérations sur 2 granules différents, au moins deux de ces opérations étant des lectures. Cette exécution doit produire un interblocage.
Question 11 (question TD)
Le niveau d'isolation SERIALIZABLE étant restrictif, l'utilisation des verrous pourrait mettre en attente des transactions dont l'exécution ne pose pas de problème pour la cohérence de la BD. Afin d'augmenter la concurrence et diminuer le temps d'attente d'autres niveaux d'isolation peuvent être utilisés. Cet exercice permet d'étudier les anomalies qui peuvent être rencontrées dans ce cas.
Question 1 (Lecture sale)
Une transaction peut lire des données sales (non validées). Cette anomalie peut être observée en utilisant le niveau READ UNCOMMITTED.
a) Pour l'exécution suivante, testez que T2 peut lire le compte du Client A qui est modifié par T1, alors que T1 n'a pas encore validé ses modifications. Que peut-on conclure sur la fiabilité de T2? Existe-t-il des verrous?
* Exécutez l'étape d'initialisation avec le niveau READ UNCOMMITTED * E1(A, 100) L1(A) L2(A) C2 R1 L1(A) C1
b) Testez la même exécution en changeant d'abord le niveau d'isolation à READ COMMITTED pendant l'étape d'initialisation. T2 voit-elle toujours l'écriture faite par T1? Existe-t-il des verrous? Quel est leur type et avant quelles opérations sont-ils demandés?
Question 2 (Lecture non réproductible)
Le niveau d'isolation sera READ COMMITTED, une transaction pourra lire seulement des données validées (on ne peut donc plus avoir des lectures sales). Il peut cependant y avoir des lectures non réproductibles: la relecture de la même ligne dans la même transaction peut montrer un résultat différent.
a) Pour l'exécution suivante, testez que les deux lectures successives du compte appartenant au Client A par T1 affichent deux valeurs différentes. Quels verrous ont été utilisés? Avant quelles opérations ont-ils été demandés et après quelles opérations sont-ils relâchés?
* Exécutez l'étape d'initialisation avec le niveau READ COMMITTED * L1(A) E2(A, 100) C2 L1(A) C1
b) Testez la même exécution en changeant d'abord le niveau d'isolation à RS (REPEATABLE READ) pendant l'étape d'initialisation. T1 voit-elle toujours deux valeurs différentes du compte? Quels verrous ont été utilisés? Avant quelles opérations ont-ils été demandés et après quelles opérations sont-ils relâchés?
Question 3 (Lecture fantôme)
Le niveau d'isolation sera RS (REPEATABLE READ). Ce niveau empêche les lectures sales et non réproductible. Il peut y avoir cependant le problème lié aux lectures fantômes: pour une transaction, si on relance la même requête à deux instants différents, l'ensemble des lignes résultat aux deux instants peut être différent car de nouveaux enregistrements ont pu être ajoutés/supprimés dans/de la base par une autre transaction, ce qui modifie le résultat. Ceci est différent de la lecture non réproductible dans le sens où les enregistrements lus précédemment n’ont pas changé, mais des enregistrements peuvent être introduits/supprimés du résultat de la requête.
a) Pour l'exécution suivante, testez que les deux lectures successives de la table Account effectuées par T1 affichent un ensemble de résultats différent.
* Exécutez l'étape d'initialisation avec le niveau REPEATABLE READ * L1 E2 C2 L1 C1
où L1 représente la lecture de toute la table Account réalisée par T1:
SELECT * FROM Account;
E2 représente l'insertion d'une ligne correspondante au compte d'un 'Client D' réalisée par T2:
INSERT INTO Account VALUES(404, 'Client D', 4000);
b) Testez la même exécution en changeant d'abord le niveau d'isolation à SERIALIZABLE pendant l'étape d'initialisation. Le niveau SERIALIZABLE empêche les lectures sales, non réproductibles ainsi que les lectures fantôme.
Tester les exécutions correspondantes aux Questions 1 à 11 de l'exercice 2 pour les niveaux d'isolation: READ UNCOMMITTED, READ COMMITED et REPEATABLE READ.
exit;
db-derby-10.15.2.0-bin/bin/stopNetworkServer &
Si JAVA_HOME n'est pas défini le définir comme indiqué à l'étape 2.