Monday, June 20, 2011

Configurando mysql no home

Utilizando um cluster comunitário, muitas vezes, não queremos configurar um serviço centralizado em que todos os usuários da máquina possam ter acesso. Na maioria das vezes, é possível personalizar configurações para cada usuário, mas, no meu caso, prefiro instalar meus programas localmente no meu home. Só assim fico sossegada de que tudo estará nos "meus conformes".

Como instalar o mysql é uma tarefa um pouco recorrente no meu trabalho, vou postar aqui um how-to express para que eu nunca mais me esqueça de como fazê-lo. Nada que não esteja no arquivo INSTALL do mysql. Na minha instalação eu utilizei o mysql 5.5 em uma máquina com SO Ubuntu 2.6.32.27, 64 bits. Vamos aos passos.

Primeiro, copie o binário para o seu computador. No meu caso:

wget http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.12-linux2.6-x86_64.tar.gz/from/http://mirrors.ircam.fr/pub/mysql/

Descompacte:

tar -zxvf mysql-5.5.12-linux2.6-x86_64.tar.gz

Faça um link simbólico para o diretório criado:

ln -s mysql-5.5.12-linux2.6-x86_64 mysql

Entre no diretório mysql:

cd mysql

Altere as variáveis MYSQL e MYSQLDUMP do arquivo bin/mysqlaccess fazendo-as apontar para o seu diretório local.

Crie as tabelas de permissão do mysql:

scripts/mysql_install_db

Inicie o servidor de banco de dados:

bin/mysqld_safe &

Mysqld_safe é a maneira mais segura de se iniciar um servidor mysql. Ele faz, por exemplo, o servidor reiniciar quando um erro acontece e automaticamente registra a informação em tempo de execução a um arquivo de log.

Em algumas máquinas precisei instalar o pacote libaio1. O fiz de duas maneiras. Primeiro utilizando o apt-get, quando as máquinas não apresentavam conflitos. E depois utilizando o dpkg do debian em máquinas com conflito de pacotes (não quis forçar a instalação, pois isso poderia quebrar o ambiente de algum usuário do cluster).

Friday, June 3, 2011

Como ativar o log do HAProxy

O HAProxy é uma aplicação que oferece funcionalidades para se atingir alta disponibilidade e balanceamento de carga em sistemas baseados em TCP e HTTP. O HAProxy funciona como um proxy interceptando requisições entre a aplicação cliente e a aplicação servidora. No meu caso, estou utilizando o HAProxy para distribuir as requisições feitas a um cluster Cassandra.

Por questões de desempenho, o HAProxy loga utilizando um servidor syslog e não um arquivo comum. O Syslog consiste em um daemon presente em muitos servidores Linux. No entanto, o HAProxy exige que esse daemon escute na porta 514 a qual, frequentemente, não está habilitada por padrão.

Um servidor syslog:
- recebe entradas a serem logadas
- decide o que é importante de ser registrado
- escreve em disco de uma maneira otimizada

Para ativar o log do HAProxy, primeiro é preciso especificar o uso do log no arquivo haproxy.cfg. No meu caso, eu tenho algo do tipo:

global
        maxconn           5000
        log                     localhost               local0              notice

Nas linhas anteriores, instruo ao HAProxy que ele registre eventos na máquina local (localhost), utilizando o rsyslog através de local0. O parâmetro notice informa que somente eventos considerados importantes serão logados. Para mais informações sobre esses parâmetros, consulte a documentação anexa ao código fonte do HAProxy (arquivo doc/configuration.txt).

Em seguida, crie um arquivo haproxy.cnf em /etc/rsyslog.d/. Ponha o seguinte conteúdo nesse arquivo:

$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
local0.* -/var/log/haproxy.log
Agora reinicie o servidor rsyslog.

sudo /etc/init.d/rsyslog restart

Para ver o que está sendo logado sobre o HAProxy, acesse:

tail -f /var/log/haproxy*.log
 
Um ponto importante, como destaca esse post, é configurar a rotação do log. Para isso, crie um arquivo haproxy dentro de /etc/logrotate.d/ com o seguinte conteúdo:

/var/log/haproxy*.log
{
    rotate 4
    weekly
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        reload rsyslog >/dev/null 2>&1 || true
    endscript
}

Simples, não?

Índices Secundários no Cassandra

O Cassandra provê índice secundário baseado em hashing. Assim, só é possível fazer consultas da forma:

SELECT user_id FROM users WHERE last_name='silva'

Não se pode, por exemplo fazer buscas do tipo

SELECT user_id FROM users WHERE last_name='s*'

