FoRuM MxC - TuToriale CounTer StriKe, DC++ & OtheR
Doriti să reactionati la acest mesaj? Creati un cont în câteva clickuri sau conectati-vă pentru a continua.

Tranzactiile MysQl Folosind PHP

In jos

Tranzactiile MysQl Folosind PHP Empty Tranzactiile MysQl Folosind PHP

Mesaj  Admin Joi Ian 10, 2008 6:00 pm

Tranzactii
O tranzactie consta din una sau mai multe instructiuni SQL, care sunt executate complet ca un intreg, iar toate modificarile aparute in baza de date accesata, sunt salvate permanent folosind comanda mysql “commit”, sau anulate folosind comanda “rollback”.
ATENTIE!!! Pentru a putea folosi corect tranzactiile trebuie ca tabelele din baza de date sa fie de tipul INNODB.

Un prim exemplu (avem o baza de date "tutoriale" si o tabela "useri"):
(comenzile de mai jos sunt date in fereastra de lucru cu MySQL, iar prompterul este specificat prin mysql>)
Code:
mysql> use tutoriale;
Database changed
mysql> Select * from useri;
Empty set (0.00 sec)

mysql> Start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator1');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator2');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator3');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator4');
Query OK, 1 row affected (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> Select * from useri; //tabela useri nu va contine inregistrarile deoarece s-a dorit renuntarea la acesta, prin executia lui rollback.
Empty set (0.00 sec)

mysql> Start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator5');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator6');
Query OK, 1 row affected (0.00 sec)

mysql> Select * from useri; //tabela contine inregistrarile, dar aceste inregistrari pot fi citite doar de catre cel ce a initiat tranzactia (depinde acum si de nivelul de izolare)
+---------+-------------+
| user_id | user_name |
+---------+-------------+
| 5 | utilizator5 |
| 6 | utilizator6 |
+---------+-------------+
2 rows in set (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator7');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator8');
Query OK, 1 row affected (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> Select * from useri; //tabela ramane goala, deoarece sa dorit renuntarea la inregistrarile anterioare prin executia lui rollback
Empty set (0.00 sec)

mysql> Start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator9');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator10');
Query OK, 1 row affected (0.00 sec)

mysql> Insert into useri(user_name) values ('utilizator11');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.03 sec)

mysql> Select * from useri; // tabela contine inregistrarile deorece s-a executat commit.
+---------+--------------+
| user_id | user_name |
+---------+--------------+
| 9 | utilizator9 |
| 10 | utilizator10 |
| 11 | utilizator11 |
+---------+--------------+
3 rows in set (0.00 sec)

mysql> quit


