terça-feira, 14 de junho de 2011

Slony: Clusterização e Replicação de Dados no Postgres

Imagine a seguinte situação: Você é um feliz proprietário de uma empresa que oferece serviços pela internet (pode ser qualquer serviço, seja criativo) e possui seu site em php, seu banco de dados postgres, e é muito feliz com o que faz.

No entanto, você têm a brilhante idéia de oferecer um serviço web (Soap, RMI, Corba, whatever) para o serviço que você já disponibiliza. Com o tempo, seu serviço vai ficando mais e mais famoso, e o acesso vai ficando pesado. Você aumenta o seu link, muda a infra-estrutura interna da sua startup mas algo parece não estar acelerando.

Com certeza o seu gargalo pode ser o banco de dados.



Quando pensamos nesses sistemas com grande volume de dados, a primeira palavra que vem a mente é escalar. Além de desejar que cada uma das pesquisas no sistema executem o mais rápido possível, precisamos criar meios para que, quando necessário, seja fácil adicionar mais recursos (como memória ou novos servidores) e o sistema consiga tirar proveito deles. Para isso muitas vezes precisamos ir além das diversas otimizações de performance e escalabilidade, como por exemplo a criação de um índice para buscas, o uso de caches e de chamadas assíncronas.


Outra abordagem que pode ser feita é a replicação do banco. A simples ideia por trás da replicação é armazenar cópias dos dados em mais de um lugar. Em bancos de dados ela é tradicionalmente implementada através da criação de uma estrutura master-slave. O banco master é aquele onde são efetuados as alterações, que são então replicadas para o slave. Com isso as queries podem ser executadas agora não só em um, mas em dois nós. Quando as máquinas se sobrecarregarem de queries, criamos um novo nó slave que permite aliviar o trabalho das primeiras.

Em bancos como Oracle, DB/2, Informix e até mesmo no mysql, o suporte à replicação de dados é nativa no banco, não havendo a necessidade de se usar recursos externos ao banco. Não sei por que o Postgres nunca teve esse recurso (errata: agora na versão 9.0 já possui) embutido.

Pensando nisso (e também influenciado pelo professor de administração de bancos de dados) estou aqui postando uma solução de replicação para o Postgres, usando o Slony, projeto da comunidade para oferecer clusterização e replicação para o Postgres anterior à versão 9.0.

Slony: Clusterização e Replicação de Dados no Postgres

Não vou entrar muito no mérito da discussão sobre os tipos de cluster, formas de replicação e das diferenças. No post "Cluster != Replicação" do blog Savepoint o autor trata bem desses conceitos.

Primeiramente, é necessário fazer a instalação do Slony-1 nas máquinas que serão parte da estrutura. Para isso, você precisará baixar o código fonte do Slony e do Postgres, ou no caso de usar uma distribuição com gerenciador de pacotes, instalar do repositório.

No caso do Ubuntu, você pode instalar simplesmente usando o comando:

sudo apt-get install postgresql-8.4 postgresql-8.4-slony1 slony1-bin

Este comando irá instalar o postgres e o slony na mesma versão. Se você já possuir um banco de dados que deseja replicar, a melhor solução é a compilação. Usar os pacotes do repositório implica que você está criando um banco novo. Em nosso caso, estamos criando um banco de dados de exemplo para ilustrar o funcionamento da replicação.

Criação do banco Master

Após instalado o Slony e o Postgres, vamos logar com a conta de usuário postgres recém criada pela instalação do banco. Durante todo o nosso tutorial, os comandos deverão ser executados à partir desse usuário. Agora vamos criar nosso banco de dados master:

sudo su - postgres
createdb master

Uma exigência do Slony em relação aos bancos que serão replicados é a necessidade de habilitar o suporte à linguagem PL/SQL no banco, já que a interação entre o Slony, o banco master e os bancos slave se dá através de gatilhos disparados por funções implementadas em PL:

