Arquitetura real · Pleno
Twitter: Redis com 105 TB de RAM
Arquitetura real em produção. Pontos centrais: Fundamentos de Cache e Sharding de Banco de Dados.
Resposta curta
Arquitetura real em produção. Pontos centrais: Fundamentos de Cache e Sharding de Banco de Dados. Este caso parte de um resumo, publicado no blog High Scalability, de uma palestra sobre Redis no Twitter feita por Yao Yue, da equipe de cache da empresa.
01 · CONTEXTO
Um cache para o serviço mais importante
Este caso parte de um resumo, publicado no blog High Scalability, de uma palestra sobre Redis no Twitter feita por Yao Yue, da equipe de cache da empresa. O título do resumo anuncia os totais: 105 TB de RAM, 39 milhões de consultas por segundo e mais de 10.000 instâncias.
No Twitter, o Redis sustentava o Timeline, descrito como o serviço mais importante da empresa. Uma timeline é um índice de tweets por identificador; encadeando tweets em uma lista obtém-se a Home Timeline, e a User Timeline, com os tweets que a própria pessoa publicou, é só outra lista. O Redis foi usado pela primeira vez no Twitter em 2010, para o serviço de Timeline, e também é usado no serviço de anúncios.
Quando um tweet é gerado, ele precisa ser escrito em todas as timelines relevantes; cada escrita é um item pequeno acrescentado a uma lista grande no cache.
Na leitura, o aplicativo pede só um lote pequeno de tweets; ao rolar a tela, outro lote é carregado.
Como a timeline pode ser grande e o desempenho importa, a leitura deve evitar o banco e ser atendida pelo cache.
O cache é look-aside: se a chave falta, quem pediu busca no armazenamento e a coloca de volta no cache; o cache não se repõe sozinho.
02 · POR QUE REDIS
Por que Redis e não Memcache
Segundo o resumo, a pergunta era por que considerar o Redis em vez do Memcache, e duas razões respondem: o problema da largura de banda de rede e o problema do prefixo comum longo.
O Memcache funcionou pior que o Redis para a timeline, e o problema era a distribuição em leque (fanout). As leituras e escritas do Twitter acontecem de forma incremental e são pequenas, mas as timelines em si são grandes. Um ciclo de ler, modificar e escrever, para escritas incrementais e leituras pequenas sobre objetos grandes como a timeline, é caro demais e cria um gargalo de rede. Segundo a palestrante, num gigalink com mais de 100 mil leituras e escritas por segundo, a rede vira o gargalo quando o objeto médio passa de 1K.
O segundo problema são os prefixos longos e repetidos. Com um esquema flexível, cada atributo pode virar uma chave própria, o que exige uma requisição por atributo, e nem todos os atributos podem estar no cache. Com métricas ao longo do tempo, o mesmo nome se repete a cada amostra, que muda só o carimbo de tempo. Para economizar espaço nos dois casos, o desejável é um espaço de chaves hierárquico.
Há ainda um argumento de recursos: um cluster dedicado de cache subutiliza as CPUs, e o Redis enxerga o que o servidor poderia fazer e não faz.
03 · ESTRUTURAS SOB MEDIDA
Hybrid List e BTree
O Twitter bifurcou o Redis 2.4 e adicionou recursos, e por isso ficou preso nessa versão. As mudanças incluíram dois recursos de estrutura de dados, gerenciamento de cluster próprio e logging próprio. Os recursos de disco do Redis não são usados.
A timeline é uma lista de IDs de tweets, ou seja, inteiros pequenos. O Redis oferece dois tipos de lista: a ziplist, eficiente em espaço, e a lista ligada, flexível, mas com dois ponteiros por item, uma sobrecarga muito alta para IDs tão pequenos. Para usar a memória bem, o Twitter usava exclusivamente ziplists.
Usar só ziplists tem custos. O limite da ziplist passa a ser o tamanho máximo de uma timeline, de modo que uma decisão de produto, quantos tweets cabem numa timeline, fica ligada a um componente de baixo nível. Inserir e remover numa ziplist muito grande é ineficiente. Além disso, as timelines variam muito de tamanho e, quando falta memória no cache, muitas timelines pequenas podem ser expulsas até sobrar memória contígua para uma ziplist grande. Como as escritas se espalham por muitas timelines, aumenta a chance de cair numa armadilha de latência de escrita, e fica difícil definir um SLA para as escritas.
A Hybrid List é uma lista ligada de ziplists, adicionada ao Redis para um desempenho de memória mais previsível. Cada ziplist tem um limite de tamanho em bytes e, quando a lista passa dele, o excedente vai para a ziplist seguinte. Antes dela, uma solução de contorno era expirar mais cedo as timelines grandes, o que liberava memória para as outras, mas era caro quando a pessoa ia ver a própria timeline.
A BTree foi adicionada para consultas por intervalo em chaves hierárquicas, devolvendo uma lista de resultados. No Redis, campos secundários ficam num hash, que usa busca linear e rende mal com muitos campos; já o conjunto ordenado (sorted set) ordena por uma pontuação do tipo double, então um nome ou chave secundária arbitrária não serve de critério. A BTree reaproveita uma implementação BSD e permite busca por chave e por intervalo.
| Vantagem | Custo | |
|---|---|---|
| Ziplist | Eficiente em espaço | Inserir e remover é ineficiente, sobretudo em listas muito grandes |
| Lista ligada | Flexível | Dois ponteiros por item, sobrecarga muito alta para IDs pequenos |
| BTree | Busca por chave e por intervalo | Pouco eficiente em memória, com muitos metadados por causa dos ponteiros |
O resumo dá os números de um datacenter para cada uma das duas estruturas.
| Timeline (Hybrid List) | BTree | |
|---|---|---|
| Heap alocada | cerca de 40 TB | cerca de 65 TB |
| Instâncias | mais de 6.000 | mais de 4.000 |
04 · CLUSTER E PROXIES
Quem roteia as chaves
Um cluster usa mais de uma instância Redis para um mesmo fim. Quando os dados ou a vazão passam do que uma instância aguenta, o espaço de chaves precisa ser particionado em shards, e o roteamento é pegar uma chave e descobrir em que shard estão os dados.
Na opinião da palestrante, o gerenciamento de cluster é a principal razão de a adoção do Redis não ter explodido. Parte do problema é que muitas operações do Redis não são idempotentes: após uma falha de rede é preciso repetir a chamada, e os dados podem se corromper, e dados corrompidos são difíceis de detectar. No Memcache, muitos clusters usam hash consistente no cliente e aceitam dados inconsistentes.
O Twitter via três opções: os servidores combinarem entre si como é o cluster, usar um proxy, ou deixar o cliente gerenciar o cluster, com os clientes formando um quórum.
| O que é | Por que foi ou não escolhido | |
|---|---|---|
| Servidores combinam entre si | Os servidores chegam a um acordo sobre a forma do cluster | Descartado: a filosofia é manter os servidores simples, burros e rápidos |
| Cliente gerencia o cluster | Os clientes formam um quórum | Descartado: mudanças são difíceis de propagar |
| Proxy | Uma camada de proxies roteia as chaves | Escolhido, por duas razões |
O peso do cliente aparece nos números: cerca de 100 projetos no Twitter usam um cluster de cache, e mudar qualquer coisa no cliente exigiria propagar a alteração para todos, o que poderia levar anos.
Das duas razões, uma é separar o caminho rápido, o de dados, do caminho lento, o de comando e controle. A outra é que, com a gestão do cluster dentro do servidor, qualquer correção ou atualização desse código exigiria reiniciar o Redis, que tem estado, e isso pode descartar muitos dados.
O serviço cliente envia o comando a um proxy, e não se importa com qual deles atende.
O proxy faz o roteamento: pega a chave, descobre o shard que guarda o dado e encaminha o comando.
Não há sharding no nível do proxy e todos são sem estado; para escalar a vazão, basta adicionar proxies.
A gestão do cluster fica fora do caminho de dados: o líder cuida da visão do cluster e, se um nó cai, move o shard para outro nó.
Se a gestão do cluster ficasse dentro do servidor, corrigir ou atualizar esse código exigiria reiniciar o Redis, que tem estado e poderia perder dados.
Se a chave falta no cache, o próprio cliente busca no armazenamento e a recoloca no cache.
Havia o receio de que o proxy inserisse mais um salto de rede entre cliente e servidor, mas o profiling mostrou que esse salto extra é um mito, ao menos no ambiente deles. A latência pelo servidor Redis ficou abaixo de 0,5 milissegundo, enquanto o caminho que passa pelo Finagle ficou perto de 10 milissegundos. O problema, concluem, está dentro da JVM.
O proxy tem um custo: recursos alocados só para encaminhar. Também não há razão para fechar conexões, e há instâncias com 100 mil conexões abertas funcionando bem, com a latência melhor se elas ficam abertas. O resumo diz ainda que a camada de proxies está voltando para C e C++.
Os clusters funcionam como cache look-aside: o cache não repõe os dados, e o cliente é quem busca a chave ausente no armazenamento e a coloca no cache. Se um nó cai, o shard é movido para outro nó, e a máquina que falhou é limpa ao voltar, para não restar dado antigo. O líder do cluster faz tudo isso, e uma visão central ajuda a manter o cluster fácil de entender.
05 · VISIBILIDADE
Provar o que o cache está fazendo
Quando alguém reclama que o cache se comporta mal, na maioria das vezes o cache está bem: os clientes estão mal configurados, pedem chaves demais ou pedem a mesma chave sem parar, saturando o servidor ou o link. Quem é acusado de abusar do sistema quer provas, e uma arquitetura orientada a serviços não dá, por si só, isolamento de problemas nem depuração mais fácil.
Por isso o Twitter construiu visibilidade no próprio cache. Registrar todos os comandos é possível, e só metadados são guardados, não os valores. Mandar todo esse log para fora da máquina é muito dado e seria economicamente inviável, então o log é resumido na própria máquina e só o resumo viaja.
A instância de cache registra metadados de cada comando, mas não os valores.
Um processo na própria máquina lê o log e gera um resumo, enviado periodicamente e muito menor que o log original.
O Storm agrega os resumos das máquinas.
As visões são guardadas e exibidas, mostrando por exemplo as chaves mais usadas e o tráfego por segundo.
Com isso dá para ver as chaves mais usadas, o tráfego por segundo, que mostra se o padrão é em picos, e o número de chaves únicas, útil para planejar capacidade. Perda frequente de pacotes pode ser ligada a uma chave quente ou a tráfego em picos. Chaves quentes são um problema, e o Twitter estava construindo uma solução de cache em camadas, com cache no cliente que guarda automaticamente essas chaves.
06 · LIÇÕES
O que a palestrante leva dali
Escala exige previsibilidade: quanto maior o cluster e o número de clientes, mais previsível e determinístico se quer o serviço. As latências de cauda importam: numa consulta que se espalha por muitos shards, basta um shard lento para a consulta inteira ficar lenta.
Num ambiente de contêineres, o Redis causa fragmentação externa, isto é, usa mais memória para guardar a mesma quantidade de dados, e compensar isso com superalocação gera grande potencial de desperdício. Segundo o resumo, serviços de latência muito baixa não combinam bem com o Mesos e por isso ficam isolados dos demais trabalhos.
Outra lição é levar a computação até o dado, como resumir logs na própria máquina. Em relação a scripts Lua dentro do Redis, a palestrante os considera inadequados para produção: um script carregado pode fazer qualquer coisa e, assim, quem opera o serviço não consegue garantir o SLA. Ela prefere um modelo de implantação, que permitiria revisão de código e benchmark.
Na lista de desejos para o Redis aparecem gerenciamento explícito de memória, scripts implantáveis e multithreading, que facilitaria o gerenciamento do cluster ao reduzir o número de instâncias por máquina.
Perguntas frequentes
O que é Twitter: Redis com 105 TB de RAM?
Arquitetura real em produção. Pontos centrais: Fundamentos de Cache e Sharding de Banco de Dados.
O que o design de Twitter: Redis com 105 TB de RAM cobre?
Ele está organizado nestas seções: Um cache para o serviço mais importante, Por que Redis e não Memcache, Hybrid List e BTree, Quem roteia as chaves, Provar o que o cache está fazendo e O que a palestrante leva dali.
Quanto tempo leva Twitter: Redis com 105 TB de RAM e qual é o nível?
Leva cerca de 15 minutos para ler e tem nível Pleno.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.