Motivatii pentru folosirea tranzactiilor *
Cateva motive pentru care folosirea tranzactiilor isi gaseste justificare ar putea fi:
Necesitatea de a realiza operatii atomice - presupune situatia in care se doreste ca mai multe operatii separate, sa se execute impreuna, intr-o maniera atomica. Aceasta abordare este utila, spre exemplu, in cazul in care dorim sa facem modificari asupra mai multor tabele aflate eventual in baze de date diferite pe masina locala sau in retea, modificari care isi au locul doar facute impreuna. Mai particular, putem da drept exemplu cazul transferului de bani dintr-un cont in altul, transfer care presupune ca, o data suma de bani citita si modificata din contul sursa, sa fie scrisa si in contul destinatie. Daca operatia nu decurge in mod atomic, se va intampla, spre exemplu, ca suma sa nu fie transferata efectiv si sa se regaseasca si in sursa si in destinatie, ceea ce reprezinta un transfer incorect (inconsistenta). Tranzactiile rezolva aceasta problema prin garantarea principiului "totul sau nimic".
Caderi ale masinii sau ale retelei - in cazul in care bazele de date sunt distribuite in retea, situatiile ce pot determina exceptii cresc. De asemenea, masina pe care se afla bazele de date poate sa cada la un moment dat. In cazul in care caderea se petrece atunci cand se face o scriere in baza de date, aceasta poate sa fie deteliorata irecuperabil. Tranzactiile sunt cele care ofera suport pentru recuperarea datelor.
Partajarea acelorasi date de mai multi utilizatori - intr-un intranet apare frecvent posibilitatea accesarii unei baze de date de mai multi clienti in acelasi timp. Conexiunile concurente pot determina o serie de erori, printre care amintim:
Citiri murdare (eng. dirty reads) - aceasta problema apare in cazul in care un client citeste date dintr-o baza de date ce nu a fot salvata definitiv pe suportul de stocare de un alt client prin operatia commit.
Citiri nerepetabile (eng unreapeatable reads) - aceasta problema apare atunci cand un client citeste date dintr-o tabela, dar in acelasi timp acestea sunt modificate de un alt client.
Problema fantomelor (eng. phantoms) - aceasta problema presupune aparitia unei noi multimi de date intre doua operatii de citire facute de un acelasi client.
Diferenta intre citiri nerepetabile si problema fantomelor este accea ca in primul caz este vorba de modificari de date existente, iar in al doilea caz este vorba de date cum ar fi inregistrari sau coloane noi, care nu existau anterior.

ACID *
In cazul folosirii tranzactiilor, un numar de patru principii sunt totdeauna garantate pentru operatiile efectuate. Aceasta poarta si numele de proprietatile ACID ale tranzactiilor, denumire care vine de la abrevierea celor patru nume de proprietati: Atomicitate (eng. Atomicity), Consistenta (eng. Consistency), Izolare (eng. Isolation) si Durabilitate (eng. Durability).
Atomicitate - garanteaza ca mai multe operatii sunt grupate impreuna si apar ca o singura unitate de continua de lucru. De asemenea, atomicitatea garanteaza ca mai multe operatii sunt executate toate odata sau nici una.
Consistenta - garanteaza ca starea sistemului ramane consistenta dupa ce o tranzactie este terminata. Prin stare consistenta se intelege o stare in care sistemul respecta o anumita regula sau, mai general anumite reguli. Sistemul poate trece prin stari temporare de inconsistenta, dar, in final, tranzactiile garanteaza consistenta sistemului.
Izolare - permite ca mai multi clienti sa citeasca si sa scrie intr-o baza de date fara sa stie unul de altul, deoarece tranzactiile sunt izolate una de alta. Este metoda prin care se rezolva problemele legate de partajarea resurselor. Acest lucru este util pentru cazul mai multor clienti care acceseaza baza de date in acelasi timp, fiecare avand impresia ca el este singurul conectat. Tranzactiile asigura blocari (eng. locks) automate ale datelor sesibile, care asigura ca o data ce un client acceseaza zona sensibila (critica), nimeni altcineva nu mai poate face acest lucru. Inca odata trebuie remarcat faptul ca, prin folosirea tranzactiilor, programatorii nu mai trebuie sa scrie cod pentru tratarea concurentelor multiple, deoarece de acest lucru se ocupa DBMS-ul(database management system) vezi http://en.wikipedia.org/wiki/DBMS. Tot ce trebuie sa facem este sa specificam tipul de acces.
Durabilitate - garanteaza ca toate actualizarile permanent salvate folosind "commit" supravietuiesc caderilor de sistem, erorilor de retea, caderii ale hard diskului sau caderilor de tensiune. Acest lucru este posibil datorita faptului ca tranzactiile pastreaza un fisier de siguranta in care sunt inregistrate toate operatiile asupra bazelor de date, inainte ca acestea sa fie efectiv comise. Datele salvate permanent pot fi astfel reconstituite prin reaplicarea pasilor salvati in acest fisier.