A indexação secundária provida pelo Cassandra apresenta uma outra limitação -- ela não deve ser utilizada com chaves de alta cardinalidade, ou seja, chaves que podem assumir muitos valores. Nesse caso, de acordo com os desenvolvedores de Cassandra, Column Families são mais indicadas para a indexação de valores.

Pelo que entendi, a partir de discussões na lista de desenvolvedores (Cassandra-749), parte dessa limitação vem do fato de que os índices secundários são armazenados localmente em cada nó, e não de maneira distribuída como uma Column Family estaria. Cada nó do cluster armazena na própria MemTable/SSTable ponteiros para os dados indexados. Ao fazer uma busca por um índice secundário, todos os nós respondem ao nó cliente apresentando todos os registros que cada um contém. Essa implementação evita que uma rodada de comunicação seja necessária para saber onde cada chave a ser procurada se encontra no cluster. Além disso, atualizações em uma CF e em seus índice secundários são atômicas, ao contrário de atualizações feitas em diversas CFs. Essa característica deve ser considerada ao analisar que tipo de índice (secundário nativo ou CF) utilizar. Outros benefícios do uso do índice secundário do Cassandra incluem: i) o uso do commitlog local para a sincronização do índice e dos dados; ii) sharding automático do índice, pois cada nó armazena parte dos dados indexados.
Por fim, Cassandra apresenta uma outra característica que aumenta o desempenho dos seus índices. Trata-se dos row bloom filters os quais informam sobre a *ausência* de uma determinada chave muito rapidamente e de forma precisa -- falsos negativos nunca ocorrem, porém falsos negativos são possíveis. 

Quando não se deve utilizar os índices secundários providos pelo Cassandra, porque o campo a ser indexado apresenta alta cardinalidade, por exemplo, há a opção de construir o seu próprio índice através de novas Column Families. A página http://www.datastax.com/docs/0.7/data_model/cfs_as_indexes introduz o assunto e o post de Pranab Ghosh na página http://pkghosh.wordpress.com/2011/03/02/cassandra-secondary-index-patterns/ o explica um pouco mais. Aqui, vou reproduzir parte desses posts.

Column Families como Índices Secundários

Dependendo do tipo de dados que se quer indexar com uma Column Family, deve-se utilizar diferentes estratégias. De qualquer maneira, é preciso considerar que cada entrada (row key) de uma CF é armazenada em uma única máquina e, por isso, manter uma entrada no índice a qual é frequentemente acessada pode gerar um desbalanço de carga na máquina que armazena aquela entrada. 

Para evitar situações assim, existem as seguintes estratégias.

Um para Um

Essa estratégia é indicada para os casos onde para cada valor indexado existe somente um dado correspondente. Nesse caso, o índice inteiro é armazenado como uma row em uma CF. Um exemplo seria um índice que mapeie os nomes dos estados do Brasil para as siglas desses estados.


Cada valor de coluna a ser indexada consiste em uma row com uma coluna por row indexada. O nome da row key deve ser o nome do índice (único no keyspace), os nomes das colunas devem ser os valores das colunas sendo indexados e os valores das colunas devem ser as row keys sendo indexadas.

Um para Alguns

Essa estratégia é indicada para os casos onde para cada valor de coluna indexado existem alguns dados correspondentes. Um exemplo seria uma CF que, dado uma lista de livros a serem vendidos e seus respectivos vendedores, mapeie todos os livros que um dado usuário está vendendo.



Nesse caso, cada índice consiste em uma row com uma super column por valor de coluna indexado. Row key será o nome do índice (único no keyspace). Os nomes das super colunas serão os valores de coluna sendo indexados. Os nomes das sub-colunas serão as row keys sendo indexadas. As sub-colunas não apresentam valores.

Um para Vários

Essa estratégia é indicada para os casos onde para cada valor indexado podem haver muitos dados correspondentes, tantos que seria impossível armazená-lo todos em um só nó. Assim, nessa estratégia se aconselha ter uma CF para cada coluna a ser indexada. Só para lembrar, nas estratégias anteriores, tínhamos uma row de CF para cada coluna a ser indexada, ou seja, uma row por índice. Um exemplo de aplicação dessa estratégia seria uma CF que mapeie, para cada região do Brasil, todos os livros que estão sendo vendidos por usuários dessa região. 



Nesse caso, um índice estará distribuído entre muitas row keys, sendo que cada valor de coluna indexado recebe a sua própria row. Em um caso mais simples, onde não haja necessidade de ordenação, o nome das colunas pode ser as row keys sendo indexadas. Já em um caso em que a ordenação é importante (por data de anúncio, por exemplo), podem-se utilizar super columas cujos nomes sejam as datas de anúncio e os nomes das sub-colunas sejam as row key indexadas.