RkBlog

Hardware, programming and astronomy tutorials and reviews.

PHP i Memcached

Memcached to system keszowania oparty o pamięć RAM umożliwiający zapisywanie danych i obiektów. Stworzony został dla serwisu LiveJournal, lecz obecnie stosowany jest na wielu serwisach jak digg czy slashdot. Zaleta: wysoce wydajny i skalowalny, możliwość łączenia serwerów memcached działających na różnych maszynach.

Instalacja

Memcached dostępny jest w repozytoriach wielu dystrybucji Linuksa, a jeżeli go brak to kompilacja nie powinna przysporzyć problemów. Zależnością jest libevent. Obsługę memcached w PHP zapewnia binarne rozszerzenie, dostępne na pecl.php.net. Kompilacja standardowa:
phpize
./configure
make
make install
Następnie w php.ini dodajemy:
extension=memcached.so


Memcached w PHP

Oto krótki przegląd metod API (manual):
By móc korzystać z memcached musimy uruchomić lokalnie serwer memcaced. Najprostsze rozwiązanie to wykonanie w konsoli polecenia memcached w konsoli przez nieuprzywilejowanego użytkownika (nie-roota). Memcached uruchomi się z domyślnymi ustawieniami i będzie nasłuchiwał na porcie 11211.

Wykorzystanie

Oto prosty przykład wykorzystania API memcached:
<?php
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ('Nie mogę się połączyć');

$version = $memcache->getVersion();
echo '<b>Wersja Serwera</b>: '.$version.'<br/>';
Co wyświetli wersję serwera memcached.

Teraz już coś bardziej przydatnego - zapisywanie i pobieranie obiektów z serwera memcached:
<?php
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ('Nie mogę się połączyć');

IF ($get_result = $memcache->get('key'))
	{
	// obiekty są w keszu
	echo '<b>Dane z serwera</b>:<br/>';
	echo $get_result->str_attr.'<br />';
	echo $get_result->int_attr;
	}
else
	{
	// obiektów nie ma w keszu
	$tmp_object = new stdClass;
	$tmp_object->str_attr = 'test';
	$tmp_object->int_attr = time();
	
	$memcache->set('key', $tmp_object, false, 10) or die ('Nie udało się zapisać elementu');
	echo 'Zapisane dane zostaną usunięte po 10 sekundach<br/>';
	echo 'Odśwież stronę by zobaczyć dane zapisane na serwerze memcached';
	}
Za pomocą metody set zapisujemy na serwerze memcached prosty obiekt. Metoda set przyjmuje trzy argumenty:
bool Memcache::set ( string klucz, mixed wartość [, int flaga [, int czas_ważności]] )
Gdzie klucz - klucz pod jaką zapisana będzie wartość. flaga - określa Tak/Nie czy używać kompresji danych, a czas_ważności określa w sekundach czas przetrzymywania elementu na serwerze. Powyższy kod za pierwszym wykonaniem wyświetli tekst informujący o zapisaniu danych, lecz odświeżanie strony z kodem przed upływem 10 sekund będzie pokazywało zapisane dane. Po upływie 10 sekund element zostanie usunięty z serwerem a ponowne wykonanie skryptu zapisze nowe dane.

A teraz keszowanie wyników zapytań w Wordpressie. W pliku wp-includes/wp-db.php znajdujemy metodę:
<?php
function get_results($query = null, $output = OBJECT) {
		$this->func_call = "\$db->get_results("$query", $output)";

		if ( $query )
			$this->query($query);

		// Send back array of objects. Each row is an object
		if ( $output == OBJECT ) {
			return $this->last_result;
		} elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
			if ( $this->last_result ) {
				$i = 0;
				foreach( $this->last_result as $row ) {
					$new_array[$i] = (array) $row;
					if ( $output == ARRAY_N ) {
						$new_array[$i] = array_values($new_array[$i]);
					}
					$i++;
				}
				return $new_array;
			} else {
				return null;
			}
		}
	}
I zamieniamy na:
<?php
function get_results($query = null, $output = OBJECT) {
	global $memcache;
	$klucz = sha1($query);
		IF ($get_result = $memcache->get($klucz))
			{
			return $get_result;
			}
		else
			{
				$this->func_call = "\$db->get_results("$query", $output)";
		
				if ( $query )
					$this->query($query);
		
				// Send back array of objects. Each row is an object
				if ( $output == OBJECT ) {
					$memcache->set($klucz, $this->last_result, false, 300) or die ('Nie udało się zapisać elementu');
					return $this->last_result;
				} elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
					if ( $this->last_result ) {
						$i = 0;
						foreach( $this->last_result as $row ) {
							$new_array[$i] = (array) $row;
							if ( $output == ARRAY_N ) {
								$new_array[$i] = array_values($new_array[$i]);
							}
							$i++;
						}
						$memcache->set($klucz, $new_array, false, 300) or die ('Nie udało się zapisać elementu');
						return $new_array;
					} else {
						return null;
					}
				}
			}
	}
Do wp-config.php dodajemy:
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ('Nie mogę się połączyć');
Gotowe. Ustawiliśmy pięciominutowe keszowanie wyników zapytań :nice:

Co powinno być keszowane ?


Kiedy stosować keszowane ?


Keszowanie żadań HTTP, stron www

Niektóre serwery, jak np. Nginx pozwalają na keszowanie całych żądań HTTP. Temat zastosowania memcached będzie jeszcze kontynuowany w odpowiednich Bibliotekach (CMS, Linux)
RkBlog

PHP w Akcji, 14 July 2008, Piotr Maliński

Comment article