1. 40 http://www.linuxmagazine.com.br
CAPA
Acelere seu servidor web com cache distribuído pelo memcached
Cache mais rápido
O prático Memcached pode reduzir em até 90% a
carga de um servidor de banco de dados web.
por Tim Schürmann
B
rad Fitzpatrick estava frus-
trado: a plataforma de blog
LiveJournal.com, fundada
e mantida principalmente por ele,
tinha como base mais de 70 máqui-
nas poderosas, mas seu desempenho
deixava muito a desejar. Nem mes-
mo o cache de 8 GB no servidor de
banco de dados parecia ajudar. Algo
precisava ser feito, e rapidamente. As
soluções típicas num cenário como
este são gerar conteúdo previamente
ou manter no cache páginas que já
tenham sido servidas. Logicamen-
te, essas soluções exigem armaze-
nagem redundante dos elementos
que ocorrem em múltiplas páginas
– o melhor caminho para encher o
cache de entulho. Se o sistema ficar
sem memória RAM, tudo pode ser
transferido para a swap em disco,
naturalmente, mas isso seria bem
demorado.
Na opinião de Fitzpatrick, a so-
lução precisava ser um novo tipo de
sistema de cache – algo que armaze-
nasse separadamente os objetos de
uma página, evitando assim o len-
to acesso ao disco. Assim, ele logo
desistiu de procurar uma solução e
decidiu projetar seu próprio cache.
Os servidores que ele usaria para
essa tarefa tinham memória RAM
suficiente livre. Inclusive, todas as
máquinas precisariam acessar o ca-
che simultaneamente e o conteúdo
modificado deveria estar disponível
para qualquer usuário, sem atrasos.
Estas considerações finalmente o le-
varam ao memcached, que reduziu a
carga do servidor de banco de dados
do LiveJournal em surpreendentes
90% e acelerou a entrega das páginas
aos usuários, melhorando o uso dos
recursos das máquinas individuais.
O memcached [1] é um sistema
de cache distribuído de alta perfor-
mance. Foi projetado para ser útil a
qualquer aplicação, mas é mais usa-
do como cache dos lentos acessos a
bancos de dados em aplicações web
dinâmicas.
Hoje, muitos sites importantes,
como Slashdot, Fotolog.com e o
próprio LiveJournal.com, contam
com o memcached para melhorar
seu desempenho. Desde o princípio
de seu desenvolvimento, o Live-
Journal.com foi comprado e vendi-
do diversas vezes, e o memcached,
disponível sob uma licença BSD de
código aberto, é responsabilidade da
Danga Interactive.
Roupa nova
Configurar um cache distribuído com
o memcached é fácil. Basta iniciar o
daemon em todos os servidores que
possuam algum espaço em RAM para
ser compartilhado. Se necessário, é
possível disponibilizar múltiplas áreas
de cache em uma única máquina.
Esta opção é particularmente útil em
sistemas operacionais que limitam o
acesso dos processos a uma parcela
2. 41
| CAPAMemcached
Linux Magazine #60 | Novembro de 2009
da memória total disponí-
vel. Nesses casos, é preci-
so iniciar vários daemons,
cada um obtendo toda a
memória disponibilizada
pelo sistema operacional,
para assim usar o máximo
de memória para o cache.
Uma biblioteca cliente
especial age como interface
com o servidor. Ela aceita
os dados e os armazena em
um dos servidores existen-
tes usando palavras-chave
selecionáveis (figura 1). A
biblioteca cliente aplica um
sofisticado método matemá-
tico para escolher qual dos
daemons do memcached receberá
os dados que serão, então, postos
em sua RAM.
Podemos comparar esse proce-
dimento com a chapelaria de um
teatro: você entrega seu casaco ao
atendente que está atrás do balcão
e recebe um número. O funcionário
pega seu casaco, acha o lugar certo
e o pendura no cabide com o seu
número. No fim do espetáculo, todo
o processo é repetido de trás para
frente: você informa ao atendente
– ou melhor, à biblioteca cliente –
o seu número, a biblioteca vai até
o daemon correspondente, pega os
dados no cabide e os entrega ao seu
aplicativo.
Esse modelo lembra muito os ban-
cos de dados e sistemas de arquivos
distribuídos. Mas, quando se traba-
lha com o memcached, é necessário
lembrar que ele é apenas um cache.
Em outras palavras, o atendente da
chapelaria não é confiável e tem di-
ficuldade de se lembrar das coisas.
Se não houver espaço suficiente para
novos elementos, um dos daemons
descartará os dados menos acessa-
dos para liberar espaço. O mesmo
acontece quando um dos daemons
falha – neste caso, qualquer informa-
ção armazenada por ele desaparece.
Em outras palavras, você não teria
seu casaco de volta no fim do espe-
táculo e o aplicativo seria forçado a
conversar novamente com o banco
de dados. O sistema do memcached
não é redundante, mas não há neces-
sidade de fazer isso, pois, no fim das
contas, ele se resume a um cache e
sua missão é armazenar informações
temporariamente e entregá-las o mais
rapidamente possível. Seguindo esta
filosofia, é impossível iterar por todos
os elementos do cache ou despejar
todo o seu conteúdo no disco.
Na escuta
A Danga Interactive disponibiliza
o memcached daemon em seu site
para download [1]. As únicas depen-
dências do programa são a biblioteca
Libevent e seu pacote de desenvolvi-
mento correspondente. O daemon
pode ser facilmente compilado e
instalado com as três etapas padrão:
$ ./configure
$ make
# sudo make install
Algumas das principais distribui-
ções já oferecem pacotes pré-compi-
lados, mas geralmente tratam-se de
versões já obsoletas. Após terminar
a instalação, o seguinte comando –
ou um similar – inicia o daemon:
# memcached -d -m 2048
-l 192.168.1.111 -p 11211
-u USERNAME
Este comando inicia o memcached
em modo daemon (-d), instruindo-
o a ceder 2048 MB de RAM desta
máquina para o cache distribuído
(-m 2048). O daemon escuta as so-
licitações do cliente na porta 11211
no endereço IP 192.168.1.111. Além
disso, ele precisa saber qual conta
usar, mas é possível omitir a opção
-u para rodá-lo sob a conta do usuá-
rio logado.
Os especialistas em segurança
devem estar furiosos: por definição,
qualquer usuário de um sistema Li-
nux pode rodar seu próprio daemon
do memcached. Para evitar isso, são
necessários alguns passos, como re-
tirar privilégios de acesso – apenas
uma das várias questões de seguran-
ça evitadas pelo memcached (mais
tarde falarei sobre isso).
Escolha seus parceiros
Após alinhar todos os daemons, esco-
lha uma das várias bibliotecas clientes
que agora estão disponíveis para vá-
rias linguagens de programação. Em
alguns casos, é até possível escolher
os pacotes [2]. Se você preferir criar
seu próprio cliente, encontrará uma
Figura 1 A biblioteca cliente aceita os dados do aplicativo e seleciona um daemon, que
se encarregará de armazenar os dados.
Aplicação Biblioteca cliente
Daemon 1
Daemon 2
Daemon 3
Daemon n
3. 42 http://www.linuxmagazine.com.br
CAPA | Memcached
descrição detalhada do protocolo na
wiki do memcached no site do pro-
jeto no Google Code [3].
O memcached é usado para acele-
rar aplicativos web e, por isso, muitas
pessoas optam por um cliente em
PHP. Para mais informações sobre o
uso do memcached com C ou C++,
veja o quadro 1.
A técnica básica é a mesma para
qualquer linguagem: após localizar e
instalar a biblioteca cliente correta,
o desenvolvedor precisa incluí-la no
seu próprio programa. A linha se-
guinte cria um novo objeto Memcached
no PHP com o cliente memcached do
repositório PECL, incluído no pa-
cote PHP5–memcached do Ubuntu:
$memcached = new Memcached;
Depois disso, uma chamada de
função informa à biblioteca em quais
servidores os daemons do memca-
ched estão escutando:
$memcache-connect (‘192.168.2.1’,
11211) or die (
‘Sem conexao com o servidor’);
Daqui para frente, é possível usar
maischamadasdefunçõesparapreen-
cherocachecomseupróprioconteúdo:
$memcache-set(
‘key’, ‘test’, false, 10);
Esta função grava a string test no
cache, com key como chave, man-
tendo a entrada por dez segundos. O
tamanho das chaves é restrito a 250
caracteres – restrição imposta pelo
daemon do memcached.
Para recuperar os dados, é preciso
passar a chave para a biblioteca clien-
te e aceitar os resultados recebidos:
$result = memcache-get(‘key’);
A listagem 1 mostra o script PHP
completo.
A opção de escrever múltiplos con-
juntos de dados no cache enquanto
eles são recuperados é interessante.
A biblioteca cliente paraleliza auto-
maticamente sua requisição aos ser-
vidores do memcached. Infelizmen-
te, algumas bibliotecas clientes não
possuem essa função; este exemplo
em PHP só é suportado pelo cliente
memcached (com um “d” no fim):
$multiplo = array(
‘chave1’ = ‘valor1’,
‘chave2’ = ‘valor2’,
‘chave3’ = ‘valor3’
);
$memcache-setMulti($multiplo);
Profiling
Nas aplicações web, sempre há a
questão de saber a melhor forma de
empregar o memcached. O profiling
responde: buscas em bancos de dados
que sobrecarregam o sistema são me-
lhor roteados via cache. As listagens
3 e 4 mostram como isso funciona na
vida real: antes de buscar no banco
de dados, o código confere se a in-
formação desejada está disponível no
memcached. Caso não esteja lá, o
banco de dados é acessado.
Para evitar uma nova busca, os
resultados são armazenados no ca-
che. Para mantê-lo atualizado, a
informação de cada operação de
escrita também vai para o cache. Na
listagem 4, as chaves são feitas ao se
combinar a palavra user com o ID
de sua conta – esta é uma estratégia
comum para gerar chaves únicas.
Essa técnica facilita a integração
do memcached aos seus aplicativos,
mas é necessário tomar cuidado com
as armadilhas, que só se tornam ób-
vias quando olhamos “sob o capô”.
Dicionário
Os programadores experientes já de-
vem ter notado que o memcached
usa internamente um dicionário; al-
gumas linguagens de programação
Quadro 1: Libmembached
Até agora, a biblioteca cliente mais popular do memcached para aplicativos
em C e C++ é a Libmemcached [4] – que não deve ser confundida com sua
antecessora já descontinuada Libmemcache (sem o “d” no final). Mesmo que
você não seja um programador C e C++, vale a pena dar uma olhada no pa-
cote. Ele contém ferramentas de diagnóstico muito interessantes para linha
de comando. Por exemplo, o memcat recupera os dados do cache para uma
chave e devolve o resultado no console; o memsat consulta o status atual de
um ou vários servidores. Para montar a Libmemcached, são necessários os
compiladores C e C++ no sistema; fora isso, bastam os comandos normais:
./configure; make; make install.
A listagem 2 mostra uma busca básica no cache.
Listagem 1: Busca básica no cache em PHP
01 ?php
02 $memcache = new Memcache;
03 $memcache‑connect(‘localhost’, 11211) or die (‘No connection to
memcached server’);
04
05 $memcache‑set(‘key’, ‘datum’, false, 10);
06
07 $result = $memcache‑get(‘key’);
08
09 var_dump($result);
10 ?
4. 43
| CAPAMemcached
Linux Magazine #60 | Novembro de 2009
chamam isso de array associativo,
vetor associativo ou hash. Como em
um dicionário comum, essa estru-
tura de dados armazena cada valor
sob uma chave (palavra) específica.
O sistema memcached implementa
este dicionário na forma de duas ta-
belas hash subsequentes [5]. Primei-
ramente, a biblioteca cliente aceita a
chave e efetua uma sofisticada função
matemática para criar um hash. O
número informa à biblioteca com
qual dos daemons do memcached
ela precisa conversar. Após receber
os dados, o daemon usa sua própria
função hash para atribuir um local
da memória a fim de armazenar os
dados. As funções matemáticas são
desenvolvidas para retornar sempre
o mesmo número exato para uma
chave específica. Este processo ga-
rante tempos de busca e resposta ex-
tremamente curtos. Para recuperar
informações do cache, o memcached
precisa apenas efetuar as duas fun-
ções matemáticas. A transmissão de
dados pela rede é responsável pela
maior parte do tempo de resposta.
Todas as máquinas envolvidas
precisam ter as mesmas versões das
mesmas bibliotecas, pois é a biblioteca
cliente quem decide quais daemons
armazenarão quais dados. Uma mis-
tura de versões pode fazer com que os
clientes utilizem funções hash dife-
rentes, armazenando assim a mesma
informação em diferentes servidores,
o que pode gerar inconsistências e
desorganização dos dados. Se você
usar a biblioteca Libmemcached de
C e C++, é bom prestar bastante
atenção a isso, pois ela oferece várias
funções hash.
Além disso, cada cliente usa um
método de serialização diferente. Por
exemplo, o Java usa o Hibernate, en-
quanto que o PHP usa oserialize. Em
outras palavras, se, além de strings,
você também estiver armazenando
objetos no cache, o uso compartilha-
do baseado em diferentes linguagens
é impossível – mesmo que todos os
clientes usem a mesma função de
hash. As bibliotecas também têm
permissão de escolher seus métodos
de compressão.
Perda de memória
O cache lida com solicitações pa-
ralelas sem perder velocidade. No
exemplo da chapelaria, vários aten-
dentes podem andar pelos corredo-
res ao mesmo tempo, pendurando
casacos ou devolvendo-os aos donos,
sem que estes tenham que esperar na
fila. O mesmo princípio se aplica ao
memcached. Cada cliente determi-
na com qual daemon conversará e,
num mundo ideal, cada atendente
estaria encarregado de um único
corredor: logicamente, nada impe-
de que dois atendentes entrem no
mesmo corredor. Se você recupera
dados do cache, os altera e os devol-
ve, não há garantias de que eles não
tenham sido modificados por outra
Listagem 3: Busca no banco de dados sem memcached...
01 function get_user($userid) {
02 $result = mysql_query (“SELECT * FROM users WHERE userid =
‘%s’”, $userid);
03 return $result;
04 }
Listagem 2: Busca básica no cache
01 #include memcached.h
02 #include string.h
03 #include stdio.h
04
05 main() {
06 /* cria a struct memcached_st (que contém todas as informações
07 básicas para os servidores memcached) */
08 memcached_st *mcd = memcached_create(NULL);
09
10 /* adiciona um servidor: */
11 memcached_server_add(mcd, “127.0.0.1”, 11211);
12
13 /* Envia o objeto para o cache: */
14
15 char *key = “chave”;
16 size_t keylength = strlen(key);
17 char *value = “informacao”;
18 size_t valuelength = strlen(value);
19 time_t expiration = 0;
20 uint32_t flags = 0;
21
22 memcached_add(mcd, key, keylength, value, valuelength,
23 expiration,flags);
24
25 /* Obtem objeto do cache: */
26
27 memcached_return errovariable;
28
29 char *result = memcached_get(mcd, key, keylength,valuelength,
30 flags, errovariable);
31
32 /* Imprime o objeto: */
33 printf(“Cache: %sn”, result);
34
35 /* Limpa: */
36 memcached_free(mcd);
37 }
5. 44 http://www.linuxmagazine.com.br
CAPA | Memcached
instância neste meio tempo. Os co-
mandos gets e cas introduzidos na
versão 1.2.5 do memcached oferecem
a solução: os usuários utilizam o co-
mando gets para recuperar dados e
recebem um identificador único, o
qual pode ser devolvido ao servidor
juntamente com os dados modifica-
dos pelo comando cas. O daemon
confere então o ID para verificar se
os dados foram alterados desde a úl-
tima busca e sobrescreve-os usando
o novo valor, se for o caso.
O modo como o memcached
lida com falhas do servidor também
depende do cliente. O padrão é sim-
plesmente agir como se a informação
solicitadanãoseencontrassenocache.
Por isso, é uma boa ideia monitorar
permanentemente os servidores de
cache. Graças ao design modular
do memcached, cada daemon pode
ser facilmente substituído. Para isso,
basta apagar o registro anterior dos
IPs e registrar os novos endereços
IP dos clientes. Mas note que, neste
caso, algumas bibliotecas acabarão
considerando todo o cache inválido.
Queijo suíço
ParaprevenirafragmentaçãodaRAM,
o daemon usa um alocador slab [6]
paragerenciaramemória.Essemétodo
reserva e libera pequenos pedaços da
memória. No caso do memcached,
essespedaçossãode1MB–odaemon
não aceita nada maior que isso. Para
armazenarmais,éprecisodistribuiros
dadosemmúltiplaschavesouusarum
sistema de caching diferente.
Anarquia
O memcached não se preocupa com
segurança. O daemon não necessita
de autenticação dos clientes. Qual-
quer um que acesse a rede também
pode acessar o cache sem reservas.
Um invasor que saiba os nomes dos
usuários por trás das chaves pode sis-
tematicamente solicitar esses nomes
aos daemons. Chaves criptografadas
podem garantir uma proteção rudi-
mentar. Para gerá-las, é necessário
aplicar um hash aos nomes dos
usuários no escopo do aplicativo e
então usar os resultados como cha-
ves. Todos os dados da conta têm
que ser deletados do cache após o
uso. Além disso, é bom definir uma
duração para os dados e incluir mais
camadas de segurança, começando
por um firewall para proteger o ser-
vidor de ataques externos.
Conclusões
O memcached é fácil de configurar
e de integrar a aplicativos existentes.
Porém, essa conveniência implica
diversos problemas de vulnerabilida-
de. Se você conseguir resolver essas
questões, terá um cache distribuído
extremamente rápido que não o dei-
xará na mão – mesmo em condições
extremas. Esse sistema prova seu
valor diariamente no LiveJournal e
no Slashdot. Ao mesmo tempo, é
extremamente frugal. A capacidade
da CPU não é comprometida, pois o
memcachedpraticamentegeraapenas
hashes. Como resultado, é possível
usar até computadores mais velhos
como provedores de cache. n
Listagem 4: ...e após a introdução do memcached
01 $memcache = new Memcache;
02 $memcache‑connect(‘servername’, 11211) or die (‘No connection to
memcached server’);
03 ...
04 function get_user($userid) {
05 $result = memcache‑get(“user” + $userid);
06 if(!$result) {
07 $result = mysql_query(“SELECT * FROM users WHERE userid =
‘%s’”, $userid);
08 memcache‑add(“user” + $userid, $result);
09 }
10 return $result;
11 }
Gostou do artigo?
Queremos ouvir sua opinião. Fale conosco em
cartas@linuxmagazine.com.br
Este artigo no nosso site:
http://lnm.com.br/article/3114
Mais informações
[1] Memcached: http://www.danga.com/memcached
[2] Bibliotecas clientes:
http://code.google.com/p/memcached/wiki/Clients
[3] Protocolo do memcached:
http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
[4] Libmemcached: http://tangent.org/552/libmemcached.html
[5] Funcionamento de tabelas hash:
http://pt.wikipedia.org/wiki/Tabela_de_dispers%C3%A3o
[6] Funcionamento de alocadores slab (em inglês):
http://en.wikipedia.org/wiki/Slab_allocator