O documento discute Active Record e o projeto Octopus. Active Record é um padrão de mapeamento objeto-relacional usado no framework Rails que mapeia classes e objetos para tabelas e registros de banco de dados. O projeto Octopus é uma biblioteca que adiciona suporte a sharding de banco de dados para Active Record, permitindo que dados sejam particionados em vários bancos de dados.
PostgreSQL Tuning: O elefante mais rápido que um leopardo
Projeto Octopus - Database Sharding para ActiveRecord
1. Active Record
&
Projeto Octopus
Thiago Pradi - Ruby Masters Conf 2011
2. Thiago Pradi
• Desenvolvedor Web na Taoweb Zinfinit
• Desenvolvedor Ruby a 3 anos
• Bacharelando em Ciência da Computação
• Participou do Ruby Summer of Code 2010
4. ActiveRecord - Padrão
• Objeto encapsula o acesso a base
• Objeto guarda regras de negócio
• Cada classe representa uma tabela
• Cada instância representa um registro
• Cada coluna representa um atributo
11. Gerência de Conexão
• Cada instância pede ao “Gerenciador de
conexões” uma conexão quando
necessário
• O Model usa a conexão e a devolve ao
gerenciador
• Proporcionando Economia de recursos
• e Problemas de concorrência?!
17. Connection
Specification
• Os atributos da conexão são carregados via
Railties ou definidos manualmente.
• Faz o require do adapter necessário, e
instância uma nova especificação da
conexão, passando como parâmetro para o
Construtor do ConnectionPool
• Adapters?!
18. Connection Adapter
• Classe abstrata responsável por definir os
métodos básicos necessários para suportar
um novo banco
• “Abstração” da conexão ao banco de
dados
• Todos os Adapters herdam do Abstract
Adapter
19. Adapters Inclusos no
Rails
• Mysql (Mysql e MySQL2 Adapters)
• PostgreSQL (PostgreSQL Adapter)
• Sqlite (SQLite e SQLite3 Adapters)
20. Voltando ao nosso
tema...
• Como temos uma conexão por model, uma
classe interna é utilizada para gerenciar as
instâncias de ConnectionPool
• Classe que implementa comportamento de
Hash
• Utilizando o nome do modelo como chave,
retorna o ConnectionPool adequado.
21. Models
class User < ActiveRecord::Base
establish_connection :development_2
end
class Post < ActiveRecord::Base
end
27. Database Sharding
• Divisão dos dados entre várias instâncias
de banco de dados
• Motivado por problemas de escalabilidade
(Conexões paralelas, alto volume de
trafego).
• Cada fragmento (instância) salva uma parte
da base de dados
28. Prós / Sharding
• Bases menores significa mais performance
• Se um servidor cair, os outros continuam
operando
• Consultas distribuidas, possibilitando
trabalhos em paralelo.
29. Contras / Sharding
• Consultas entre Shards
• Dificuldade de balanceamento futuro
• Implementações não oficiais
• Conflito entre bases
31. Master/Master
• Cada instância fica com a replicação
configurada, sendo slave e master ao
mesmo tempo
• Geralmente, no caso de duas bases,
números pares e ímpares são utilizados
para ID.
33. Master/Slaves
• Cada consulta é enviada para um log
• o banco escravo é configurado para ler o
log do master
• Os escravos ficam na escuta do log
• Quando uma consulta de escrita chega, os
escravos executam ela na sua instância.
34. Master/Slaves
• Dados não particionados
• Suporte oficial pela maioria dos banco de
dados
• Somente uma instância escreve os dados, as
outras funcionam somente para leitura
• Cada instância carrega uma cópia da base
35. Master/Slaves
• Base de dados contém backup em tempo
real
• Processamento de leitura distribuído entre
vários servidores
36. MySQL
• Master/Slaves suporte oficial
• Funciona com Log Binário
• Master/Master com o MySQLMM ( Não
Oficial)
• Mysql Cluster
37. PostgreSQL
• Master/Slave incluso com a versão 9.0
• Funciona via streaming de Log
• Várias alternativas: http://
wiki.postgresql.org/wiki/
Replication,_Clustering,_and_Connection_
Pooling
38. Oracle
• Replicação built-in
• Várias soluções para Replicação entre
diferentes bases de dados:
• http://www.oracle.com/technetwork/
database/features/data-integration/
default-159085.html
43. Problemas
• Problema simples: alguns relatórios pesados
estavam destruindo a performance do
banco
• Existia um banco Replicado, para onde
algumas consultas poderiam ser enviadas
• Simples! Vamos procurar uma
implementação de Database Sharding!
45. Infelizmente
• Falta de documentação
• Falta de exemplos
• Bibliotecas complexas e difíceis de
customização.
• Não compatíveis com Rails 3
46. Então, resolvi meu
próprio problema!
• O Projeto Octopus foi aprovado para o
Ruby Summer of Code 2010.
• Trabalhei em conjunto com meu mentor
Mike Perham, autor da gem DataFabric
47. Idéia Inicial
• Implementação simples e flexível
• Possibilidade de escolher quais consultas
devem ser enviadas para qual shard.
• Suportar Sharding e replicação
• Suporte a mover massas de dados entre
Shards
48. Conversa com Mentor
• Foco nas primeiras features, e mais
importantes
• Inicio da implementação
49. Octopus nascendo!
• Idéia baseada no Masochism e no
DataFabric
• Monkey-patch no método que retorna a
conexão, e ao invés de retornar a conexão
real, um “Proxy” é retornado
• Classe Octopus::Proxy
50. Arquivo de
Configuração
• localizado em config/shards.yml
• Salva as informações sobre o modo de
operação, e o shards disponíveis.
• Possibilita agrupamento de shards
51. Exemplo:
octopus:
replicated: true
shards:
slave1:
database: octopus_shard2
adapter: mysql
host: localhost
slave2:
database: octopus_shard3
adapter: mysql
host: localhost
53. Exemplo:
class CreateUsersOnBothShards < ActiveRecord::Migration
using(:brazil, :canada)
def self.up
User.create!(:name => "Both")
end
def self.down
User.delete_all()
end
end
54. Exemplo:
class CreateUsersOnShardsOfAGroup < ActiveRecord::Migration
using_group(:country_shards)
def self.up
User.create!(:name => "Group")
end
def self.down
User.delete_all()
end
end
58. Controle de shards
# Busca o usuário no shard1
@user = User.using(:shard1).find_by_name("Joao")
# Busca o usuário no master
@user2 = User.find_by_name("Jose")
#Define um novo nome de usuário
@user.name = "Mike"
# Salva o usuário na base de dados correta.
@user.save
60. Stack do Rails
class ApplicationController < ActionController::Base
around_filter :select_shard
def select_shard(&block)
if logged_in?
Octopus.using(current_user.shard, &block)
else
yield
end
end
end
61. Replicação
• Uma base Master
• Vários Slaves
• Escritas enviadas para o Master
• Leituras enviadas para os Slaves
62. Stack do Rails
• Totalmente integrado com a stack inteira
do Rails
• Permite uma experiência de usuário
simples e funcional.
63. Octopus Off Rails
• Pensado para funcionar off da stack do
Rails
• Funciona com Sinatra, etc
68. Idéias para o Futuro
• Configuração automática do banco de
dados para replicação
• Ferramenta para mover/sincronizar dados
entre os shards
• Melhores algoritmos de balanceamento
69. Participe do Projeto!
• O Octopus não é perfeito
• Submeta idéias/patches
• Arquivo TODO.txt na raiz do projeto
• Leia a Wiki / README no github