Todas as publicações

Como migrar do MySQL para o Couchbase Server 2.0: Parte 2

Em minha última peçaNo artigo anterior, analisamos a mecânica básica do MySQL e do Couchbase Server 2.0 e comparamos as formas de modelar os dados e como as consultas básicas e as listas de registros funcionam, desde a instrução SQL do MySQL até a visualização do Couchbase Server.

Desta vez, começaremos analisando como escrever essas visualizações e elementos específicos das consultas SQL, como as cláusulas WHERE e GROUP BY, e o processo de migração real dos dados e da lógica do aplicativo para o Couchbase Server.

Pense nas consultas que você deseja executar

Como já vimos, a consulta a um banco de dados do Couchbase Server é, na verdade, um processo de dois estágios. A primeira etapa envolve a criação de uma visualização dos seus dados que ofereça suporte à pesquisa ou seleção nos documentos do seu banco de dados. A segunda parte é o URL e os valores-chave que você deseja selecionar na visualização. Isso significa que, ao pensar em como deseja obter as informações do banco de dados do Couchbase, você precisa pensar em como deseja pesquisar os dados, e isso permitirá que você defina a estrutura da visualização que precisa ser escrita.

O Couchbase Server usa exibições para criar listas de documentos do banco de dados, e a saída de uma exibição é uma chave e um valor correspondente. Tanto as chaves quanto os valores podem ser qualquer valor JSON. A chave na saída é importante porque forma a base do mecanismo de pesquisa, classificação e paginação.

Por exemplo, uma função de visualização que apenas gera todos os documentos de receita no banco de dados, usando o título da receita como chave, pode ter a seguinte aparência:

function(doc) {
Se (doc.type === 'recipe' &&
doc.title !== null) {
emit(doc.title, null);
}
}

Isso é chamado de função map - ela mapeia as informações dos documentos em um formato que você deseja usar. Uma segunda etapa opcional é chamada de função de redução e é análoga às funções de agregação do MySQL e à cláusula GROUP BY.

Ao migrar do MySQL, a construção de suas exibições para corresponder às informações que você deseja gerar ou consultar também pode afetar a forma como você armazena as informações. Vamos voltar ao exemplo da receita. No MySQL, você usaria uma consulta na tabela de ingredientes para comparar com cenoura e obter uma lista de receitas correspondentes. Por exemplo, como uma consulta SQL, você expressaria isso como :

SELECT receita.id, receita.título FROM ingredientes join (receita) on
(ingredients.recipeid = recipe.id) where ingredient = 'carrot' (ingredientes.recipeid = receita.id)

No Couchbase, uma maneira de obter o mesmo resultado é escrever uma visualização que produza uma linha para cada ingrediente do nosso documento de receita, assim:

function(doc) {
Se (doc.type == 'recipe' &&
doc.title !== null) {
para id em doc.ingredients {
emit(doc.ingredients[id].ingredient, null);
}
}
}

Para consultar, você deve especificar a chave da saída que deseja corresponder. A consulta é realizada pela API REST ou pela biblioteca do cliente, mas a especificação é a mesma. Por exemplo, se quiser pesquisar "cenouras", você deve especificar isso como a chave que está procurando. Usando a API REST:

http://localhost:8092/recipes/_design/dev_ingred/_view/byingred?key=%22carrots%22&connection_timeout=60000&limit=10&skip=0

Lembre-se de que a chave é qualquer valor JSON, portanto, os valores de chave que você fornece à consulta também devem ser JSON codificados corretamente. Como você pode ver neste exemplo, a visualização fornece a base da sua consulta e o banco de dados acessa o mecanismo de seleção.

O uso de exibições dessa forma significa que a maioria dos aplicativos acabará sendo construída com várias exibições simultâneas. O processo de criação das exibições não é diferente da construção das consultas principais dentro do aplicativo e, em seguida, da otimização da consulta e dos índices que produzem o resultado. A principal diferença é que a definição da visualização é armazenada no banco de dados, e não como uma consulta no código do aplicativo.

Agregações

No MySQL, a agregação é usada em vários lugares diferentes. A agregação é tratada por uma combinação da cláusula GROUP BY e funções que coletam ou resumem as informações. Usando nosso exemplo de receita novamente, podemos executar uma consulta que forneça uma contagem das diferentes receitas pelos ingredientes que elas contêm. Por exemplo, uma consulta como esta:

