Cachowanie zapytań SQL

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

Cachowanie zapytań SQL

Przypuśćmy, że masz własny dosyć rozbudowany serwis, ale niestety działa on
powolnie oraz wykorzystuje za dużo transferu. Co robić? W takim wypadku są dwa
wyjścia. Pierwsze zoptymalizować kod, a drugie skorzystać z cachowania zapytań
SQL. W dzisiejszym poście omówię te drugie rozwiązanie.

Raczkujesz w tematyce PHP? Jeśli tak to najpierw dowiedz się:
Co to są klasy?
Wykonywanie zapytań SQL w PHP (wiem, że to podstawa, ale może są jeszcze
ludzie na tyle uparci, że ciągle by korzystali z plików tekstowych)
Podstawowe operacje na plikach.

A więc do rzeczy, założyłem że, klasa do cachowania zapytań SQL będzie miała
następujące możliwości:
zapytanie z pominięciem cache (do wykonania takich zapytań jak INSERT czy
UPDATE)
Funkcja super-cache (do statycznych rzeczy, rzadko zmieniających się np. menu na
stronie, strony statyczne itp)
Zwykłe cachowanie zapytań
Wyczyszczenie cache

Jak widać klasa niż nadzwyczajnego nie robi, ale za to jaka przydatna, dzięki niej
można wyświetlić jakąś treść jeśli w danej chwili padł nam serwer SQL, co niestety
nieraz się zdarza. Najpierw zadeklarujemy potrzebne nam zmienne.

[php]
class cache{
var $zapytanie;
var $typ;
}
[/php]

Zmienna typ przechowuje co ma zrobić klasa. Już teraz powiem, że przyjmie ona
takie wartości jak:
no-cache

page 1 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

super-cache

Jeśli zmienna typ jest pusta zostanie dokonana funkcja z normalnych cachowaniem.
Teraz zajmiemy się dwiema funkcjami odpowiadającymi za połączenie z bazą
danych.
[php]
private function polacz(){
require('config/baza.php');
$polaczenie = @mysql_connect($serwer, $user , $haslo)
or die('Brak połączenia z serwerem MySQL.<br />Błąd: '.mysql_error());
$db = @mysql_select_db($baza, $connection)
or die('Nie mogę połączyć się z bazą danych<br />Błąd: '.mysql_error());

return $polaczenie;

}

private function rozlacz($connection){

@mysql_close($connection) or die('Nie można rozłączyć bazy ');
return 1;
}
[/php]

Połączenie odbywa się bezpośrednio przy pobieraniu danych z bazy MySQL i
ustawiłem je na prywatne, aby nie potrzebnie użytkownik jeszcze raz bezpośrednio
na stronie się z nią połączył.

Jedną z najważniejszych funkcji w tej klasie będzie cachuj($plik); Odpowiada ona
(jak łatwo się domyśleć) za cachowanie zapytania. Jej też nadam status prywatnej,
aby uniemożliwić jej wykonanie przez użytkownika. Jest to na tyle ważne, ponieważ
ona od razu zapisuje plik i zwraca jego zawartość, więc zrobiłby się dosyć duży

page 2 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

folder temp.

[php]
private function cachuj($plik){
$connection=$this->polacz() ;

$wynik = mysql_query( $this->zapytanie)
or die('Błąd zapytania');

while($r = mysql_fetch_array($wynik)) {
$zawartosc[$i]=$r;
$i++;
}

$zawartosc1=serialize($zawartosc);

ignore_user_abort(1); /*czytaj:
http://php.net/manual/pl/features.connection-handling.php */
file_put_contents($plik, $zawartosc1);

$this->rozlacz($connection);
return $zawartosc ;

}
[/php]
Połączenie i rozłączenie się z bazą danych znajduje się tutaj tylko dlatego, że jak
np. padłby serwer MySQL, a jakieś dane były już w cache to można wyświetlić
stronę, oprócz tego lekko wpływa to na prędkość strony, jedyny minus takiego
rozwiązania jest taki, że przy każdym zapytaniu do bazy danych funkcja się ciągle
włącza i rozłącza, co przy pustym folderze temp może trochę spowolnić ładowanie
strony.

page 3 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

Oprócz tego pojawia się tutaj funkcja serialize() oraz później pojawi się unserialize().
Obie te funkcje służą do przemiany tablicy w stringa oraz stringa w tablicę np. masz
sobie jakąś tablice i chcesz ja wrzucić w naszym wypadku do pliku, traktujesz ja
funkcja serialize() i zwraca ona pojedynczy tekst , potem jak chcesz mieć z
powrotem ta tablice traktujesz to funkcja unserialize() i masz z powrotem tablice z
zachowaniem jej struktury. Przykładzik w kodzie:

[sourcecode lang="php" collapse="true"]
<?php

