![]() |
![]() ![]() |
![]() |
![]()
Post
#1
|
|
Grupa: Zarejestrowani Postów: 260 Pomógł: 14 Dołączył: 8.09.2011 Ostrzeżenie: (0%) ![]() ![]() |
Cachuje w pamieci wyniki zapytań, tj jesli raz odczytam jakis wiersz to trzymam go w pamieci az do konca wykonywania skryptu, i w razie czego z niego korzystam.
Sytuacja modelowa: Dwóch uzytkownikow poprzez skrypt WWW probują zmodyfikować rekord x w tabeli bazy danych. zalozmy ze w rekord x mozna wpisac jakas wartosc liczbową lub flagę _dont_modify jesli cos tam sie zakonczylo i juz nie powinnismy modyfikowac tego. 0. rekord x = 10. 1. Uzytkownik 1 odczytuje rekord x 2. uzytkownik 2 odczytuje rekord x 3. Uzytkownik 2 robi update obiekty x. Sprawdza (korzystajac z wartosci cache) czy (x != _dont_modify). Jest to prawda wiec moze edytowac: wpisuje flage _dont_modify 4. Skrypt 2 konczy swoje dzialanie 5. Uzytkownik 1 robi update obiektu x. Sprawdza (korzystajac z wartosci cache) czy (x!= _dont_modify). Jest to prawda (w cache jest stara wartosc nadal) wiec wpisuje wartosc np. 15. 6. Skrypt 1 konczy swoje działanie. Efekt? Uzytkownik zakonczyl np. swoja bitwe i zostal wpisany status _dont_modify. Jednoczesnie drugi uzytkownik podawal inny wynik ktory byl sprzeczny, i bitwa nie zostala zakonczona wg niego. Efekt jest taki ze mamy wyniki w drzewie, bitwa zostala uznana za zakonczoną, w dalszej czesci programu zmodyfikowane zostalo drzewo przechowujace turniej i utworzone kolejne bitwy miedzy graczami, a bitwa w dalszym ciagu ma nieustalony wynik! (przyjmuje ze bitwa jest zakonczona gdy jest ustawione dont_modify). Jak to rozwiazac? Transakcje? Ale to wtedy caly odczyt i zapis musialby byc w jednej transakcji... padnie mi wydajnosc na stronie. I drugie pytanie, jak podobny problem rozwiazac w cache opartym na plikach? Co do plikow myslalem nad rozwiazaniem ktore odczytuje plik przed samym zapisem i porownuje czy rekord ktory chcemy update'owac nie zmienil sie, i jesli sie zmienil to wyrzuci wyjatek ale to tez nie zalatwia sprawy, poniewaz jesli mam proces ktory jesli cos zajdzie to najpierw zapisuje wartosc do tabeli X a pozniej do pliku Y, i plik Y wyrzuci wyjatek i nic nie zapisze, a sterowanie i rozpoznawanie stanu tego obiektu opiera sie na wartosci Y, to mam nowa wartosc i stary stan co rowniez moze doprowadzic do szalonych bzdur. Niby moge zrobic flock(LOCK_EX) na ten plik cache, ale wtedy blokuje kazde kolejne wywolanie skryptu na 100ms, a updaty nie beda az tak czesto. Ogolnie to chce miec klase abstrakcji dostepu do zrodla danych ktore bedzie udostepnialo taki sam interfejs zarowno przy zapisie do plikow obiektow bazy danych jak rowniez do bazy danych (niektore rzeczy lepiej w plikach a niektore lepiej w DB). Poradzilem sobie zakładając locka LOCK_EX wtedy kiedy wiem ze bede cos zapisywal, a zakladam LOCK_SH jak tylko odczytuje i wyswietlam, mimo wszystko moze ktos zna lepsze rozwiazanie? Ten post edytował Orzeszekk 20.10.2011, 22:13:20 -------------------- "The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time."
Tom Cargill, Bell Labs |
|
|
![]()
Post
#2
|
|
Grupa: Zarejestrowani Postów: 39 Pomógł: 3 Dołączył: 17.09.2011 Ostrzeżenie: (0%) ![]() ![]() |
Cytat I drugie pytanie, jak podobny problem rozwiazac w cache opartym na plikach? W PHP się nie da. Musiałbyś użyć C/C++ a całość zrobić nie za pomocą biblioteki standardowej tylko API do operacji atomowych na plikach albo bardzo skomplikowanych struktur danych + użyć mutexów. Co jest tak skomplikowane, że i tak nie ma szans dobrze działać na serwerach z wieloma CPU. Cytat Niby moge zrobic flock(LOCK_EX) na ten plik cache, ale wtedy blokuje kazde kolejne wywolanie skryptu na 100ms, a updaty nie beda az tak czesto. FLOCK nie działa. Niby w dokumentacji jest że działa ok, ale na każdej większej witrynie istnieje ryzyko utraty danych jeśli korzystasz z plików bo jedyna operacja atomowa obsługiwana przez PHP to rename, jako tako działa jeszcze FLOCK/append, ale jako-tako nie znaczy że dobrze. Gubi dane prędzej czy później. Jeśli podczas zapisu do pliku padnie ci serwer albo go zresetujesz to będziesz miał śmieci. To nie jest tak, jak z bazą, że serwer pada robisz rebuild tabel i masz poprawne dane, najwyżej ostatnie pare transakcji się gubi. Jak ci serwer padnie podczas zapisu do pliku to będziesz miał albo część danych albo totalny śmietnik. Dlatego jedynym pewnym sposobem jest zapis do pliku tymczasowego i użycie rename i to jedynie pod linux bo pod windows nawet rename nie jest atomowe. Transakcja też sama nie rozwiąże problemu. Cytat 1. Uzytkownik 1 odczytuje rekord x 2. uzytkownik 2 odczytuje rekord x Zwykły select tu nic nie zmienia. Oba rekordy dalej mogą zostać odczytane przed zmianą nawet jeśli całość jest w transakcji. Tu jest potrzebny MUTEX a nie transakcja, to zupełnie co innego. Możesz użyć SELECT ... FOR UPDATE albo LOCK TABLES, ewentualnie GET_LOCK(rekord który będziesz zmieniał). Cytat Poradzilem sobie zakładając locka LOCK_EX wtedy kiedy wiem ze bede cos zapisywal, a zakladam LOCK_SH jak tylko odczytuje i wyswietlam, mimo wszystko moze ktos zna lepsze rozwiazanie? Tak, zapisujesz dane do pliku tymczasowego a kiedy zapiszesz już wszystko robisz rename. Inaczej będziesz gubił dane. W PHP działają dobrze jedynie dwie metody dostępu do plików: dopisywanie na końcu przez append / flock. Jak chcesz zmienić zawartość to musisz cały plik odczytać i zapisać od nowa. Ogólnie to sorry ale samo założenie tego cache'u jest bezsensowne. To samo i tak robi mysql. Jeśli nie zmieniałeś rekordu - to nie będzie odczytywał tabeli tylko dostaniesz wartość z wewnętrznego cache. Możesz sobie zrobić LOCK TABLES albo MUTEX na górze skryptu tylko, że mysql zrobi to samo standardowo i o wiele wydajniej. Ten post edytował xdev 5.11.2011, 09:20:54 |
|
|
![]() ![]() |
![]() |
Wersja Lo-Fi | Aktualny czas: 19.06.2025 - 21:55 |