SELECT ingrediente,count(recipeid) FROM ingredientes GROUP BY ingrediente

vai nos fornecer uma lista de ingredientes, com uma contagem ao lado de cada um para mostrar quantas receitas têm aquele ingrediente. Em um aplicativo final, isso compõe os "ingredientes da receita mais quente" ou, em outros aplicativos, a "postagem de blog mais popular" e assim por diante.

Por exemplo, para obter o equivalente ao que foi dito acima, podemos usar a mesma função map que usamos acima. Em seguida, podemos usar uma das funções de redução incorporadas, _count, que produzirá uma contagem com base no número de chaves exclusivas geradas pela função emit(). Se houver uma função de redução, ela será usada automaticamente quando a exibição for acessada. O acesso à visualização produz uma lista do número de receitas para ingredientes individuais:

{"rows":[
{"key": "banana", "value":20},
{"key": "bananas", "value":33},
{"key": "baps", "value":1},
{"key": "barbecue sauce", "value":1},
{"key": "basmati rice", "value":16},
{"key": "bay leaf", "value":58},
{"key": "bay leaves", "value":35},
{"key": "bean sprouts", "value":18},
{"key": "bean thread noodles", "value":1},
{"key": "beef braising steak", "value":17}
]
}

As funções de agregação e redução também podem ser usadas para outras operações. Por exemplo, na parte 1, mostrei uma receita com informações de tempo para a preparação e o cozimento. Poderíamos usar uma função map e reduce juntas para obter o tempo total de cozimento de cada receita. Primeiro, escreva uma função de mapa que produza o título da receita como chave e as informações de tempo de cozimento como um valor:

function (doc, meta) {
if (doc.title) {
emit(doc.title, parseInt(doc.preptime));
emit(doc.title, parseInt(doc.cooktime));
}
}
Garantimos que a saída do valor seja um número (usando a função JavaScript parseInt()). O uso da função _sum incorporada fará com que a exibição calcule o total de todos os valores em relação a uma determinada chave (o título da nossa receita):
{"rows":[
{"key": "Apple pie", "value":55},
{"key": "Apricot cheesecake", "value":70},
{"key": "Barbecued aubergine", "value":10},
{"key": "Barbecued beefburgers", "value":30},
{"key": "Barbecued corn on the cob slices", "value":13},
{"key": "Beef in red wine", "value":144},
{"key": "Blue cheese and tomato dip", "value":5},
{"key": "Cajun chicken", "value":20},
{"key": "Caribbean cobbler stew", "value":95},
{"key": "Chappati or roti", "value":45}
]

No SQL, você pode simplesmente armazenar o tempo total de cozimento na tabela para facilitar a consulta dessas informações. No Couchbase, podemos usar a capacidade de processar e combinar informações ao criar a visualização.

Consultas de intervalo

Para consultas mais complexas, em que você está selecionando vários campos, é necessário criar uma exibição adequada e, em seguida, usar o argumento chave da consulta para especificar o que está procurando. Quando quiser extrair informações de um intervalo de valores, você pode usar a chave inicial e a chave final para especificar o intervalo a ser retornado pela exibição. Por exemplo, a construção de uma exibição com o tempo de cozimento como chave permitiria a saída de uma lista de todas as receitas correspondentes em que o tempo de cozimento estivesse entre 5 e 25 minutos:

http://localhost:8092/recipes/_design/recipes/_view/by_cooktime?startkey=%225%22&endkey=%2225%22

Isso é análogo a:

SELECT title FROM recipe WHERE cooktime >= 5 AND cooktime <= 25

Consulta em vários campos

Para consultar vários campos, você precisa criar uma exibição que produza os dois (ou mais) campos que você deseja consultar. Ao procurar receitas, por exemplo, é bastante comum querer procurar receitas com um determinado ingrediente, mas também dentro de um determinado limite de tempo. Quando você abre a geladeira 30 minutos antes de sair e encontra tomates, você quer saber o que pode cozinhar em 20 minutos com tomates. Para fazer isso, produzimos uma exibição que emite uma chave com o ingrediente e o tempo necessário para preparar a receita, assim:

função (doc, meta) {
se (doc.ingredientes) {
para (i=0; i < doc.ingredientes.comprimento; i++)                                                              {                                                                                                         se (doc.ingredientes[i].ingrediente != nulo)
{
emitir([doc.ingredientes[i].ingrediente, parseInt(doc.tempo total)], nulo);
}
}
}
}
Agora, para obter todas as receitas com tomates que podem ser preparadas em 20 minutos, podemos pesquisar:
["tomates",20]
Ou de forma mais explícita:
http://localhost:8092/recipes/_design/dev_bytime/_view/byingredtime?full_set=true&key=%5B%22tomatoes%22%2C20%5D&stale=false&connection_timeout=60000&limit=10&skip=0
É claro que, se você tem 20 minutos para preparar uma receita, você tem menos de 20 minutos. Então, de fato, podemos usar uma consulta de intervalo com uma chave inicial de ["tomates",0] e uma chave final de ["tomates",20] e agora obteremos todas as receitas que podem ser preparadas em 20 minutos contendo cenouras:
{"total_rows":27446, "rows":[
{“id”:”4997AFDE-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,5],”value”:null},
{“id”:”79C16D8A-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,5],”value”:null},
{“id”:”CF52D23E-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,5],”value”:null},
{“id”:”42F5D520-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,6],”value”:null},
{“id”:”1A0AB61C-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,8],”value”:null},
{“id”:”4D0F9DF2-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,8],”value”:null},
{“id”:”50CE1676-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,10],”value”:null},
{“id”:”564A66CC-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,10],”value”:null},
{“id”:”95E9464A-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,10],”value”:null},
{“id”:”9A78477E-3027-11E2-BB0D-B67A7E241592″,”key”:[“tomatoes”,10],”value”:null}
]
}