$x=array('zero','jeden','dwa','trzy');
echo $x .'<Br />;
$x=serialize($x);
echo $x .'<Br />';
$x=unserialize($x);
echo '<pre>';
print_r($x);
echo '</pre>;
?>
[/sourcecode]
O zapisie i odczytu plików mówiłem już w poprzedniej notce, ale tutaj jeszcze się
usprawiedliwię dlaczego tak długo wtedy nawijałem o starszych wersjach PHP. Jeśli
funkcja file_put_contents() oraz file_get_contetnts(); nie będzie Ci działać musisz na
początku klasy dopisać funkcje (prywatne) podane w poprzedniej notce.

Funkcja cachuj przyjmuje parametr plik, który zostanie stworzony w funkcji
zapytanie, którą właśnie omówię.

Następną funkcją jaką dam pod obstrzał jest zapytanie();. Jest ona sercem tej klasy,
ponieważ steruje ona całym ruchem generowanym z MySQL do plików i z tych
plików na ekran usera :). Jest ona troszkę bardziej złożona niż, poprzednie dlatego
rozłożę ją na części.
[php]
public function zapytanie(){
include('config/cache.php');
if ($this->zapytanie!=''){

}
else return 'Brak zapytania do SQL';

page 4 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

}

[/php]
Najpierw dołączamy plik cache.php zawiera on najważniejsze zmienne potrzebne
do odczytania cache. Czyli czas świeżości zapisanego zapytania oraz lokalizacja
folderu temp, w fazie testowania mój plik wyglądał tak:
[php]
<?php

$czas=60;
$temp='temp';

?>
[/php]
Czas jest podany w sekundach. Jeśli chcesz aby było świeżość ciasteczka miała np.
godzinę wystarczy, że pomnożysz przez 60 (w racji przypomnienia lekcji fizyki z
klasy 2 gimnazjum, gdzie było mówione o wzorze V=s/t, ale to już taka ciekawostka
:P).

Następnie małe zabezpieczenie przed zapomnieniem napisaniem zapytania. Każdy
kod teraz wklejamy między ten blok If.

Wspominałem przy funkcji cachuj(); o atrybucie plik, aby dostarczyć funkcji tej
zmiennej należy wykonać dwie proste linijki kodu:
[php]
$nazwa=md5($this->zapytanie);
$plik= $temp .'/'.$nazwa.'.cache';
[/php]
Pewnie zdziwi co niektórych użycie tutaj szyfrowania MD5, jest tutaj z dwóch
powodów. Pierwszy jest taki, że trzeba było zabezpieczyć się jakoś przed znakami
specjalnymi, które nie mogą być użyte w nazwie pliku.
Drugi powód z kolei jest taki, że jakby już udało się zapisać zapytanie bez
szyfrowania MD5, udałoby się wyciągnąć wszystkie dane użytkownika np. z
zapytanie
SELECT 'mail' FROM 'userzy';
Jeśli zna rozszerzenie pliku (ja specjalnie dałem cache, ponieważ z takim jeszcze się
nie spotkałem) oraz folder, w którym przechowywane są pliki to bez problemu by
wyciągnął wszystkie w tym przypadku maile. Oczywiście pod warunkiem, że zostało
wykonane takie zapytanie.

page 5 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

Teraz zajmiemy się typami cachowania (co wspominałem na samym początku) czyli
nasze super-cache i no-cache. Wykorzystałem tutaj instrukcję warunkową switch,
ponieważ tutaj korzystanie tutaj z elseif graniczyłoby z głupotą.

[php]
switch ($this->typ){
case 'super-cache':
break;

case 'no-cache':
break;

default:
break
}
[/php]
W części super-cache oraz default, kod będzie bardzo podobny, różnić się jedynie
będzie pomnożeniem czasu ważności pliku w moim przypadku o 5 razy. No ale
najpierw należy sprawdzić, czy plik jaki potrzebujemy istnieje, jeśli nie to zaraz go
zrobimy :):
[php]
if (file_exists($plik) ){
}
else return $this->cachuj();
[/php]
Teraz pobierzemy datę ostatniej modyfikacji pliku, utworzymy datę wygaśnięcia
oraz sprawdzimy czy plik nadaje się do spożycia :). Skorzystałem tutaj z daty
ostatniej modyfikacji pliku, ponieważ w celu zoptymalizowania kodu funkcja cachuj
nie usuwa pliku lecz go modyfikuje zastępując jedynie zawartość (oszczędność 3 lub
4 linijek na klasie, ale zawsze coś:) )
[php]
$utworzono=filemtime($plik);
$wygasnie=$utworzono+$czas;
if($wygasnie > time()){
$odczytaj=file_get_contents($plik);
return unserialize($odczytaj);
} //if
else return $this->cachuj();
[/php]
W przypadku opcji super-cache linijka:
[php] $wygasnie=$utworzono+$czas;[/php]

