Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [PHP][Doctrine][MySQL] LOCK i UNLOCK, Strona zawieszona na kilka godzin...
Adi32
post
Post #1





Grupa: Zarejestrowani
Postów: 348
Pomógł: 26
Dołączył: 8.10.2008
Skąd: Lublin

Ostrzeżenie: (0%)
-----


Witajcie.

(temat wisiał niespełna dobę w dziale Forum/Bazy danych/MySQL jednak postanowiłem go przenieść tutaj.
Mimo, że jest to mniej odpowiedni temat to w niewielkim stopniu pasuje a jest tutaj większy ruch).

Teoretycznie wszystkiego mógłbym się dowiedzieć chociażby z manuala, googla itp. ale postanowiłem napisać ponieważ przy aktualnym projekcie potrzebuje mieć pewność co do niektórych rzeczy.

Słowo wstępu.
Przeważnie korzystam z Doctrine ORM. Pojawiła się sytuacja, która zmusza mnie do skorzystania z blokowania tabel (przynajmniej tak mi się wydaje) a to co znalazłem w manualu Doctrina na temat blokowania nic mi nie pomogło, dlatego kod nieco się przeplata.

(1) - Zasada działania blokowania tabel...
Muszę w pewniej tabeli wykonać pewne akcie związane z usunięciem starych rekordów i dodaniem nowych. Ważne jest aby w trakcie wykonywania tych akcji nikt nie mógł nawet czytać z tej tabeli a gdyby jednak próbował to najlepiej jakby jego przeglądarka chwileczkę poczekała na zwolnienie blokady.

w tym celu zaprodukowałem taki kod:

  1. # Blokujemy tabele żeby nikt z niej nie czytał gdy będziemy ją przetwarzać
  2. Library_Baza::getInstance()->query("LOCK TABLES test_acl READ");
  3.  
  4. # kasujemy stare dane
  5. Doctrine_Query::create()->delete("TestAcl")->where("id_uzytkownik = $id")->execute();
  6.  
  7. //pre ($for_save);
  8.  
  9. # dodajemy nowe dane
  10. foreach ($for_save as $save) {
  11. $nowy = new TestAcl;
  12. $nowy->id_uzytkownik = $id;
  13. $nowy->fromArray($save);
  14. $nowy->save();
  15. }
  16.  
  17. # odblokowywujemy tabele
  18. Library_Baza::getInstance()->query("UNLOCK TABLES");


Czy jest on w porządku pod każdym względem? Czy spełnia przynajmniej to zadanie o które mi chodzi?

(2) - Dlaczego wykonanie powyższego kodu (testuje go od 3 dni) daje czasami taki efekt, że:
Dosłownie klikam "zapisz" co powoduje wykonanie się tego kodu i od tej pory przeglądarka czeka na odpowiedź z servera nawet kilka godzin? Można odświeżać, usuwać kod itp - nic nie pomaga. Czy dzieje się tak dlatego, że została zablokowana tabela (z której korzysta każda część skryptu) i cały czas czeka na jej odblokowanie?

(opcjonalnie) - Czy mogę ten efekt uzyskać bez przeplatania? Wykorzystując tylko Doctrine?


Miałem jeszcze jakieś pytania ale wyleciały mi z głowy...
Go to the top of the page
+Quote Post
uupah5
post
Post #2





Grupa: Zarejestrowani
Postów: 207
Pomógł: 18
Dołączył: 4.09.2010
Skąd: warszawa

Ostrzeżenie: (0%)
-----


Cytat(Adi32 @ 6.06.2012, 11:57:24 ) *
Pojawiła się sytuacja, która zmusza mnie do skorzystania z blokowania tabel (przynajmniej tak mi się wydaje)

moim zdaniem źle kombinujesz. istnieje bardzo niewielka szansa, że faktycznie blokowanie tabeli jest w twoim przypadku prawidłowym rozwiązaniem problemu.
ponieważ nie napisałeś nic więcej PO CO robisz blokowanie, skupiając się na JAK to zrobić, to proponuję abyś naświetlił szerzej kontekst.
a jako zadanie domowe;), pogooglaj w tematach:
- transakcje
- blokowanie tabeli a wydajność
- silniki mysql, różnice/zalety
- granularity locking




Go to the top of the page
+Quote Post
Adi32
post
Post #3





Grupa: Zarejestrowani
Postów: 348
Pomógł: 26
Dołączył: 8.10.2008
Skąd: Lublin

Ostrzeżenie: (0%)
-----


Dziękuję za odpowiedź, już traciłem nadzieję.

Na transakcjach się znam, przynajmniej tak mi się wydaje...
W moim przypadku sprawę wydajności można pominąć, chodzi o jedną małą blokadę w pewniej okoliczności.
Korzystam z silnika InnoDB ze względu na relacyjność. W połączeniu z Doctrinem i warstwą aplikacji "Model" zapytania łączone to sama przyjemność.
Wiem, że z pewnych względów zapytania natywne są lepsze jeżeli chodzi o wydajność ale przy niewielkich projektach różnica jest znikoma, tymbardziej jeżeli stosuje się system cachowania, który mam zamiar wprowadzić.

Trochę zszedłem z tematu także opiszę najpierw o co mi tak na prawdę chodzi.

Postanowiłem zrobić jakiś nowy system ACL w pełni zarządzalny który uwzględniałby wszystkie lokacje i akcje z nimi związane.

Administrator posiada drzewo (mapę strony) dla określonego użytkownika.

Modul
--Controller
----Akcja
------Parametr (opcjonalnie)