Legal! Em comparação com a instrução SQL:

SELECT recipeid from recipes JOIN ingredients ON (ingredients.recipeid = recipe.recipeid) WHERE ingredients.ingredient = 'tomatoes' and totaltime <= 20

A propósito, observe que obtemos o ID de cada documento para que possamos carregar o documento e, portanto, a receita. Talvez não tenhamos o título da receita em nossa saída, mas as bibliotecas de clientes podem carregar automaticamente as informações do documento para nós. A parte difícil é realizar a pesquisa.

Classificação da saída da visualização

A classificação da saída de uma visualização é automática. A saída de cada visualização é classificada automaticamente pela chave gerada por cada visualização. Isso é necessário porque, sem isso, a consulta de intervalo seria impossível de ser realizada. Isso torna algumas operações rápidas e fáceis. Se você quiser gerar uma lista de receitas em ordem alfabética pelo título, crie uma visualização que gere o título da receita como chave. Isso significa que a função map:

função (doc, meta)
{
emit(doc.title, null);
}
Acessado por meio da opção Exibir como:
http://localhost:8092/recipes/_design/recipes/_view/by_title

É análogo a:

SELECT title FROM recipe ORDER BY title

Para obter uma lista em ordem decrescente no MySQL, use a palavra-chave DESC:

SELECT title FROM recipe ORDER BY title DESC

No Couchbase Server, você usa o parâmetro descendente para o URL:

http://localhost:8092/recipes/_design/recipes/_view/by_title?descending=true

Paginação

No MySQL, a paginação é feita usando uma combinação das cláusulas LIMIT e OFFSET, por exemplo, para obter os primeiros 10 registros da nossa consulta de título de receita:

SELECT title FROM recipe ORDER BY title LIMIT 10

Para obter os próximos 10 registros:

SELECT title FROM recipe ORDER BY title LIMIT 10 OFFSET 10

Com o Couchbase Server, você usa uma configuração semelhante, fornecendo os argumentos limit e skip à consulta de visualização. Para obter os dez primeiros registros:

http://localhost:8092/recipes/_design/recipes/_view/by_title?limit=10

E os próximos 10 registros:

http://localhost:8092/recipes/_design/recipes/_view/by_title?limit=10&skip=10

Quando se trata de blocos de páginas relativamente curtos, isso funciona bem. No caso de páginas mais longas, ou quando é possível que você esteja pulando um grande número de linhas de saída, é possível usar startkey_docid para avançar na exibição. Isso é mais eficiente para esses cenários de paginação mais longos. Verifique o documentação para obter mais informações.

Carregando o objeto principal

Em nosso exemplo de banco de dados de receitas no MySQL, há várias tabelas necessárias para carregar as informações do banco de dados sobre uma única receita para que ela possa ser exibida. No Couchbase Server, quando você tiver o ID do documento de uma determinada receita, terá acesso a toda a estrutura de registro da receita, pois ela foi armazenada no banco de dados do Couchbase Server.