Niveluri de izolare *
Nivelurile de izolare necesita o abordare mai speciala, mai atenta, din partea programatorilor tocmai datorita problemelor ce apar in momentul partajarii acelorasi resurse de catre mai multi clienti.
Programatorul aplicatiei poate specifica nivelul de izolare al tranzactiilor efectuate. Izolarea poate fi stricta sau relaxata. O izolare stricta determina separarea totala a tranzactiilor, care se realizeaza prin restrictii impuse asupra datelor din tabelele bazei, soldate cu o scadere a performantelor. De aceea trebuie ales cu grija nivelul de izolare de care este nevoie.
Care sunt nivelurile de izolare?
READ UNCOMMITTED - Nu ofera nici un fel de izolare, dar garanteaza performanete inalte in ceea ce priveste viteaza de acces. Acest nivel de izolare nu este indicat in cazul conexiunilor concurente si, mai ales, in cele cu date sensibile, cum ar fi sistemul bancar, din cauza problemelor inevitabile care pot aparea.
READ COMMITTED - Rezolva problema citirilor murdare. Folosind acest nivel de izolare, nu se vor citi datele care au fost modificate in tabela accesata, fara a fi salvate definitiv prin "commit".
REPEATABLE READ - Rezolva problemele citirii murdare si citirii nerepetabile. Acesta este nivelul de izolare implicit pentru servere ca Oracle si MySQL.
SERIALIZABLE - Rezolva problemele citirii murdare, citirii nerepetabile si problema fantomei. Folosind acest nivel de izolare, tranzactiile sunt izolate total, ceea ce inseamna ca ele se vor executa secvential, independent una fata de alta. Alegerea acestui nivel va determina incetinirea simtitoare a tranzactiilor concurente.


Gata cu teoria hai sa vedem cum putem folosi tranzactiile in codul nostru php.

Am creat o clasa care ca si utilizarea este asemanatoare cu folosirea tranzactiilor in .NET si JAVA.

fisier transaction.php **- instantierea si folosirea acestei clase se poate face corect doar daca aveti versiunea PHP 5.X
<?php class tranzactii{ public $conexiune; private $tablou_interogari; public $tablou_rezultate; private $execut=false; function tranzactii($host,$user,$parola){ $this->conexiune=mysql_connect($host,$user,$parola); } public function start($nivel_izolare=3){ if($this->conexiune!=NULL){ if($nivel_izolare==1){ mysql_query('set transaction isolation level read uncommitted',$this->conexiune) or die(mysql_error($this->conexiune)); } elseif($nivel_izolare==2){ mysql_query('set transaction isolation level read committed',$this->conexiune) or die(mysql_error($this->conexiune)); } elseif($nivel_izolare==3){ mysql_query('set transaction isolation level repeatable read',$this->conexiune) or die(mysql_error($this->conexiune)); } else{ mysql_query('set transaction isolation level serializable',$this->conexiune) or die(mysql_error($this->conexiune)); } mysql_query('Start Transaction',$this->conexiune) or die(mysql_error($this->conexiune)); } } private function pastreaza(){ mysql_query('Commit',$this->conexiune); } private function renunta(){ mysql_query('Rollback',$this->conexiune); } public function rezultat(){ return $this->execut; } public function adauga($tablou_sql){ $this->tablou_interogari[]=$tablou_sql; } public function executa(){ $this->execut=true; try{ if(count($this->tablou_interogari)>0 and $this->conexiune!=NULL){ foreach($this->tablou_interogari as $keye=>$valoare){ $selecteaza_bd=mysql_select_db($valoare[0],$this->conexiune); if($selecteaza_bd){ $rezultat_fraza_sql=mysql_query($valoare[1],$this->conexiune); if($rezultat_fraza_sql!=false){ $this->tablou_rezultate[]=array('SQL No.'=>$keye,'DB'=>$valoare[0],'SQL phrase'=>$valoare[1],'SQL result'=>'Good','Error'=>''); } else{ $this->tablou_rezultate[]=array('SQL No.'=>$keye,'DB'=>$valoare[0],'SQL phrase'=>$valoare[1],'SQL result'=>'Bad','Error'=>mysql_error($this->conexiune)); $this->execut=false; } } else{ $this->execut=false; $this->tablou_rezultate[]=array('SQL No.'=>$keye,'DB'=>$valoare[0],'SQL phrase'=>$valoare[1],'SQL result'=>'No result','Error'=>'Am intalnit o problema la selectarea bd-ului'); } } if($this->execut==true){ $this->pastreaza(); } else{ $this->renunta(); } } } catch(Exception $e){ $this->execut=false; $this->tablou_rezultate[]=array('Exceptie'=>$e->getMessage()); } } public function inchide_conexiune(){ @mysql_close($this->conexiune); } } ?>
Ok, cum folosim aceasta clasa? In primul rand se include fisierul transaction.php, se instantiaza clasa, se alege nivelul de izolare, se adauda frazele SQL dorite si apoi le executam pe toate ca un intreg. Putem de asemeni sa aflam daca toate interogarile au fost executate corect si salvate permanent in baza de date, sau care este rezultatul frazelor SQL (ideal de folosit atunci cand una sau mai multe dintre acestea nu indeplinesc anumite conditii).