createlang -U postgres -h localhost plpgsql master

Depois de criarmos o banco, vamos popular o mesmo. Abra o console do postgres no banco de dados master com o comando:

psql master

e execute as seguintes instruções SQL:

GRANT ALL PRIVILEGES ON DATABASE master TO vindex;

CREATE SEQUENCE contato_seq START WITH 1;

CREATE TABLE contato (
    id INTEGER PRIMARY KEY,
    nome VARCHAR(50)
);

INSERT INTO contato (id, nome) VALUES
    ((SELECT NEXTVAL('contato_seq')), 'Joao'),
    ((SELECT NEXTVAL('contato_seq')), 'Maria'),
    ((SELECT NEXTVAL('contato_seq')), 'Jose');

\q

Após a execução destes comandos, serão criados no banco uma sequência chamada contato_seq que é usada para fazer o auto-incremento do id na tabela contato, uma tabela contato com os campos id e nome e a inserção de três novos contatos.

Criação do banco Slave

Agora nós vamos criar um banco de dados que irá replicar os dados do banco master. Este banco será criado também localmente, para facilitar a explicação deste tutorial. Caso você deseje usar um banco externo, é necessário somente habilitar no arquivo /etc/postgresql/8.4/main/pg_hba.conf para que o servidor aceite conexões externas.

Como vamos fazer local, vamos continuar criando o banco slave, com o comando:

createdb slave

Agora, precisaremos copiar a estrutura do banco de dados master no nosso banco de dados slave. O Slony não faz replicação de estrutura do banco de dados, então é necessário após configurar o banco master, exportar sua estrutura e incorporá-la no banco slave. Isso pode ser feito com os seguintes comandos:

pg_dump -s -U postgres -h localhost master > dump.master
cat dump.master | psql -U postgres -h localhost slave

O primeiro comando faz o dump da estrutura do master na forma de SQL no arquivo dump.master e em seguida, é usado para criar a estrutura do banco slave.

Depois de criada a estrutura do banco, precisaremos configurar o acesso à nosso banco. Isso pode ser feito de várias formas, mas a mais simples é através do arquivo .pgpass. Neste arquivo iremos adicionar as configurações de autenticação para os nossos bancos. Este arquivo é lido pelo Slony na hora de se conectar aos bancos. Os comandos para criar são os seguintes:

touch .pgpass
echo "localhost:5432:master:postgres:senha" >> .pgpass
echo "localhost:5432:slave:postgres:senha" >> .pgpass
chmod 0600 .pgpass

Aqui nós criamos o arquivo .pgpass, caso não exista, adicionamos duas linhas relativas à cada um dos nossos bancos e setamos a permissão de acesso ao arquivo como sendo somente para o usuário. Nas linhas, são passadas diversas informações à respeito da conexão ao banco, com a sintaxe "host:porta:banco:usuario:senha", lembrando que a senha é a senha do usuário postgres do banco de dados, não do sistema operacional. A conexão também pode ser feita usando outros usuários, mas não vou mostrar aqui.

Configuração do Slony

Depois de criada toda a estrutura dos bancos de dados, vamos começar a configurar o Slony para fazer funcionar a replicação. Para isso, nós precisamos passar para o mesmo as configurações de nosso cluster. Isso é feito através do comando slonik. Abaixo segue a sintaxe do comando:

slonik <<_EOF_
cluster name = sql_cluster;

node 1 admin conninfo = 'dbname=master host=localhost user=postgres password=senha';
node 2 admin conninfo = 'dbname=slave host=localhost user=postgres password=senha';

init cluster (id = 1, comment = 'Node 1');

create set (id = 1, origin = 1, comment = 'Conjunto do Cluster');

set add table (set id = 1, origin = 1, id = 1, full qualified name = 'public.contato', comment = 'Tabela contato');
set add sequence (set id = 1, origin = 1, id = 2, full qualified name = 'public.contato_seq', comment = 'Sequencia contato_seq');