Com o MySQL, a necessidade de simplificar o mecanismo de carregamento de objetos tão complexos levou a vários projetos de suporte diferentes que podem ser usados como uma forma de armazenar em cache os dados complexos carregados para que fiquem mais acessíveis (memcached, por exemplo). O Couchbase Server não elimina a necessidade de carregar as informações do banco de dados, mas faz com que o carregamento do registro da receita seja uma única operação, em vez de várias consultas e formatação desses dados na estrutura usada para exibição.

No SQL, por exemplo, você pode carregar os dados da receita examinando as tabelas individuais e construindo seu objeto, executando várias consultas diferentes:

SELECT * FROM recipe WHERE recipeid=23434
SELECT * FROM ingredients WHERE recipeid=23434
SELECT * FROM methods WHERE recipeid=23434
SELECT * FROM keywords WHERE recipeid=23434
Quando você souber o ID do documento, obter o conteúdo de todo o documento é uma única consulta ao Couchbase Server. Por exemplo, se você obtiver o ID do documento Aromaticroastchicken da nossa visualização "por ingrediente", poderei obter a receita inteira para exibição com uma única solicitação GET usando a biblioteca do cliente. Por exemplo, em PHP:
$recipe = couchbase->get('Aromaticroastchicken');

O resultado são os dados da receita como JSON:

{
"title" : "Aromatic roast chicken" (Frango assado aromático),
"preptime" : "15",
"porções" : "6",
"totaltime" : "120",
"subtitle" : "Um recheio de cuscuz e temperos aromáticos com limão perfumado e alho assado transformam um simples frango assado em algo especial",
"cooktime" : "105",
"palavras-chave" : [
"diet@dairy-free",
"cook method.hob, oven, grill@oven",
"diet@peanut-free",
"coleções especiais@cheffy recomendado",
"diet@corn-free",
"coleções especiais@refeição de fim de semana",
"occasion@entertaining",
"coleções especiais@muito fácil",
"diet@yeast-free",
"diet@shellfish-free",
"coleções especiais@feeling spicy!",
"main ingredient@poultry" (ingrediente principal@aves),
"diet@demi-veg",
"meal type@main",
"diet@egg-free",
"diet@cow sem laticínios"
],
"ingredientes" : [

],
"method" : [

],

}

Uma consulta, uma solicitação ao banco de dados para carregar tudo o que é necessário para exibir a receita em uma única página. Outros conteúdos poderiam ser incluídos aqui, é claro, comentários de usuários, metadados adicionais, todos armazenados em um único documento e a uma única solicitação de distância.

Migração dos dados

A formatação dos dados traz à tona outro ponto importante ao pensar nessa migração quando iniciei o processo. Pense em seus dados e em como eles serão usados. Se você exibe informações que, no MySQL, vêm de várias fontes, faz sentido agrupar essas informações e armazená-las no Couchbase Sever como um único documento.  

Como já foi mencionado, muitos mecanismos de aceleração usados com o MySQL funcionam com base no armazenamento em cache das informações depois de terem sido carregadas de várias fontes para acelerar o carregamento de estruturas e informações tão complexas.

O mesmo princípio básico é verdadeiro com o Couchbase Server, levando em conta as ressalvas que mencionei acima sobre a estrutura. Você precisa ter certeza de que a estrutura criada permite a construção de uma visualização que torne o conteúdo pesquisável, se esses dados forem a forma como você deseja extrair os documentos do banco de dados.

Por exemplo, em nossa estrutura de receitas, não faz sentido armazenar os ingredientes em um único campo de texto e perder os detalhes do conteúdo. Isso é semelhante a colocar tudo em uma única coluna de texto no MySQL e tem as mesmas armadilhas: dificulta a pesquisa e a consulta do conteúdo e complica as possibilidades de visualização e exibição.

O processo de migração real deve ser bastante simples. Carregue as informações do MySQL, crie um documento para cada objeto principal em seu banco de dados e, em seguida, escreva os documentos em um servidor Couchbase. Na verdade, você pode até usar esse método para migrar as informações em um aplicativo em execução. Em vez disso, tente carregar o item do Couchbase e, se ele não existir, carregue-o do MySQL e, em seguida, armazene o objeto estruturado no Couchbase. 