page 6 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

Przybiera postać:

[php] $wygasnie=$utworzono+($czas*5);[/php]

Niby PHP rozpoznaje kolejność wykonywania działań, ale abym ja się nie pomylił
wolałem dać w nawiasie :).

Za to opcja no-cache, jest bardziej podobna do funkcji cachuj, odpowiednio łączy się
z bazą danych, zadaje zapytanie zapisuje zawartość do zmiennej i ją zwraca:

[php]
case 'no-cache':
$connection=$this->polacz();

//i zapis

$wynik = mysql_query( $this->zapytanie);

while($r = mysql_fetch_array($wynik)) {
$zawartosc[$i]=$r;
$i++;
}

$this->rozlacz($connection);
return $zawartosc;

break;
[/php]
Co do samej funkcji zapytanie to już tyle, przedstawię ją wam jeszcze w całej swej
okazałości, z rozrzutności zajęła mi ta funkcja lekko ponad 50 lini.

page 7 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

[sourcecode lang="php" collapse="true"]

function zapytanie(){
include('config/cache.php');
if ($this->zapytanie!=''){
$nazwa=md5($this->zapytanie);
$plik= $temp .'/'.$nazwa.'.cache';

switch ($this->typ){
case 'super-cache':

if (file_exists($plik) ){

$utworzono=filemtime($plik);
$wygasnie=$utworzono+($czas*5);
if($wygasnie > time()){
$odczytaj=file_get_contents($plik);
return unserialize($odczytaj);
}
else return $this->cachuj($plik);
}
else return $this->cachuj($plik);
break;

case 'no-cache':
$connection=$this->polacz();

//zadanie pytania, przesłanie go do tablicy zawartość z serializowanie danych
//i zapis

$wynik = mysql_query( $this->zapytanie);

while($r = mysql_fetch_array($wynik)) {

page 8 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

$zawartosc[$i]=$r;

$i++;
}

$this->rozlacz($connection);
return $zawartosc;

break;

default:

if (file_exists($plik) ){
$utworzono=filemtime($plik);
$wygasnie=$utworzono+$czas;
if($wygasnie > time()){
$odczytaj=file_get_contents($plik);
return unserialize($odczytaj);
} //if
else return $this->cachuj();
} //if

else return $this->cachuj();

break;

}//switch

}//if
else return 'brak zapytania';

page 9 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

}//zapytanie()

[/sourcecode]
Pozostała jedynie funkcja wyczyść(); jest ona najkrótsza, i szczerze mówiąc za
bardzo nie potrzebna, skorzystać z niej można np. przy edycji stron statycznych,
aby nowa zawartość pojawiła się wcześniej. Skorzystałem tutaj z funkcji unlink oraz
opendir i closedir. Pojawia się też instrukcja warunkowa uniemożliwiająca usunięcie
innych plików niż cache. Uwaga, ważne jest, aby zmienna plik w funkcji zapytanie()
oraz tutaj miały identyczne rozszerzenia.
[php]
function wyczysc(){
include('config/cache.php');

if($this->zapytanie!='') {

$nazwa=md5($this->zapytanie);
$plik= $temp .'/'.$nazwa.'.cache';

if (file_exists($plik) ){
unlink($plik);
}

}

else{

$nazwa_folderu=$temp;
$dir = opendir($nazwa_folderu);
while($plik = readdir($dir)) { //pobieramy w pętli nazwy plików z folderu
$plik1=explode('.', $plik);
if ($plik1[1]=='cache'){
unlink($nazwa_folderu.'/'.$plik); //usuwamy go
}
}
closedir($dir);

page 10 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

}

} //reset

[/php]
Dla osób nieobytych jeszcze z klasami pokaże jak wykorzystać ją na stronach.
Przykład jedynie pokazuje jak wywołać funkcję i wyświetlić jej dane bez
przetworzenia. Myślę, że tyle wystarczy.

[sourcecode lang="php" collapse="true"]
include ('mods/cache/class.php'); //wstawiamy tylko raz na początku strony

$x =new cache; //stworzenie nowego obiektu cache

$x->zapytanie=&quot;SELECT * FROM `userzy`&quot;; // aby przypisać wartość
zmiennym w klasie wykorzystujemy znak ->
$x->typ='super-cache';
echo '<pre>';
print_r($x->zapytanie()); //funkcja zapytanie zwróci wynik, który w testach
można już pokazać bez dalszej obróbki.
echo '</pre>';
[/sourcecode]

To już tyle z mojej strony. Jak widać, również można stworzyć własną klasę, która
będzie o wiele mniejsza od oferowanych kombajnów typu smart czy
zendframework. Oczywiście pomagają przy tworzeniu stron, ale nie zawsze
potrzebujemy tyle funkcji na raz, więc po co ich tyle ładować, skoro można samemu
zrobić. Na koniec podaje cały kod klasy, nie zapomnij dać swoich linków do plików z
danymi do połączenia bazy danych oraz do opcji cachowania (uwaga dwa razy był
plik includowany).