fisierul testare_tranzactii.php **
<?php include ('transaction.php'); ?> <html> <head> <title>Untitled Document</title> </head> <body> <?php $conexiune = new tranzactii('host','user','parola'); // se instantiaza clasa $conexiune->start(4); // se alege nivelul de izolare. 1-read uncommitted 2-read committed 3-repeatable read (implicit) 4-serializable $conexiune->adauga(array('java','Insert into studenti (nume_student) values (\'Diesel\')')); // se adauga frazele SQL respectiv un tablou al carui pozitie 0 este numele bd-ul iar pozitia 1 va fi fraza SQL dorita. Aceasta fraza SQL are sintaxa corect scrisa. $conexiune->adauga(array('java','Insert into studenti (\'Diesel\')')); // se adauga a doua fraza SQL. Aceasta fraza SQL are sintaxa incorect scrisa. $conexiune->executa(); // se executa frazele SQL adaugate. Prima fraza SQL este corect scrisa, in consecinta ea va fi executata cu succes. A doua fraza SQL este incorect scrisa, in consecinta ea nu va fi executata cu succes, acest lucru determinand executarea lui rollback. Deci ambele fraze sql nu sunt salvate in bd. var_dump($conexiune->rezultat()); // $conexiune->rezultat() ne returneaza TRUE sau FALSE in functie de rezultatul executiei frazelor SQL. Daca cel putin o fraza SQL nu este executata cu succes atunci rezultatul lui $conexiune->rezultat() va fi FALSE. $conexiune->inchide_conexiune(); // in cazul in care se doreste inchiderea conexiunii cu MySQL print_r($conexiune->tablou_rezultate); // $conexiune->tablou_rezultate va returna un tablou ce va afisa rezultatele pentru fiecare fraza SQL adaugata. Va rog sa folositi print_r pentru a intelege structura tabloului. ?> </body> </html>

In cazul in care serverul MySQL este inchis pentru a nu obtine Warning la linia 10 din transaction.php, puteti pune @ inainte de a instantia clasa.
Tranzactiile pot fi folosit pe MySQL si pe versiuni mai vechi ale acestuia, insa cei de la MySQL recomanda ultima veriune (5.0)
Admin
Admin
FonDaToR FoRuM
FonDaToR FoRuM

masculin Numarul mesajelor : 96
Varsta : 32
Localizare : Bucuresti
Stare de spirit : Foarte Buna
Data de inscriere : 08/01/2008

http://WwW.MxC-NeTwoRk.FoRuMeR.Ro

Sus In jos

Sus

- Subiecte similare

 
Permisiunile acestui forum:
Nu puteti raspunde la subiectele acestui forum