Os documentos no Couchbase são armazenados por seu ID. O ID pode ser qualquer cadeia de caracteres que você desejar, o que significa que você pode usar algo identificável para você ou para o seu aplicativo sem ter que depender sempre de uma visualização para encontrá-lo. Para usar nosso banco de dados de receitas sempre presente, você poderia usar o título da receita, por exemplo, "Fish Stew" (ensopado de peixe). A limitação dessa abordagem (assim como no MySQL e nos índices exclusivos) é que você não pode armazenar dois documentos com o mesmo ID, e há muitas receitas diferentes de ensopado de peixe por aí. Como alternativa, você poderia usar um UUID ou usar o título da receita com um sufixo para permitir várias receitas "Fish Stew".

Assim como na opção de campo MySQL AUTO_INCREMENT, o Couchbase gerará automaticamente um UUID para cada documento. Isso não é obrigatório; você pode armazenar tanto documentos com um ID de documento específico quanto IDs gerados automaticamente no mesmo banco de dados, o que significa que você pode usar IDs conhecidos para armazenar informações específicas e IDs gerados automaticamente para o corpo principal de dados.

Concluindo

A principal diferença entre o Couchbase e o MySQL é a representação dos dados e como você os acessa. Para determinados modelos de dados, o Couchbase oferece uma solução mais simples para o problema de carregar os dados no estilo de objeto único que vimos demonstrados nestes artigos. As receitas são um bom exemplo em que o elemento principal do armazenamento de dados é a receita como um todo (nosso documento). A capacidade de pesquisar e localizar dados de receitas e apresentá-los como uma lista é um requisito em termos da forma como precisamos apresentar e acessar os dados, não um requisito relacionado à forma como eles são armazenados.

Esperamos que essas duas publicações tenham lhe dado algumas ideias sobre como seus dados podem ser movidos do MySQL para o Couchase Server e como modelar e desenvolver os dados do Couchbase Server e o código do aplicativo para fazer com que o sistema funcione e opere como sua infraestrutura existente baseada no MySQL.

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

#next_pages_container { width: 5px; hight: 5px; position: absolute; top: -100px; left: -100px; z-index: 2147483647 !important; }

Autor

Postado por MC Brown

Mc Brown é vice-presidente de publicações técnicas e educação da Couchbase. Responsável pela criação da documentação e das informações de suporte.

5 Comentários

  1. Este é um ótimo artigo. Então, considerando a simplicidade do mapeamento, por que vocês não podem fornecer uma linguagem de consulta SQL ou \"SQL like\" sobre o Couchbase? Quero dizer, a Sun conseguiu fazer isso usando JPQL em cima do provedor de persistência deles :-)

    1. Olá, Frank,

      Hoje, nossa API de consulta é a exibição, e ela expõe um conjunto muito simples de métodos para criar consultas, definir alguns critérios e classificar os resultados. Isso se baseia em uma API REST oculta. É isso que você vê nesta postagem do blog. Essa API aproveita o conceito de visualizações, indexadas e criadas usando a função MapReduce no Couchbase. Também é importante observar que a consulta ao Couchbase é praticamente a mesma em todos os idiomas.

      Quanto ao seu segundo ponto, você fala sobre o JPQL, que não é diretamente da Sun, mas do JCP - portanto, de todo o setor Java -, o Couchbase trabalhou em um esforço de padronização em torno de uma linguagem de consulta para banco de dados NoSQL: http://www.unqlspec.org/

      A equipe de P&D do Couchbase está trabalhando ativamente para fornecer mais opções de consulta e API para versões futuras.

  2. Esta série foi ótima para a leitura de dados, mas que tal uma ou duas postagens sobre a gravação de dados? Por exemplo: atualizar um único elemento em uma matriz em um documento.

    1. Steve,

      As APIs de gravação do Couchbase são bastante simples no momento, usando as operações set ou delete. Para atualizar um valor de atributo, você precisa definir o documento. As atualizações em nível de subdocumento são algo que gostaríamos de suportar no futuro. Também estamos investigando uma linguagem de consulta que facilitará a consulta e a manipulação de dados. Provavelmente, primeiro consultaremos os dados e depois os manipularemos.

      Se você tiver sugestões sobre as cláusulas obrigatórias que devemos apoiar, informe-nos. Gostaríamos muito de ouvir seu feedback.

  3. Você realmente me ajudou :-) Obrigado!

Deixar uma resposta