[sourcecode lang="php" collapse="true"]

page 11 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

<?php

/// klasa cache
//autor Dawid Panfil
// więcej informacji o klasie na www.webmaster.taniewww-ino.pl

class cache{
var $zapytanie;
var $typ;

function polacz(){
require('config/baza.php');
$connection = @mysql_connect($serwer, $user , $haslo)
or die('Brak połączenia z serwerem MySQL.<br />Błąd: '.mysql_error());
$db = @mysql_select_db($baza, $connection)
or die('Nie mogę połączyć się z bazą danych<br />Błąd: '.mysql_error());

return $connection;

}

function rozlacz($connection){

@mysql_close($connection) or die('Nie można rozłączyć bazy '); ;
return 1;
}

private function cachuj($plik){
$connection=$this->polacz() ;

page 12 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

$wynik = mysql_query( $this->zapytanie)

or die('Błąd zapytania');

while($r = mysql_fetch_array($wynik)) {
$zawartosc[$i]=$r;
$i++;
}
ignore_user_abort(1); /*czytaj:
http://php.net/manual/pl/features.connection-handling.php */
$zawartosc1=serialize($zawartosc);

file_put_contents($plik, $zawartosc1);

$this->rozlacz($connection);
return $zawartosc ;

} //cachuj();

function zapytanie(){
include('config/cache.php');
if ($this->zapytanie!=''){
$nazwa=md5($this->zapytanie);
$plik= $temp .'/'.$nazwa.'.cache';

switch ($this->typ){
case 'super-cache':

if (file_exists($plik) ){

$utworzono=filemtime($plik);

page 13 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

$wygasnie=$utworzono+($czas*5);

if($wygasnie > time()){
$odczytaj=file_get_contents($plik);
return unserialize($odczytaj);
}
else return $this->cachuj($plik);
}
else return $this->cachuj($plik);
break;

case 'no-cache':
$connection=$this->polacz();

//zadanie pytania, przesłanie go do tablicy zawartość z serializowanie danych
//i zapis

$wynik = mysql_query( $this->zapytanie);

while($r = mysql_fetch_array($wynik)) {
$zawartosc[$i]=$r;
$i++;
}

$this->rozlacz($connection);
return $zawartosc;

break;

default:

page 14 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

if (file_exists($plik) ){

$utworzono=filemtime($plik);
$wygasnie=$utworzono+$czas;
if($wygasnie > time()){
$odczytaj=file_get_contents($plik);
return unserialize($odczytaj);
} //if
else return $this->cachuj();
} //if

else return $this->cachuj();

break;

} //switch

} ///if
else return 'brak zapytania';

}//zapytanie();

function wyczysc(){
include('config/cache.php');

if($this->zapytanie!='') {

$nazwa=md5($this->zapytanie);
$plik= $temp .'/'.$nazwa.'.cache';

page 15 / 16

background image

Zmagania Suchego | Cachowanie zapytań SQL

Copyright suchy dawidpanfil@gmail.com
http://free.xaa.pl/cachowanie-zapytan-sql

if (file_exists($plik) ){

unlink($plik);
}

}

else{

$nazwa_folderu=$temp;
$dir = opendir($nazwa_folderu);
while($plik = readdir($dir)) { //pobieramy w pętli nazwy plików z folderu
$plik1=explode('.', $plik);
if ($plik1[1]=='cache'){
unlink($nazwa_folderu.'/'.$plik); //usuwamy go
}
}
closedir($dir);
}

} //wyczysc
}//clasa

?>
[/sourcecode]

page 16 / 16


Wyszukiwarka

Podobne podstrony:
rozne, SBD zapytania sql PL, I
rozne, SBD zapytania sql PL, I
Zapytania SQL, powtórzenie materiału
Optymalizacja zapytań w SQL
Zapytania SQL PL, pjwstk PJLinka.pl, SBD
Zapytania SQL, Szkoła, MS SQL
zapytania SQL, PJWSTK, 0sem, SBD
Wykorzystanie błędów w zapytaniach sql, DZIAŁ IT, Doc HACK
Zapytania SQL, I
zapytania SQL
rozne, SBD zapytania sql rozwiazania PL, Zapytania SQL rozwiązane
Bazy Danych Jezyk Zapytan SQL Programowanie Proceduralne 2 id
Optymalizacja zapytań SQL
OKLADKA Optymalizacja zapytan SQL indd optymalizacja zapytan SQL
Zapytania w jezyku SQL ref 3
SQL Jak osiagnac mistrzostwo w konstruowaniu zapytan sqljak

więcej podobnych podstron