Quand on développe un programme P accédant à une base de données, on suppose que :
P s’exécutera indépendamment de tout autre programme ou utilisateur
l’exécution de P se déroulera toujours intégralement.
Or les bases de données constituent des ressources accessibles simultanément à plusieurs utilisateurs pour rechercher, créer, modifier ou détruire des informations :
les accès simultanés à une même ressource sont dits concurrents => contrôle de concurrence
Il existe une multitude de raison qu’un programme ne se déroule pas intégralement (arrêt des serveurs, erreurs de code, violation de contrainte …) => reprise sur panne
http://sql.bdpedia.fr/transactions.html
DELETE UPDATE INSERT : ce sont des instructions transactionnelles atomiques (elles fonctionnent complètement ou pas du tout)
Le concept de transaction sur un compte (bancaire …)
UPDATE sb_accounts SET balance = balance - 1000 WHERE account_no = 111111 ;
UPDATE ca_accounts SET balance = balance + 1000 WHERE account_no = 222222 ;
INSERT INTO journal VALUES (100896, 'Transfert de M Dupond ', '2017-01-01', 111111, 222222, 1000);
COMMIT WORK;
documentation : “doc developpez.com” “doc postgreSQL”
DROP TABLE IF EXISTS td_Client;
CREATE TABLE td_Client (
INTEGER NOT NULL,
id_client varchar(30) NOT NULL,
nom INTEGER NOT NULL,
nb_places_reservees INTEGER NOT NULL,
solde PRIMARY KEY (id_client)
);
INSERT into td_Client VALUES (1, 'Philippe', 0, 2000);
INSERT into td_Client VALUES (2, 'Julie', 0, 350);
INSERT into td_Client VALUES (3, 'alex', 0, 350);
SELECT @@autocommit;
UPDATE td_Client SET nom="ALEXANDRE" WHERE id_client=3;
SET autocommit=0; -- Les "COMMIT" ne sont plus automatiques
SELECT * FROM td_Client WHERE id_client=3;
START TRANSACTION;
UPDATE td_Client SET nom="alex1" WHERE id_client=3;
SELECT * FROM td_Client WHERE id_client=3;
ROLLBACK;
SELECT * FROM td_Client WHERE id_client=3;
UPDATE td_Client SET nom="alex2" WHERE id_client=3;
SELECT * FROM td_Client WHERE id_client=3;
-- se connecter depuis un autre terminal sur cette base de données (nouvelle session) ; qu'elle est la valeur du champ nom ?
COMMIT;
SELECT * FROM td_Client WHERE id_client=3;
ROLLBACK;
SELECT * FROM td_Client WHERE id_client=3;
SELECT @@autocommit;
SELECT * FROM td_Client WHERE id_client=3;
UPDATE td_Client SET nom="alex ***** 1" WHERE id_client=3;
SELECT * FROM td_Client WHERE id_client=3;
CONCLUSION : Propriétés des transactions : ACIDité [Harder&Reuter, ACM CS 15(4), 1983]
https://makina-corpus.com/devops/bien-debuter-avec-les-transactions-sql
http://sys.bdpedia.fr/transactions.html https://sgbd.developpez.com/tutoriels/cours-complet-bdd-sql/?page=concurrence-et-reprise#LXI
DROP TABLE IF EXISTS td_Spectacle, td_Journal;
CREATE TABLE td_Spectacle (
INTEGER NOT NULL,
id_spectacle VARCHAR(30) NOT NULL,
titre INTEGER NOT NULL CHECK(nb_places_offertes>=0),
nb_places_offertes INTEGER NOT NULL CHECK(nb_places_libres>=0),
nb_places_libres DECIMAL(10,2) NOT NULL,
tarif PRIMARY KEY (id_spectacle)
);
CREATE TABLE td_Journal (
id INTEGER AUTO_INCREMENT,
VARCHAR(30) NOT NULL,
libelle_transaction INTEGER,
client_id INTEGER,
nb_places INTEGER,
spectacle_id DECIMAL(19,4),
tarif PRIMARY KEY (id)
);
SET autocommit = 0;
DELETE FROM td_Client;
DELETE FROM td_Spectacle;
INSERT into td_Client VALUES (1, 'Philippe', 0, 2000);
INSERT into td_Client VALUES (2, 'Julie', 0, 350);
INSERT into td_Spectacle VALUES (1, 'Ben hur', 250, 50, 50);
INSERT into td_Spectacle VALUES (2, 'Tartuffe', 120, 30, 30);
COMMIT;
utiliser les variables en début de script (nb_place, spectacle_id et client_id)
SET @nb_place=20;
SET @spectacle_id=1;
SET @client_id=1;
SET autocommit = 0;
START TRANSACTION;
SELECT @nb_place,@spectacle_id,@client_id;
-- à compléter
-- à compléter
-- à compléter
-- à compléter
SELECT * FROM td_Spectacle;
SELECT * FROM td_Client;
SELECT * FROM td_Journal;
COMMIT;
SELECT * FROM td_Spectacle;
SELECT * FROM td_Client;
SELECT * FROM td_Journal;
REMARQUE : lors du COMMIT toutes les opérations valides sont exécutées => programme pour vérifier l’intégrité des données
http://sql.bdpedia.fr/transactions.html#s1-transactions
LOCK TABLES nom_table [AS alias_table] [READ | WRITE] [,....];
Conséquence de l’utilisation de verrou :
Accès uniquement au table verrouillée
Lorsqu’une session MySQL pose un verrou sur un élément de la base de données, cela veut dire qu’il restreint, voire interdit, l’accès à cet élément aux autres sessions MySQL qui voudraient y accéder.
session 1 (terminal)
LOCK TABLES td_Client READ, -- On pose un verrou de lecture sur td_Client
WRITE; -- et un verrou d'écriture sur td_Spectacle td_Spectacle
exécuter les 3 requêtes ci-dessous dans 2 terminaux
session 1 et Session 2 (terminal)
SELECT * FROM td_Client;
SELECT * FROM td_Journal;
SELECT * FROM td_Spectacle;
session 1 et Session 2 (terminal)
UPDATE td_Client SET nb_places_reservees = 1 WHERE id_client=1;
UPDATE td_Spectacle SET nb_places_libres = 2 WHERE id_spectacle=1;
session 1 (terminal)
UNLOCK TABLES ;
Verrous de TABLE et verrous de ligne Il est possible de poser un verrou sur une TABLE entière, ou seulement sur une ou plusieurs lignes d’une TABLE. Étant donné qu’un verrou empêche l’accès d’autres sessions, il est en général plus intéressant de poser un verrou sur la plus petite partie de la base possible.
Cette notion d’accès simultané aux données par plusieurs sessions différentes s’appelle la concurrence. Plus la concurrence est possible, donc plus le nombre de sessions pouvant accéder aux données simultanément est grand, mieux c’est. En effet, prenons l’exemple d’un site web. En général, on préfère permettre à plusieurs utilisateurs de surfer en même temps, sans devoir attendre entre chaque action de pouvoir accéder aux informations chacun à son tour. Or, chaque utilisateur crée une session chaque fois qu’il se connecte à la base de données (pour lire les informations ou les modifier). Préférez donc (autant que possible) les verrous de ligne aux verrous de TABLE !
Conséquences pour les autres sessions
Si une session a obtenu un verrou de lecture sur une TABLE, les autres sessions :
Si par contre une session a obtenu un verrou d’écriture, les autres sessions ne peuvent absolument pas accéder à cette TABLE tant que ce verrou existe.
SET autocommit = 0;
START TRANSACTION;
SELECT "P1";
SELECT SLEEP(15);
UPDATE td_Client SET nom="alexandre 1" WHERE id_client=1;
SELECT SLEEP(15);
UPDATE td_Spectacle SET titre="Ben hur 1" WHERE id_spectacle=1;
-- lancer l'autre script dans un terminal
SELECT "P1";
COMMIT;
SET autocommit = 0;
START TRANSACTION;
SELECT "P2";
UPDATE td_Spectacle SET titre="Ben hur 2" WHERE id_spectacle=1;
SELECT SLEEP(15);
UPDATE td_Client SET nom="alexandre 2" WHERE id_client=1;
SELECT "P2";
COMMIT;
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
doc
SHOW GLOBAL VARIABLES LIKE 'innodb_rollback_on_timeout';
SELECT * FROM td_Spectacle WHERE id_spectacle = 1 LOCK IN SHARE MODE;
UPDATE td_Spectacle SET nb_places_libres=5 WHERE id_spectacle = 1;
: pose un verrou partagé sur la prochaine clé sur tous les index que la lecture rencontre.
SELECT * FROM td_Spectacle WHERE id_spectacle = 1 FOR UPDATE;
UPDATE td_Spectacle SET nb_places_libres=10 WHERE id_spectacle = 1;
: pose un verrou exclusif sur la prochaine clé sur tous les index que la lecture rencontre.
http://samisd2003.free.fr/WinLAMP/MYSQL/innodb-locking-reads.html
niveau 0 : READ-UNCOMMITTED ; autorise la lecture sale
niveau 1 : READ-COMMITTED ; interdit la lecture sale
niveau 2 : REPEATABLE-READ (niveau par défaut) ; idem, et rend la session aveugle aux modifications (mêmes validées) faites par les autres sessions (“lecture cohérente” ou consistent read)
niveau 3 : SERIALIZABLE : idem, et fait comme si les transactions étaient validées les unes après les autres et non en même temps
SELECT @@TX_ISOLATION;
'isolation
# Et voici les commandes pour régler le niveau dSET session transaction isolation level read uncommitted;
SET session transaction isolation level read committed;
SET session transaction isolation level repeatable read;
SET session transaction isolation level serializable;
https://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-isolation-levels.html
https://stackoverflow.com/questions/6121917/automatic-rollback-if-commit-transaction-is-not-reached
MySQL 8+ SELECT @@transaction_isolation; -- session
SELECT @@global.transaction_isolation; -- global
https://mysql.developpez.com/faq/?page=Transactions
https://www.canal-u.tv/chaines/inria/transactions-et-concurrence/degres-d-isolation-dans-les-sgbd
article sympa :
https://medium.com/neoxia/deepdive-voyage-au-pays-des-lock-mysql-innodb-2bb043f9739
https://fr.wikibooks.org/wiki/MySQL/Proc%C3%A9dures_stock%C3%A9es
'45000' SET MESSAGE_TEXT = 'Problème: pas assez de places disponibles.'; SIGNAL SQLSTATE
DROP PROCEDURE IF EXISTS sp_commande_spectacle ;
//
DELIMITER
CREATE PROCEDURE sp_commande_spectacle(IN p_nb_places INT, IN p_client_id INT, IN p_spectacle_id INT)
BEGIN
START TRANSACTION;
-- à compléter
-- à compléter
-- à compléter
-- à compléter
COMMIT;
END; //
DELIMITER ;
SELECT * FROM td_Spectacle;
SELECT * FROM td_Client;
SELECT * FROM td_Journal;
CALL sp_commande_spectacle(25,1,1);
SELECT * FROM td_Spectacle;
SELECT * FROM td_Client;
SELECT * FROM td_Journal;
https://stackoverflow.com/questions/6121917/automatic-rollback-if-commit-transaction-is-not-reached
https://khanrahim.wordpress.com/2010/05/16/transaction-with-stored-procedure-in-mysql-server/
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
ROLLBACK;
END;
CREATE DEFINER=##
@%
PROCEDURE sp_commande_spectacle(IN nb_places INT, IN client_id INT, IN spectacle_id INT)
DROP PROCEDURE IF EXISTS sp_commande_spectacle ;
//
DELIMITER
CREATE PROCEDURE sp_commande_spectacle(IN p_nb_places INT, IN p_client_id INT, IN p_spectacle_id INT)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
'45000' SET MESSAGE_TEXT = 'SQLError occured. Procedure error ROLLBACK';
SIGNAL SQLSTATE END;
DECLARE exit handler for sqlwarning
BEGIN
ROLLBACK;
'35000' SET MESSAGE_TEXT = 'SQLwarning occured. Procedure warning ROLLBACK';
SIGNAL SQLSTATE END;
START TRANSACTION;
-- à compléter
-- à compléter
-- à compléter
-- à compléter
COMMIT;
END; //
DELIMITER ;
Dans MySQL, un trigger (ou déclencheur) est une commande SQL définie par l’utilisateur qui est automatiquement invoquée lors d’une opération INSERT, DELETE ou UPDATE. Le code trigger est associé à une table et est détruit une fois que la table est supprimée.
un trigger, c’est quoi ?https://www.w3resource.com/mysql/mysql-triggers.php
présentation (voir article)
//
DELIMITER CREATE TRIGGER [TRIGGER_NAME]
TRIGGER TIME] [TRIGGER EVENT]
[ON [TABLE]
FOR EACH ROW
TRIGGER BODY]//
[ DELIMITER ;
exemple :
DELIMITER #
CREATE TRIGGER trigger_CLIENT_insert_before
BEFORE INSERT
ON td_Client
FOR EACH ROW
BEGIN
IF NEW.nom='' THEN
'45000'
SIGNAL SQLSTATE SET MESSAGE_TEXT = 'Le nom ne peut pas etre une chaine vide';
END IF;
END; #
DELIMITER ;
autre solution
IF NEW.nom='' THEN
'45000'
SIGNAL SQLSTATE SET MESSAGE_TEXT = 'Le nom ne peut pas etre une chaine vide';
ELSE
SET NEW.nom=LOWER(NEW.nom);
END IF;
pour tester
TRIGGERS;
SHOW SELECT * FROM td_Client;
INSERT into td_Client VALUES (3, 'SEBastien', 0, 350);
INSERT into td_Client VALUES (4, '', 0, 350);
SELECT * FROM td_Client;
DROP TRIGGER trigger_CLIENT_insert_before;
DELETE FROM td_Client WHERE id_client>2;
DROP TABLE IF EXISTS td_log_client_update;
CREATE TABLE td_log_client_update (
id INTEGER AUTO_INCREMENT,
INTEGER,
client_id
updated_date DATETIME,VARCHAR(255),
updated_by PRIMARY KEY (id)
);
DROP TRIGGER td_Client_after_update;
//
DELIMITER
CREATE TRIGGER td_Client_after_update
AFTER UPDATE
ON td_Client FOR EACH ROW
BEGIN
DECLARE vUser varchar(50);
-- Find username of person performing the INSERT into table
SELECT USER() INTO vUser;
-- Insert record into audit table
-- à compléter
-- à compléter
END; //
DELIMITER ;
UPDATE td_Client SET nom='SEBastien' , nb_places_reservees = nb_places_reservees + 1
WHERE id_client=1;
SELECT * FROM td_Client;
SELECT * FROM td_log_client_update;
IF NEW.nom='' THEN
'45000'
SIGNAL SQLSTATE SET MESSAGE_TEXT = 'Le nom ne peut pas etre une chaine vide';
ELSE
-- à compléter
-- à compléter
END IF;
https://www.orleans-informatique.com/info/sio/exercices/TP%20N%C2%B05%20-%20SQL%20et%20triggers.pdf
https://www.orleans-informatique.com/etudiants/informatique/bts-sio/sql-conception-base-donnees