Friday, June 3, 2011

Í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.

No comments:

Post a Comment