store node (id = 2, comment = 'Node 2');
store path (server = 1, client = 2, conninfo = 'dbname=master host=localhost user=postgres password=senha');
store path (server = 2, client = 1, conninfo = 'dbname=slave host=localhost user=postgres password=senha');

store listen (origin = 1, provider = 1, receiver = 2);
store listen (origin = 2, provider = 2, receiver = 1);
_EOF_

Esse comando possui várias informações importantes sobre o cluster. Vamos falar linha-a-linha.

Primeiramente, é dado nome ao cluster. Na próxima linha, é criado o primeiro nó do cluster, que no nosso caso é o banco de dados master. Em seguida, é criado o segundo nó, que é nosso banco slave. Após, o cluster é iniciado. Depois de iniciado o cluster, é criado um set de replicação, onde são inseridas, abaixo, a tabela e a sequência que criamos. Depois, configuramos um nó de armazenamento e informamos a forma como ocorrerá o processo de replicação dos dados.

Depois de executado esse comando, vamos verificar o status do nosso cluster. Isso é feito com os comandos:

slon sql_cluster "dbname=master user=postgres password=senha" &
slon sql_cluster "dbname=slave user=postgres password=senha" &

Estes comandos permitem visualizar o que está ocorrendo com os bancos de dados. É também com estes comandos que podemos verificar se a replicação está ocorrendo e também eventuais erros que possam aparecer no processo.

Depois de verificar que a replicação está funcionando, devemos informar para o slony que o nosso set está pronto para funcionar. Para isso, vamos inscrever nosso set como apto para funcionar. O comando à seguir faz exatamente isto:

slonik <<_EOF_
cluster name = sql_cluster;

node 1 admin conninfo = 'dbname=master host=localhost user=postgres password=senha';
node 2 admin conninfo = 'dbname=slave host=localhost user=postgres password=senha';

subscribe set (id = 1, provider = 1, receiver = 2, forward = yes);
_EOF_

A partir deste momento, nosso cluster está funcionando e a replicação já deve estar ocorrendo. Você pode verificar isso com simples comandos SQL:

echo "select * from contato" | psql master
echo "select * from contato" | psql slave
echo "insert into contato (id, nome) values (1000, 'Teste');" | psql master
echo "select * from contato" | psql master
echo "select * from contato" | psql slave

Se o banco foi replicado corretamente, na primeira consulta ao banco slave deverão haver os registros originais do banco master e depois da inserção, após a sincronização, o registro inserido. Caso não apareça nenhum registro na tabela, algum erro deve ter acontecido. Neste caso, confira os logs do slony para o master e slave, pois eles fornecem bastante informação sobre o possível erro.

Dependendo do caso, pode ser necessário remover a configuração do cluster e recriá-la. Isso pode ser feito com o seguinte comando:

slonik <<_EOF_
cluster name = sql_cluster;

node 1 admin conninfo = 'dbname=master host=localhost user=postgres password=senha';
node 2 admin conninfo = 'dbname=slave host=localhost user=postgres password=senha';

uninstall node ( id = 1 );
uninstall node ( id = 2 );
_EOF_


Conclusão

Escalar os dados de uma aplicação é uma tarefa complicada e a solução varia de acordo com o caso. Aqui demonstramos uma solução baseada no Postgres 8.4. Nas versões à partir da 9.0 a replicação é nativa do SGBD e pode ser efetuada à partir das próprias ferramentas de gerenciamento do banco, como o pgAdmin.

Em alguns casos, simplesmente a replicação pode ser suficiente para atender o nosso cliente lá do começo do post. Em outros casos, pode ser necessária uma estrutura diferente. Perceber essa necessidade, entender como funciona processamento distribuído de dados e encontrar a combinação ideal é o grande desafio que o desenvolvedor ou o administrador de banco de dados passa.

Fontes:

SQL Magazine
MSDN Brasil
Viva o Linux
Cluster != Replicação