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 6.06.2012, 10:57:24
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...


--------------------
Wolałem języki z rodziny C ale poszedłem na łatwizne...
Go to the top of the page
+Quote Post
uupah5
post 7.06.2012, 20:29:38
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 8.06.2012, 08:59:11
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.


--------------------
Wolałem języki z rodziny C ale poszedłem na łatwizne...
Go to the top of the page
+Quote Post
Crozin
post 8.06.2012, 09:34:39
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 8.06.2012, 10:04:07
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.


--------------------
Wolałem języki z rodziny C ale poszedłem na łatwizne...
Go to the top of the page
+Quote Post

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

 



RSS Wersja Lo-Fi Aktualny czas: 15.07.2025 - 18:00