Por necessidade, resolvi realizar testes em bancos de dados NoSQL. Estava precisando de um banco de dados que comporte pelo menos 1.5 milhões de registros por dia e que forneça uma forma rápida para realizar consultas.
Testei três NoSQL: Cassandra, MongoDB e CouchDB.
Criei uma pequena aplicação em PHP para inserir 500 milhões de registros (exagerei né? Mas precisava ter certeza) e um PHP para listar a quantidade de registros. Percebi que ainda tem poucos tutoriais que mostram de forma clara como as coisas funcionam e por isso resolvi escrever alguns pequenos posts mostrando o caminho das pedras.
Resolvi então escrever alguns posts sobre banco de dados NoSQL com PHP.
Vamos começar falando sobre o Cassandra com PHP.
1- Por onde começar?
A primeira coisa a fazer é acessar o site do projeto http://cassandra.apache.org e fazer o download do Cassandra. A versão que usei para os testes foi o Cassandra 0.6.3.
Passos para baixar e iniciar o Cassandra:
- wget http://ftp.unicamp.br/pub/apache/cassandra/0.6.3/apache-cassandra-0.6.3-bin.tar.gz
- tar -xzvf apache-cassandra-0.6.3
- cd apache-cassandra-0.6.3
- ./bin/cassandra -f
Depois desses comandos o Cassandra estará funcionando com as configurações padrão. Mas antes de começarmos a usar é necessário entenderemos a estrutura desse banco de dados NoSQL. Pra quem é acostumar com bancos de dados tradicionais, é necessário um pouco de esforço para a quebra de paradígma.
2- Conhecendo a estrutura do Cassandra
O Cassandra é um banco de dados NoSQL e armazena as informações no formato chave = valor, mas para quem está acostumado com bancos de dados tradicionais, tabelas, joins, normalização, etc., é difícil colocar na cabeça que tudo o que você aprendeu a vida toda foi por água abaixo. :)
Li vários artigos e tutoriais para entender o esquema de armazenamento e indexação do Cassandra. Um excelente ponto de partida é este artigo: WTF is a SuperColumn? An Intro to the Cassandra Data Model.
Pra quem não sabe o que significa WTF: What the fuck... ou seja, que porra é uma supercolumn?!?! :)
Com este artigo dá para entender bem o funcionamento do esquema de armazenamento e indexação, por isso, não explicarei detalhadamente como funciona, apenas superficialmente através de exemplo para complementar o artigo citado.
De forma bem simplificada:
- Column: Armazena chave, valor e timestamp. Todos os dados são armazenados em columns. A column não precisa ser criada em arquivo de configuração, basta inserir uma nova column com chave/valor que o banco de dados cria automaticamente. Column - ['id']['chave'] = ['valor']
- SuperColumn: Armazena chave = chave/valor/timestamp. É como se fosse um conjunto de columns. A SuperColumn na verdade é uma ColumnFamily do tipo Super. Possui duas chaves. SuperColumn - ['id']['chave1']['chave2'] = valor
- ColumnFamily: É uma família de colunas, ou seja, é o agrupamento de várias chave = valor. É necessário criar a ColumnFamily dentro das configurações do cassandra (storage-conf.xml). Ou seja, as Columns ficam armazenadas em uma ColumnFamily.
Exemplo:
ColumnFamily: Usuarios
['junior']['login'] = ['junior']
['junior']['nome'] = ['Nivaldo']
['junior']['sobrenome'] = ['Montenegro']
['junior']['email'] = ['nivaldomjunior@gmail.com']
['usuario2']['login'] = ['usuario2']
['usuario2']['nome'] = ['Fulano']
['usuario2']['sobrenome'] = ['de tal']
['usuario2']['email'] = ['fulano@gmail.com']
['usuario2']['fone'] = ['(83)1234-1234']
Usuarios = { // ColumnFamily
junior: { // Esta é a chave da CF (id)
// Dentro da CF podemos ter um número infinito de chave/valor
login: "junior",
nome: "Nivaldo",
sobrenome: "Montenegro",
email: "phatduckk@example.com",
}, // Fim
usuario2: { // Esta é a chave para outra linha na CF (id)
// Podemos inserir outro número infinito de chave/valor e não precisa ter as mesmas chaves/valor que outras linhas da CF
login: "junior",
nome: "Nivaldo",
sobrenome: "Montenegro",
email: "phatduckk@example.com", fone: "(83) 1234-1234"
},
}
- KeySpace: É um conjunto de ColumnFamily. Analogamente, é como se fosse um banco de dados criado dentro do cassandra. É necessário realizar configurações no arquivo storage-conf.xml para criar as KeySpaces e realizar um restart no Cassandra.
Evoluindo o exemplo dado para a ColumnFamily, podemos transformar a ColumnFamily em uma SuperColumn e termos dados mais organizados sobre os clientes.
Exemplo SuperColumn:
Exemplo:
SuperColumn: Agenda
['junior']['amigo1']['nome'] = ['Amigo 1']
['junior']['amigo1']['telefone'] = ['(88)1234-1234']
['junior']['amigo2']['nome'] = ['Amigo 2']
['junior']['amigo2']['telefone'] = ['(99)4312-4312']
['usuario2']['amigox']['nome'] = ['amigox']
['usuario2']['amigox']['telefone'] = ['(77)9987-0987']
['usuario2']['amigoy']['nome'] = ['Amigo y']
['usuario2']['amigoy']['email'] = ['fulano@gmail.com']
['usuario2']['amigoy']['fone'] = ['(83)1234-1234']
Agenda = { // SuperColumn
junior: { // ColumnFamily amigo1: { //Chave da ColumnFamily
// Dentro da CF podemos ter um número infinito de chave/valor
nome: "Amigo 1",
telefone: "(88)1234-1234",
} //Fim do amigo1
amigo2: { //Outra chave na ColumnFamily
nome: "Amigo 2",
telefone: "(99)4312-4312",
} // Fim do amigo 2
}, // Fim da CF junior
usuario2: { // Outra ColumnFamily
amigox: { //Chave da ColumnFamily
// Podemos inserir outro número infinito de chave/valor e não precisa ter as mesmas chaves/valor que outras linhas da CF
nome: "amigox",
telefone: "(77)99870987",
} //Fim amigox
amigoy: { //Outra CF
nome: "Amigo Y",
fone: "(83)1234-1234"
email: "fulano@gmail.com",
} //Fim amigoy
}, //Fim da CF usuario2
}
Ou seja, temos os seguintes níveis no Cassandra:
KeySpaces
ColumnFamily
Columns
SuperColumn
ColumnFamily
Columns
Um fato importante é que as buscas necessariamente precisam ser realizadas usando os índices, ou seja, as chaves das ColumnFamilys. Não tem como fazermos as consultas usando os valores finais das chaves diretamente no banco de dados. Para isso é necessário buscarmos todos os dados e realizarmos a consulta usando a linguagem de programação. Por isso, é extremamente importante criar índices que realmente sejam utilizados e não usar números sequencias como costumamos fazer nos campos id das tabelas em um banco de dados tradicional.
3- Configurando o cassandra para atender o exemplo acima
É necessário configurar o Cassandra para criar as KeySpaces, ColumnFamilys e SuperColumns (ColumnFamily do tipo Super) que iremos precisar para armazenar os nossos dados. As configurações são relativamente simples, mas precisamos definir qual o tipo de ordenação que faremos para cada ColumnFamily e SuperFamily, pois os dados são armazenados e ordenados automaticamente de acordo com a configuração criada para a ColumnFamily ou SuperColumn.
Exemplo de configuração:
<Keyspace Name="Teste">
<!-- Definição da ColumnFamily -->
<ColumnFamily CompareWith="UTF8Type" Name="Usuarios"/>
<!-- Definição da SuperColumn -->
<ColumnFamily CompareWith="UTF8Type" Name="Agenda"
CompareSubcolumnsWith="UTF8Type" ColumnType="Super"/>
</Keyspace>
O parâmetro CompareWith nas ColumnFamilys é o que vai determinar a forma de ordenação da ColumnFamily. O CompareWith no nosso exemplo vai ser usado para ordenar a chave da ColumnFamily, que no nosso exemplo tem dois valores: junior e usuario2. No artigo citado acima (esse link) tem um exemplo bem detalhado sobre a forma de ordenação.
Já nas SuperColumns, o parâmetro CompareWith ordena o nome da ColumnFamily, no nosso exemplo os valores junior e usuario2, e o parâmetro CompareSubColumnsWith ordena a chave da ColumnFamily, que no nosso exemplo tem os valores amigo1, amigo2, amigox e amigoy.
4- Conclusão
O Cassandra é um banco de dados NoSQL extremamente flexível e poderoso, principalmente quando se pensa em escalabilidade, mas a organização dos dados é o que vai determinar o sucesso da utilização desse tipo de banco de dados. É necessário realizar um trabalho minucioso em cima dos índices para que as buscas sejam realmente rápidas e eficientes, minimizando a filtragem de dados usando a linguagem de programação.
Espero que eu tenha esclarecido as suas dúvidas e no próximo post vou falar um pouco sobre como utilizar o Cassandra com PHP em um servidor com Debian.