Thiago Berlitz Rondon

Memcached
Publicado em 01/01/2010

Memcached

Memcached é um projeto que tem como objetivo aumentar a velocidade de sites dinâmicos diminuindo a carga no banco de dados. É um sistema distribuído de alto desempenho para o cache de objetos na memória .

A utilização do cache é particularmente interessante em sites dinâmicos com banco de dados sobrecarregado, mas pode ser útil em qualquer cenário que exija muito processamento, como renderização de templates, feeds, manipulação de dados de sessão e webservices.

Arquitetura

Vamos começar discutindo sobre as motivações por trás da implementção deste poderoso sistema de cache.

Escalabilidade

Característica desejável em todo sistema, em uma rede ou em um processo, que indica sua habilidade de manipular uma porção crescente de trabalho de forma uniforme, ou estar preparado para crescer.

C10K

Memcached é implementado em um servidor baseado em eventos 'non-blocking' (async). Esta arquitetura tem como objetivo solucionar o problema 'C10K' (vide http://www.kegel.com/c10k.html), no qual descreve como lidar com 10 mil clientes simultaneamente.

Banco de dados

Estamos explicando sobre um servidor de cache, e não um banco de dados, por isto você não pode efetuar um 'dump', interagir entre as chaves, não há por que ele ser persistente ou redudante. Assim, como não há sessões e preocupações relacionadas a segurança, pois estes assuntos podem ser tratadas em outras camadas, como por exemplo um bom firewall.

Lembre-se: Não utilize o memcached como banco de dados, se quiser um banco de dados, procure um.

Cache

Um espaço temporário onde dados acessados frequentemente podem ser armazenados para serem acessados rapidamente, economizando recursos.

O cache é utilizado para sua aplicação ser escalavel, ou seja para manter resultados de querys, objetos que existe uma certa demora para ser calculado, thumbnails, ou seja qualquer coisa que demore para ser gerado.

Tabela hash

Memcached se utiliza de uma gigante tabela hash distribuida em múltiplos servidores. Uma visão resumida de uma tabela hash caso você não saiba, ela implementa uma "matriz (array) com elementos", no qual cada elemento contém uma lista de nós, com cada nó contendo chave e valor.

A maioria dos hashes começa pequeno e vão aumentando dinamicamente ao longo do tempo, dependendo da necessidade.

Mas o que isto tem haver com o memcached ? Memcached apresenta ao usuário uma inteface de dicionário (chave => valor), mas é implementado internamente como uma mistura de duas camadas.

A primeira camada é implementada na biblioteca do cliente, ele decide para qual servidor irá enviar a solicitação com um algoritimo baseado em caracteristicas das matrizes. A segunda camada é como nos visualizamos na programação.

Operações atomicas.

Para segurança do cenário, todas as operações no memcached são atomicas, ou seja toda ação deve ser executada completamente em caso de sucesso, ou ser abortada completamente em caso de erro. Um exemplo trivial que ilustra este conceito é a gravidez, não se diz que uma mulher esta 'meio gravida'; ou ela esta gravida ou não esta.

Alocador slab

Este é um algoritimo com a finalidade de evitar fragmentação de dados na memória por um aplicativo, este alocador foi implementado pela primeira vez por Jeff Bonwick para os sistemas operacionais da SunOS.

A ideia principal é que a quantidade de tempo necessária para inicializar um objeto comum excede a quantidade de tempo necessária para alocá-lo e deslocá-lo.

Então a melhor solução seja em vez de liberar a memória de volta a um conjunto global, a memória permaneceria inicializada para seu proposito desejado, velocidade !

slabs

Todo cache contém uma lista de slabs, que podemos dividir os blocos de memória em:

full

Estão totalmente alocados.

partial

Parcialmente alocados.

empty

Vázios, ou não possuiem nenhum objeto alocado.

Dependendo do número de memória disponível, são alocados na inicialização N-slabs.

O algoritimo slab é baseado em um conceito da matemática aritmética, a exponenciação (powers-of-N). Pela espeficação do protocolo memcached, os slabs tem o tamanho de até 1MB e são dividos em pedaços menores, e o primeiro pedaço é a menor e vai aumentando por um fatoral, por exemplo, de 1.20 até que ocupe todo o espaço dedicado ao slab.

exp

Veja, o - a - é o tamanho da estrutura mínima de um item no slab, e assim ela vai aumentando exponencialmente até que o total - n - seja de no máximo 1 MB.

Por isso se os limites são exagerados você terá uma perda de rendimento, este algoritimo é apenas eficiênte quando existem parâmetros bem definidos.

TTL

TTL e um acrônimo do inglês para "Time-to-Live", que neste caso significamente especificamente o tempo de vida da informação no memcached.

Porém, o algoritimo do memcached utiliza uma método de expiração preguiçosa, isto significa que ela não fica verificando os itens que estão expirados no cache. Quando um item é requisitado, ele verifica se o tempo expirou antes de retornar o valor para o cliente.

LRU (Least Recently Used)

É um método que faz parte da familia de algoritimos para cache, no qual em uma lista é descartado o item acessado a mais tempo. Para isto, é mantido a informação de quando o item foi utilizado.

Afinal, como o cache do memcached funciona ?

A estrutura para o fluxo de dados é um LRU (Least Recently Used), com a expiração de tempos. Quando você aloca algo nele, você deve falar por quanto tempo este objeto será valido. Caso, mesmo assim a memória se esgote então ele irá começar a substituir os slabs expirados, e então os slabs sem utilização com mais tempo serão os próximos a serem utilizados.

Limites

As chaves podem ter no máximo 250 caracteres e os valores 1 MB, pela razão de necessidade e velocidade no qual o algoritimo slab trabalha, ou seja como foi observado a memória é quebrada em vários slabs que podem ter tamanhos diferentes.

É interessante abstrair o motivo pelo qual o limite do valor é de 1 megabyte. Observe, se estamos conversando sobre uma página web e você esta tentando armazenar algo com grandes valores em um cache, algo provavelemnte esta errado.

A segunda é por conta do algoritimo slab, como já foi discutido.

Algoritimo para implementação.

Um breve algoritimo para implementação do memcached para ser usado como cache de informações que sao obtidas de um banco de dados.

	1. [Conectar ao servidor Memcached]
	2. [Buscar $chave] - Caso exista, ir para 6.
	3. [Conectar ao banco de dados]
	4. [Buscar informação]
	5. [Armazenar no memcached]
	6. [Retornar $valor].

Lembre-se: Você pode utilizar o memcached em outras situações, não apenas para banco de dados, como comentamos no começo do artigo.

Implementação Perl.

	use Cache::Memcached;

	# 1. [Conectar ao servidor Memcached]
	my $memd = new Cache::Memcached {
		'servers' => [ "10.1.1.1:11211" ],
		'debug' => 0,
	};
	# 2. [Buscar $chave] - Caso existe, ir para 6.
	if (my $valor = $memd->get("$chave")) {
		# 6. Retornando $valor
		return $valor;
	}

	# 3. [Conectar ao banco de dados]
	# -- (código para conectar ao banco de dados) --

	# 4. [Buscar informação]
	# -- (buscando inforamcao ($valor)) --

	# 5. [Armazenar no memcached]
	$memd->set("$chave", "$valor");

	# 6. [Retornando $valor].
	return $valor;

Veja como é simples, primeiramente você configura os servidores que irá utilizar para buscar e adicionar chaves.

Depois, você pode adicionar uma chave com o valor neles, assim como buscar. Caso, queira adicionar a informação do tempo de vida da chave no servidor, você pode passar um parâmetro adicional.

	$memd->set("chave", "valor", $tempo_para_expirar);

O $tempo_para_expirar é um argumento opcional, sendo o valor padrão dele "nunca expirar".

Existem outros métodos disponíveis para facilitar sua implementação, veja o manual do módulo para maiores informações.

Alternativas ao memcached.

Existem outras implementações para te auxiliar na implementação de um cache, veja algumas sugestões abaixo:

Cache::FastMmap
Cache::Bounded
Cache::FileCache

Facilitadores para o Catalyst

Com Catalyst::Plugin::Cache é possível implementar o seu sistema de cache com memcached ou suas alterntivas.

Esta é a configuração básica que o módulo exige para o memcached.

	use Catalyst qw/Cache/;

	__PACKAGE__->config->{'Plugin::Cache'}{backend} = {
		class => "Cache::Memcached",
		servers => [ '10.1.1.1:11211'],
	};

Um exemplo simples de implementação no Controller:

	sub equinocio : Local {
		my ($self, $c, $id) = @_;
		my $cache = $c->cache;
		my $result;
		unless ($result = $cache->get($id) ) {
			$result = 1;
			$c->cache->set($id, $result);
		}
	}

Agradecimentos

Ao canal #sao-paulo.pm na rede irc.perl.org por corrigir o artigo, ele foi escrito com pouco tempo. Em especial ao Breno Oliveira, Nuba Princigalli, Daniel de Oliveira Mantovani e ao Otávio Fernandes.

AUTHOR

Thiago Rondon , trabalha atualmente na Aware TI. http://www.aware.com.br/

blog comments powered by Disqus