I system checkboxów do zaznaczanie czy użytkownik ma posiadać dostęp czy nie.

tabela w bazie jest prosta, zawiera kolumny:

id_uzytkownik, module, controller, action, param

Administrator ma dostęp wszędzie więc wpis wygląda tak:

1,*,*,*,*

Użytkownik który ma wszelkie prawa odnoście strony i może jedynie zajrzeć do panelu administracyjnewgo będzie psoiadał 2 wpisy:

2,index,*,*,*
2,admin,*,-,-

I to już działa ale chciałbym aby tabela była zablokowana w momencie gdy admin zmienia uprawnienia, ponieważ zmiana uprawnień polega na skasowaniu wszystkich wpisów dla danego użytkownika i dodania nowych.

Próbowałem różnych akcji względem tabeli test_acl i rezultat jest taki, że teraz mogę jedynie z niej czytać. Nie mogę w niej nic zmienią ani usunąć nawet z poziomu phpmyadmina. Jak spróbuje to strona się zawiesza - ładuje się, czeka na odpowiedź z serwera aż usune ciasteczka sesji.

Pozdrawiam.
Go to the top of the page
+Quote Post
Crozin
post
Post #4





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


1. Używasz Doctrine w wersji 1.x, może już czas na przesiadkę na 2.2/2.3? O ile dobrze pamiętam nowa wersja wspiera blokowanie tabel - ale to tylko taka mała uwaga.
2. W rzeczy samej, może dojść do jakiegoś deadlocka.
3. Tutaj w zupełności wystarczy Ci najzwyklejsza transakcja, z poziomu PDO (nie trzeba się nawet bawić w zmianę poziomu izolacji). Co więcej nie ma potrzeby blokowania użytkownika (tabeli). Po prostu odczyta starą zawartość w momencie gdy nowa jest generowana - jest to przecież jedna z czterech podstawowych cech transakcji (ACID).
4. Czyli ostatecznie (w Doctrine będzie to wyglądało niemal identycznie)
  1. try {
  2. $pdo->beginTransaction();
  3.  
  4. $pdo->query('DELETE ... WHERE user_id = 5;');
  5. $stmt = $pdo->prepare('INSERT ... user_id = 5 ...;');
  6.  
  7. for (...) {
  8. $pdo->execute(array('param1' => 'value', 'param2' => 'value'));
  9. }
  10.  
  11. $pdo->commit();
  12. } catch (...) {
  13. $pdo->rollback();
  14. }
5. Jeżeli użytkownik (user_id#5) wykona zapytanie SELECT, w momencie gdy powyższy kod będzie dopiero na etapie pętli for, zwrócone zostaną wyniki sprzed ustanowienia transakcji, czyli sprzed usunięcia rekordów dot. tego użytkownika.

Ten post edytował Crozin 8.06.2012, 09:36:37
Go to the top of the page
+Quote Post
Adi32
post
Post #5





Grupa: Zarejestrowani
Postów: 348
Pomógł: 26
Dołączył: 8.10.2008
Skąd: Lublin

Ostrzeżenie: (0%)
-----


Cytat(Crozin @ 8.06.2012, 10:34:39 ) *
1. Używasz Doctrine w wersji 1.x, może już czas na przesiadkę na 2.2/2.3? O ile dobrze pamiętam nowa wersja wspiera blokowanie tabel - ale to tylko taka mała uwaga.


Korzystałem z Doctrina2 przelotnie (specialnie zaktualizowałem wersję php do nowszej) jednak wykonywanie zapytań było mniej wygodne - spodobała mi sie formuła z Doctrine1. Być może za słabo zapoznałem się z D2?

Cytat(Crozin @ 8.06.2012, 10:34:39 ) *
2. W rzeczy samej, może dojść do jakiegoś deadlocka.

A jak teraz z niego wyjść? W manualu SQL znalazłem różne polecenia i zapytania jednak mam Access Denited przy próbie ich wykonania. Uważam że skoro bez konta roota zrobiłem deadlocka to i powinienem móc go cofnąć...

Cytat(Crozin @ 8.06.2012, 10:34:39 ) *
3. Tutaj w zupełności wystarczy Ci najzwyklejsza transakcja, z poziomu PDO (nie trzeba się nawet bawić w zmianę poziomu izolacji). Co więcej nie ma potrzeby blokowania użytkownika (tabeli). Po prostu odczyta starą zawartość w momencie gdy nowa jest generowana - jest to przecież jedna z czterech podstawowych cech transakcji (ACID).


No faktycznie, masz rację - nie pomyślałem o tym.

Cytat(Crozin @ 8.06.2012, 10:34:39 ) *
4. Czyli ostatecznie (w Doctrine będzie to wyglądało niemal identycznie)
  1. try {
  2. $pdo->beginTransaction();
  3.  
  4. $pdo->query('DELETE ... WHERE user_id = 5;');
  5. $stmt = $pdo->prepare('INSERT ... user_id = 5 ...;');
  6.  
  7. for (...) {
  8. $pdo->execute(array('param1' => 'value', 'param2' => 'value'));
  9. }
  10.  
  11. $pdo->commit();
  12. } catch (...) {
  13. $pdo->rollback();
  14. }
5. Jeżeli użytkownik (user_id#5) wykona zapytanie SELECT, w momencie gdy powyższy kod będzie dopiero na etapie pętli for, zwrócone zostaną wyniki sprzed ustanowienia transakcji, czyli sprzed usunięcia rekordów dot. tego użytkownika.


Dzięki Crozin.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 22.08.2025 - 12:57