Em nossa série de treinamentos contínuos, várias perguntas surgem a cada vez, e eu as relaciono com suas respectivas respostas abaixo!
Couchbase 103 - Modelagem de documentos
P: Supondo que você possa armazenar um documento como XML em vez de JSON, o que você acha de um em relação ao outro para o Couchbase?
R: Você pode armazenar XML, assim como podemos armazenar cadeias de caracteres. No entanto, é difícil fazer exibições Map-Reduce para indexá-las e consultá-las, pois não analisamos o XML no lado do servidor para poder usar a notação de ponto como no JSON. O XML não é nativo do javascript. A extensão E4X para analisar XML não faz parte do mecanismo V8 que usamos no Couchbase para processar as funções de mapa javascript.
P: Podemos fazer inserções/atualizações em massa no Couchbase?
R: Normalmente, você faz isso da mesma forma que faz qualquer operação CRUD com o Couchbase, usando os SDKs. Temos uma ferramenta de linha de comando que pode ler arquivos JSON do disco, cbtransfer. Recomendo o uso de uma VM rápida (por exemplo, Java/C/C++/Go em vez de Ruby) e a paralelização do trabalho se houver uma grande quantidade de documentos.
P: Você não está voltando ao padrão "relacional" quando usa o padrão de vários documentos?
R: Não necessariamente, é mais uma separação de preocupações, mas ainda não há junções. Trata-se apenas de isolar os comportamentos do usuário em documentos separados, não de normalização. Há uma grande diferença entre os dois. Em vez de ter um documento para muitos comportamentos de usuário, você está isolando os documentos por chave para comportamentos de usuário específicos e ainda armazenando dados desnormalizados. O que estamos conseguindo com o uso de JSON em vez de tabelas e de dados desnormalizados em vez de normalizados é a capacidade de distribuir dados. Quando você tem tabelas normalizadas, a distribuição de dados e a computação de junções são muito desafiadoras. Você não pode dividir facilmente tabelas grandes em vários servidores e, quando o faz, a computação de junções exige a agregação de tabelas grandes em vários servidores, o que será pesado em termos de computação e transferência de dados e não poderá sustentar muitas consultas. É claro que a lógica das operações CRUD em uma situação de RDBMS distribuído é igualmente complexa.
P: Com base em sua experiência, qual é a melhor maneira de garantir, tanto quanto possível, que um item seja referido com o mesmo nome em todo o banco de dados?
R: Em primeiro lugar, escolha características imutáveis para as chaves, de modo que os IDs referenciais dentro do documento JSON não precisem ser atualizados. Se você precisar alterar uma chave que tenha sido referenciada em outros documentos, provavelmente terá de criar uma visualização para localizar e atualizar esses documentos após a alteração da chave.
P: Você observou que uma das vantagens do CB é a linha reta entre o aplicativo e os dados, pois eles são armazenados no banco de dados, ou seja, não há incompatibilidade de impedância. Wque se o aplicativo mudar e dados diferentes forem usados em páginas diferentes, ou (ainda pior) quando vários aplicativos estiverem analisando os mesmos dados. Então, a abordagem RDBMS não é melhor? ou você ainda a defenderia? Em caso afirmativo, por quê?
R: Se partes diferentes de um documento JSON forem usadas em páginas diferentes, isso não será um problema. Se diferentes comportamentos de usuário modificarem o mesmo documento e houver uma oportunidade para condições de corrida, você poderá usar operações CAS ou separar os documentos em vários. Se vários aplicativos estiverem analisando os mesmos dados, isso também não será um problema. Se esses aplicativos forem de terceiros e puderem fazer gravações nos dados, é melhor que eles passem por uma API para que a formatação das chaves JSON e outras validações possam ser feitas para manter a formatação e a integridade dos dados. Nos sistemas RDBMS, você depende do banco de dados para fazer a validação de tipo; essa responsabilidade passa do banco de dados para a camada de aplicativos com o Couchbase. Isso é resolvido com bastante facilidade e, de qualquer forma, é uma prática melhor fazer com que os aplicativos de terceiros passem por uma API em vez de irem diretamente para o banco de dados. Mesmo no caso de vários aplicativos interagindo com o mesmo banco de dados, o uso de SOA também pode ser uma arquitetura melhor.
P: Como você "encontra" o tamanho (em termos de bytes no disco, por exemplo) de um determinado documento?
R: Não temos uma maneira de fazer isso para documentos individuais. Os documentos são compactados em disco, usando o Snappy, portanto, você pode obter uma taxa de compactação média do tamanho do documento do lado do cliente (json bruto não compactado) e, em seguida, obter uma compactação arbitrária de 40% para ver o espaço ocupado em disco. Ou você pode fazer uma média observando o número de itens (documentos), pegando o uso do disco e dividindo-o para ver o tamanho médio do documento no disco.
P: Como "atualizar" o(s) item(ns) json, por exemplo, o carimbo de data/hora da atualização do documento, usando qualquer forma, mas não manualmente?
R: Não há mecanismo automático para atualizar chaves json individuais em um documento json. Documentos inteiros são transmitidos para frente e para trás com operações CRUD. Se você estiver atualizando qualquer chave json com um novo valor, isso será feito no lado do cliente, incluindo, entre outros, a atualização de um carimbo de data/hora.
P: Como você lida com as atualizações de dados, isso não consumirá muito tempo?
R: Se você estiver se referindo apenas à atualização do documento JSON, é claro que a operação de substituição em sub-milissegundos não é o que você está falando. Portanto, tive de presumir, para responder a essa pergunta, que você está se referindo à migração de uma estrutura de documento JSON para outra, como um documento de usuário que tem algumas chaves JSON e que você deseja adicionar/remover/transformar. Como mencionei no webinar, você pode fazer isso de várias maneiras. É claro que ele assume duas formas diferentes: uma delas é o estilo de trabalho em lote/cron, em que você itera pelos documentos e faz a transformação/mutação. O tempo que isso leva depende do tamanho do seu conjunto de dados, de quantas máquinas estão no cluster do Couchbase e de quantas máquinas estão fazendo as transformações. Portanto, "demorado" é uma avaliação relativa. A outra maneira de lidar com isso seria transformar os documentos "sob demanda", ou seja, quando eles são recuperados pelo uso normal do seu aplicativo. Por exemplo, quando um usuário faz um login, você pode recuperar o documento do usuário, transformá-lo e substituí-lo no Couchbase.
P: O que Jasdeep não mencionou é que os dados precisam residir na memória (pelo menos para justificar a velocidade). Então, qual é a vantagem sobre um banco de dados relacional na memória que suporta SQL avançado? Eles também fazem um bom clustering ...
R: Se você estiver fazendo um único nó em escala vertical de um banco de dados relacional na memória, o limite será a escala vertical dessa única máquina. Quando você tiver mais de um servidor e junções (e particularmente junções distribuídas), encontrará gargalos de desempenho semelhantes aos ancestrais vinculados a disco, puramente por causa da física. Se você fizer uma união por meio de uma conexão de rede em muitas tabelas grandes, não importa como você a faça, ela será lenta e, se você a fizer com frequência, todo o desempenho será degradado em escala.
P: Em um padrão Counter-ID para usuários, onde residirá o valor da contagem de usuários?
R: É um valor inteiro positivo simples com uma chave, como "user::count". Certifique-se de incrementar apenas o contador! E siga-o com uma operação de adição em vez de um conjunto, dessa forma, se houver um erro de lógica, você o detectará.
P: Como você faria um MapReduce de índice reverso (invertido)?
R: Esse assunto está mais no tópico do webinar Couchbase 104: Views. Mas os índices reversos são complicados com o Map-Reduce, você pode criá-los emitindo a tag para quando ela ocorrer e, em seguida, fazer uma consulta de intervalo para essa tag.
P: Quando você divide documentos em vários documentos?
R: Em geral, há três motivos para pensar em dividir um documento em vários documentos. O primeiro é que uma determinada ação do usuário pode modificar um documento que também pode ser modificado por outra ação do usuário (e ambas são frequentes). Se vários usuários puderem modificar o mesmo documento em um determinado momento (ou quase provavelmente simultaneamente), isso é uma "condição de corrida" e você deve isolar esse documento e usar operações CAS. O segundo caso é quando um único documento pode ser "inchado" ou muito grande e, nesse caso, pode ser mais eficiente dividi-lo em termos de código e manutenção de código. Em terceiro lugar, se você estiver usando um contador atômico para representar um componente específico de um objeto/classe. Isso terá que ser seu próprio par chave-valor, independente do documento JSON primário, é claro.
P: Como você faria uma pesquisa se tivesse um ID de navio e Lat/Long e quisesse pesquisar por qualquer um dos parâmetros?
R: Nesse caso, como as coordenadas geográficas têm precisão de três casas decimais, nenhum padrão de chave o ajudará com isso. No entanto, temos duas opções para fazer buscas do tipo de informação que você deseja. Nossas visualizações gerais Map/Reduce são mais do que satisfatórias, mas também temos visualizações especializadas com Geo. Elas permitem a pesquisa em caixas delimitadas e outras técnicas geográficas mais avançadas. Não falo especificamente sobre isso no webinar Couchbase 104: Views, mas entre em contato comigo se estiver interessado em saber mais ou se precisar de ajuda com relação ao Geo e ao Couchbase.
P: Contar os usuários é uma maneira melhor (com a função .incr()) de criar o ID do usuário do que, por exemplo, um grande número aleatório ou um GUID? Estou falando do caso em que pode haver muitos novos usuários por segundo. A contagem não causará mais conflitos do que uma grande chave aleatória que tem uma probabilidade muito baixa de conflito?
R: Como os contadores atômicos são, bem, atômicos, então você pode contar com eles (em um único cluster) para serem executados em ordem. Se todos os seus servidores de aplicativos estiverem apontando para esse cluster, todos eles estarão executando operações incr simultaneamente. Mas como elas são atômicas, cada operação de cada servidor de aplicativos executando operações gerará um novo número inteiro, portanto, todas serão exclusivas. Se você nunca usar uma operação decr (diminuir), não terá problemas. A situação fica um pouco mais complexa se você tiver vários data centers e estiver usando XDCR (Cross Data Center Replication). Nesses casos, é melhor prefixar seus contadores pelo ID do data center (que você criou).
P: Estou usando a gem couchbase-model no Rails e estou tendo dificuldades para entender o Counter-ID e o namespacing das chaves no paradigma do Rails, por exemplo, usando o método Object.find() ou acessando/incrementando refdocs não-JSON. Você poderia postar um exemplo que misture e combine o Counter-ID com o Rails, incluindo o incremento a partir do bucket do Model?
R: Isso precisará de algo melhor do que um blog para ser mostrado. Vou pensar em uma maneira de montar um resumo e postar um link aqui. Mas me dê um pouco de tempo para fazer isso. :)
P: Existe uma maneira de obter vários documentos para diferentes ids. O exemplo em que estou pensando é uma lista de IDs de usuários do Facebook que queremos mapear para seus documentos de usuário. Existe uma maneira de passar vários IDs do Facebook e receber de volta uma matriz de documentos?
R: Temos uma operação multi-get em cada sdk apenas para essa finalidade. Na matriz resultante, se o documento não existir, ele terá um elemento null/nill para indicar isso (talvez o Java tenha um retorno diferente, terei de verificar, mas sei que ele também tem).
P: As visualizações são a única maneira de usar chaves compostas? Existe uma maneira de usá-las com chaves get ou setup para usar esse tipo de funcionalidade sem uma visualização? https://www.couchbase.com/blog/understanding-grouplevel-view-queries-compound-keys
R: Esse é mais um tópico para o Couchbase 104: Visualizações e indexação, mas basicamente uma chave pode ser o que você quiser, portanto, tecnicamente, sim, você pode ter uma chave composta (depende apenas de como você define composta). No entanto, o link ao qual você está se referindo está focado na consulta de um índice de visualização, e esses também têm chaves, chaves de índice, com as quais construímos nossas árvores B+. Nesse caso, você está pegando partes da chave e dividindo-as com base no delimitador "," (vírgula). Estamos falando de um assunto diferente.
P: No momento, estamos usando uma exibição para pesquisar o ID de usuário do Facebook para obter nosso próprio ID de usuário do aplicativo. Parece que, se realmente usarmos o padrão de pesquisa que você sugeriu, isso pode ser mais rápido do que usar uma visualização?
R: Sim, e é claro que isso depende. A escala também é um fator aqui. Quanto mais nós houver no cluster, mais amplo será o processo de coleta e dispersão da consulta do View, ou seja, mais nós terão de ser dispersos e coletados em termos de resultados da consulta do View. Portanto, apenas pela física, a pesquisa será mais rápida. A desvantagem é que você está adicionando mais documentos e, portanto, mais requisitos de RAM/metadados, mas, como resultado, está ganhando uma tremenda escalabilidade, porque as operações binárias passam por uma conexão persistente com um servidor para o facebook_id->app_user_id e, novamente, uma segunda operação binária para o mesmo ou outro nó por meio de uma conexão persistente sempre será mais rápida do que espalhar uma consulta View por todos os nós e coletar os resultados. As visualizações são muito poderosas porque são mais flexíveis do que os padrões de chave; entretanto, nesse cenário, opte pela velocidade.
P: As exibições têm uma opção obsoleta que parece poder atenuar o problema de constância em comparação com duas chamadas get separadas. É realmente mais rápido ter duas chamadas para o banco de dados do que uma visualização bem gerenciada? Eu poderia me preocupar com a escala, duas chamadas em vez de uma em uma visualização.
R: Isso não atenua o problema de consistência. Os índices são sempre consistentes no Couchbase (até que nós mesmos os desenvolvamos, o que está no radar), mas veja a pergunta/resposta acima e assista a Couchbase 104: Views and Indexes! Isso pode mudar sua opinião.
P: No exemplo que você está demonstrando, estamos discutindo sobre um único documento e seus atributos/propriedades via JSON. Como lidamos com o fato de você querer obter estatísticas/contagem em vários objetos/documentos? Por exemplo: a) me traga todos os usuários com a cor favorita azul? b) me informar todos os usuários que usam cartão de crédito Visa?
R: Isso requer visualizações e a técnica Map/Reduce para reunir todos esses elementos de dados de documentos e colocá-los em uma estrutura de índice para que você possa fazer consultas como essas. Esse é um bom exemplo de coisas que você não pode realmente fazer com padrões-chave e requer integração com VIews e/ou Elastic Search. Nesses cenários de que você está falando, você poderia usar qualquer uma das soluções.
P: *Require* views/map-reduce? Que tal armazenar o segundo documento u::uuid { email:user@domain.com } para depois acessar u:user@domain.com? Quando isso deve ser feito por meio de view/map-reduce?
R: Essa pergunta estava se referindo ao que acontece sem um padrão Lookup. Se você tiver chaves de documento geradas aleatoriamente, normalmente precisará criar um índice para poder pesquisar documentos por meio de diferentes propriedades JSON. No entanto, se você criar um padrão Lookup, poderá fazer uma busca dupla para obter o documento principal; a primeira busca será baseada em informações conhecidas (endereço de e-mail), o valor será o ID gerado aleatoriamente que está associado ao documento principal; a segunda busca usará esse valor e obterá o documento principal.
P: Qual é a melhor maneira de consultar esses documentos de notificação?
R: No exemplo que eu estava mencionando, as próprias chaves são registros de data e hora para quando as notificações devem ser entregues. Aqui está um resumo que pode ajudar a explicar o paradigma: https://gist.github.com/scalabl3/7235173
P: O principal objetivo da normalização é evitar anomalias de atualização; como todos os programas têm bugs, existe uma maneira no couchbase de evitar essas anomalias de atualização e não ter que depender do desenvolvedor "acertar"?
R: O principal objetivo da normalização era economizar espaço em disco. Se você olhar para trás, na época em que o SQL foi criado, obter cerca de 1 GB de armazenamento em disco (o que não era possível em um único disco) era cerca de $700.000. A normalização reduziu a redundância de dados ao apontar para os mesmos dados de vários lugares e, em seguida, remontar esses dados agregando-os ou juntando-os. Na prática, ficou claro muito rapidamente que, se você estiver inserindo, atualizando e excluindo dados em um esquema normalizado, se alguma parte falhar, a integridade dos dados vai por água abaixo. É justamente por causa da normalização que as transações são necessárias! Em uma forma desnormalizada ou agregada, as transações se tornam muito menos necessárias, mas temos simultaneidade otimista e simultaneidade pessimista para ter transações de documento único e você pode usar um commit de duas fases.
Realmente não há proteção completa em nenhum sistema para os desenvolvedores que "erram". Avise-me se você descobrir isso :).
P: Não seria vantajoso combinar o padrão Counter-ID com o padrão Lookup?
R: Sem dúvida, é vantajoso, porque assim você também tem uma contagem total de documentos para esse tipo. Se você usar a identificação aleatória e a pesquisa, não terá a contagem total. Com a contagem total, você pode iterar pela coleção apenas gerando chaves em lotes até a contagem total. Caso contrário, você precisará criar uma visualização (um índice) para iterar pela coleção.
P: Um índice para o RDBMS também não lhe proporcionaria um desempenho linear?
R: O desempenho é excelente, desde que você possa viver em um único servidor e dimensioná-lo verticalmente. A bagunça acontece quando você precisa dividir o tráfego em mais de um servidor. É nesse ponto que o nascimento de todos os novos bancos de dados que explodiram em cena pode ser rastreado no início dos anos 2000, quando o público "explodiu" a capacidade dos bancos de dados relacionais de lidar com grandes quantidades de tráfego. Coisas como o memcached vieram primeiro, e depois os NoSQLs.
P: Por que não estender o CB com funcionalidades abstratas baseadas nesses padrões, no lado do servidor? No momento, os padrões precisam ser implementados individualmente no lado do cliente.
R: É uma ideia e, na verdade, acho que há alguma validade em fazê-la, mas também há um bom argumento do outro lado de que ela deve estar sob controle programático. Assim que você adiciona um novo recurso, as pessoas querem mexer nele :)
Obrigado por participar do Couchbase 103!
Jasdeep
@escalável
[...] Postagem do blog da semana #1: Couchbase 103: Perguntas e respostas [...]