paint-brush
Quando usar índices secundários do DynamoDBpor@rocksetcloud
4,540 leituras
4,540 leituras

Quando usar índices secundários do DynamoDB

por Rockset16m2024/05/23
Read on Terminal Reader

Muito longo; Para ler

Os índices secundários do DynamoDB são uma ferramenta poderosa para permitir novos padrões de acesso aos seus dados.
featured image - Quando usar índices secundários do DynamoDB
Rockset HackerNoon profile picture

Os índices são uma parte crucial da modelagem de dados adequada para todos os bancos de dados, e o DynamoDB não é exceção. Os índices secundários do DynamoDB são uma ferramenta poderosa para permitir novos padrões de acesso aos seus dados.


Nesta postagem, veremos os índices secundários do DynamoDB . Primeiro, começaremos com alguns pontos conceituais sobre como pensar sobre o DynamoDB e os problemas que os índices secundários resolvem. A seguir, veremos algumas dicas práticas para usar índices secundários de maneira eficaz. Por fim, encerraremos com algumas reflexões sobre quando você deve usar índices secundários e quando procurar outras soluções.


Vamos começar.

O que é DynamoDB e quais são os índices secundários do DynamoDB?

Antes de entrarmos nos casos de uso e nas práticas recomendadas para índices secundários, devemos primeiro entender o que são os índices secundários do DynamoDB . E para isso, devemos entender um pouco como funciona o DynamoDB.


Isso pressupõe algum conhecimento básico do DynamoDB. Abordaremos os pontos básicos que você precisa saber para entender os índices secundários, mas se você for novo no DynamoDB, talvez queira começar com uma introdução mais básica.

O mínimo que você precisa saber sobre o DynamoDB

DynamoDB é um banco de dados exclusivo. Ele foi projetado para cargas de trabalho OLTP, o que significa que é ótimo para lidar com um grande volume de pequenas operações – pense em coisas como adicionar um item a um carrinho de compras, curtir um vídeo ou adicionar um comentário no Reddit. Dessa forma, ele pode lidar com aplicativos semelhantes a outros bancos de dados que você possa ter usado, como MySQL, PostgreSQL, MongoDB ou Cassandra.


A principal promessa do DynamoDB é a garantia de desempenho consistente em qualquer escala . Quer sua tabela tenha 1 megabyte de dados ou 1 petabyte de dados, o DynamoDB deseja ter a mesma latência para suas solicitações do tipo OLTP. Isso é importante: muitos bancos de dados terão desempenho reduzido à medida que você aumenta a quantidade de dados ou o número de solicitações simultâneas. No entanto, fornecer essas garantias exige algumas compensações, e o DynamoDB possui algumas características exclusivas que você precisa entender para usá-lo de maneira eficaz.


Primeiro, o DynamoDB dimensiona horizontalmente seus bancos de dados, espalhando seus dados por várias partições ocultas. Essas partições não são visíveis para você como usuário, mas são a base do funcionamento do DynamoDB. Você especificará uma chave primária para sua tabela (um único elemento, chamado de 'chave de partição', ou uma combinação de uma chave de partição e uma chave de classificação), e o DynamoDB usará essa chave primária para determinar em qual partição seus dados residem. . Qualquer solicitação que você fizer passará por um roteador de solicitação que determinará qual partição deverá tratar a solicitação. Essas partições são pequenas – geralmente 10 GB ou menos – portanto podem ser movidas, divididas, replicadas e gerenciadas de forma independente.




A escalabilidade horizontal por meio de fragmentação é interessante, mas não é exclusiva do DynamoDB. Muitos outros bancos de dados – tanto relacionais quanto não relacionais – usam fragmentação para escalar horizontalmente. No entanto, o que é exclusivo do DynamoDB é como ele força você a usar sua chave primária para acessar seus dados. Em vez de usar um planejador de consultas que traduz suas solicitações em uma série de consultas, o DynamoDB força você a usar sua chave primária para acessar seus dados. Basicamente, você está obtendo um índice endereçável diretamente para seus dados.


A API do DynamoDB reflete isso. Há uma série de operações em itens individuais ( GetItem , PutItem , UpdateItem , DeleteItem ) que permitem ler, gravar e excluir itens individuais. Além disso, há uma operação Query que permite recuperar vários itens com a mesma chave de partição. Se você tiver uma tabela com uma chave primária composta, os itens com a mesma chave de partição serão agrupados na mesma partição. Eles serão ordenados de acordo com a chave de classificação, permitindo que você lide com padrões como "Buscar os pedidos mais recentes para um usuário" ou "Buscar as últimas 10 leituras de sensores para um dispositivo IoT".


Por exemplo, vamos imaginar uma aplicação SaaS que possui uma tabela de Usuários. Todos os usuários pertencem a uma única organização. Poderíamos ter uma tabela parecida com a seguinte:



Estamos usando uma chave primária composta com uma chave de partição 'Organização' e uma chave de classificação 'Nome de usuário'. Isso nos permite realizar operações para buscar ou atualizar um usuário individual, fornecendo sua organização e nome de usuário. Também podemos buscar todos os usuários de uma única organização, fornecendo apenas a organização para uma operação Query .

O que são índices secundários e como funcionam

Com alguns princípios básicos em mente, vamos agora examinar os índices secundários. A melhor maneira de compreender a necessidade de índices secundários é compreender o problema que eles resolvem. Vimos como o DynamoDB particiona seus dados de acordo com sua chave primária e como ele incentiva você a usar a chave primária para acessar seus dados. Isso é muito bom para alguns padrões de acesso, mas e se você precisar acessar seus dados de uma maneira diferente?


Em nosso exemplo acima, tínhamos uma tabela de usuários que acessamos por organização e nome de usuário. No entanto, também podemos precisar buscar um único usuário pelo endereço de e-mail. Esse padrão não se ajusta ao padrão de acesso de chave primária que o DynamoDB nos incentiva. Como nossa tabela é particionada por atributos diferentes, não há uma maneira clara de acessar nossos dados da maneira que desejamos. Poderíamos fazer uma varredura completa da tabela, mas isso é lento e ineficiente. Poderíamos duplicar nossos dados em uma tabela separada com uma chave primária diferente, mas isso acrescenta complexidade.


É aqui que entram os índices secundários. Um índice secundário é basicamente uma cópia totalmente gerenciada de seus dados com uma chave primária diferente. Você especificará um índice secundário em sua tabela declarando a chave primária do índice. À medida que as gravações chegam à sua tabela, o DynamoDB replica automaticamente os dados para o seu índice secundário.


Nota *: Tudo nesta seção se aplica a índices secundários globais . O DynamoDB também fornece índices secundários locais , que são um pouco diferentes. Em quase todos os casos, você desejará um índice secundário global. Para obter mais detalhes sobre as diferenças, consulte este artigo sobre como escolher um índice secundário global ou local .*


Neste caso, adicionaremos um índice secundário à nossa tabela com uma chave de partição “Email”. O índice secundário terá a seguinte aparência:



Observe que estes são os mesmos dados, apenas foram reorganizados com uma chave primária diferente. Agora, podemos procurar um usuário com eficiência pelo endereço de e-mail.


De certa forma, isso é muito semelhante a um índice em outros bancos de dados. Ambos fornecem uma estrutura de dados otimizada para pesquisas em um atributo específico. Mas os índices secundários do DynamoDB são diferentes em alguns aspectos importantes.


Primeiro, e mais importante, os índices do DynamoDB residem em partições totalmente diferentes da sua tabela principal. O DynamoDB deseja que cada pesquisa seja eficiente e previsível e deseja fornecer escalonamento horizontal linear. Para fazer isso, ele precisa reestilhaçar seus dados pelos atributos que você usará para consultá-los.



Em outros bancos de dados distribuídos, eles geralmente não reestilhaçam seus dados para o índice secundário. Geralmente, eles apenas manterão o índice secundário para todos os dados no fragmento. No entanto, se seus índices não usarem a chave de fragmento, você perderá alguns dos benefícios do dimensionamento horizontal de seus dados, pois uma consulta sem a chave de fragmento precisará realizar uma operação de coleta de dispersão em todos os fragmentos para encontrar os dados que você deseja. estou procurando.


Uma segunda maneira pela qual os índices secundários do DynamoDB são diferentes é que eles (frequentemente) copiam o item inteiro para o índice secundário. Para índices em um banco de dados relacional, o índice geralmente conterá um ponteiro para a chave primária do item que está sendo indexado. Depois de localizar um registro relevante no índice, o banco de dados precisará buscar o item completo. Como os índices secundários do DynamoDB estão em nós diferentes da tabela principal, eles desejam evitar um salto de rede de volta ao item original. Em vez disso, você copiará quantos dados precisar no índice secundário para lidar com sua leitura.


Os índices secundários no DynamoDB são poderosos, mas têm algumas limitações. Primeiro, eles são somente leitura – você não pode escrever diretamente em um índice secundário. Em vez disso, você gravará na tabela principal e o DynamoDB cuidará da replicação no índice secundário. Segundo, você é cobrado pelas operações de gravação em seus índices secundários. Assim, adicionar um índice secundário à sua tabela geralmente dobrará os custos totais de gravação da sua tabela.

Dicas para usar índices secundários

Agora que entendemos o que são índices secundários e como funcionam, vamos falar sobre como usá-los de maneira eficaz. Os índices secundários são uma ferramenta poderosa, mas podem ser mal utilizados. Aqui estão algumas dicas para usar índices secundários de maneira eficaz.

Tente ter padrões somente leitura em índices secundários

A primeira dica parece óbvia: índices secundários só podem ser usados para leituras, então você deve tentar ter padrões somente leitura em seus índices secundários! E ainda assim, vejo esse erro o tempo todo. Os desenvolvedores primeiro lerão um índice secundário e depois gravarão na tabela principal. Isso resulta em custo extra e latência extra, e muitas vezes você pode evitá-lo com algum planejamento antecipado.


Se você leu alguma coisa sobre modelagem de dados do DynamoDB, provavelmente sabe que deve pensar primeiro em seus padrões de acesso. Não é como um banco de dados relacional onde você primeiro projeta tabelas normalizadas e depois escreve consultas para juntá-las. No DynamoDB, você deve pensar nas ações que seu aplicativo realizará e, em seguida, projetar suas tabelas e índices para dar suporte a essas ações.


Ao projetar minha tabela, gosto de começar primeiro com os padrões de acesso baseados em gravação. Com minhas gravações, muitas vezes mantenho algum tipo de restrição – exclusividade em um nome de usuário ou um número máximo de membros em um grupo. Quero projetar minha tabela de uma forma que torne isso simples, de preferência sem usar transações do DynamoDB ou usar um padrão de leitura-modificação-gravação que pode estar sujeito a condições de corrida.


Ao trabalhar com isso, você geralmente descobrirá que existe uma maneira “primária” de identificar seu item que corresponde aos seus padrões de escrita. Esta acabará sendo sua chave primária. Então, adicionar padrões de leitura secundários adicionais é fácil com índices secundários.


Em nosso exemplo anterior de Usuários, cada solicitação de Usuário provavelmente incluirá a Organização e o Nome de usuário. Isso me permitirá consultar o registro individual do usuário, bem como autorizar ações específicas do usuário. A pesquisa de endereço de e-mail pode ser para padrões de acesso menos proeminentes, como um fluxo de 'esqueci a senha' ou um fluxo de 'busca de um usuário'. Esses são padrões somente leitura e se ajustam bem a um índice secundário.

Use índices secundários quando suas chaves forem mutáveis

Uma segunda dica para usar índices secundários é usá-los para valores mutáveis em seus padrões de acesso. Vamos primeiro entender o raciocínio por trás disso e depois examinar as situações em que ele se aplica.


O DynamoDB permite atualizar um item existente com a operação UpdateItem . No entanto, você não pode alterar a chave primária de um item em uma atualização . A chave primária é o identificador exclusivo de um item, e alterar a chave primária é basicamente criar um novo item. Se quiser alterar a chave primária de um item existente, você precisará excluir o item antigo e criar um novo. Este processo de duas etapas é mais lento e caro. Muitas vezes você precisará ler o item original primeiro e depois usar uma transação para excluir o item original e criar um novo na mesma solicitação.


Por outro lado, se você tiver esse valor mutável na chave primária de um índice secundário, o DynamoDB tratará desse processo de exclusão + criação para você durante a replicação. Você pode emitir uma solicitação simples UpdateItem para alterar o valor e o DynamoDB cuidará do resto.


Vejo esse padrão surgir em duas situações principais. A primeira, e mais comum, é quando você tem um atributo mutável que deseja classificar. Os exemplos canônicos aqui são uma tabela de classificação para um jogo onde as pessoas estão continuamente acumulando pontos ou para uma lista de itens continuamente atualizada onde você deseja exibir primeiro os itens atualizados mais recentemente. Pense em algo como o Google Drive, onde você pode classificar seus arquivos pela “última modificação”.


Um segundo padrão onde isso surge é quando você tem um atributo mutável que deseja filtrar. Aqui, você pode pensar em uma loja de comércio eletrônico com um histórico de pedidos de um usuário. Você pode permitir que o usuário filtre seus pedidos por status - mostre todos os meus pedidos que foram 'enviados' ou 'entregues'. Você pode incorporar isso em sua chave de partição ou no início de sua chave de classificação para permitir a filtragem de correspondência exata. À medida que o item muda de status, você pode atualizar o atributo status e contar com o DynamoDB para agrupar os itens corretamente em seu índice secundário.


Em ambas as situações, mover esse atributo mutável para o seu índice secundário economizará tempo e dinheiro. Você economizará tempo evitando o padrão leitura-modificação-gravação e economizará dinheiro evitando os custos extras de gravação da transação.


Além disso, observe que esse padrão combina bem com a dica anterior. É improvável que você identifique um item para escrever com base no atributo mutável, como pontuação anterior, status anterior ou a última vez que foi atualizado. Em vez disso, você atualizará por um valor mais persistente, como o ID do usuário, o ID do pedido ou o ID do arquivo. Em seguida, você usará o índice secundário para classificar e filtrar com base no atributo mutável.

Evite a partição 'gorda'

Vimos acima que o DynamoDB divide seus dados em partições com base na chave primária. O DynamoDB visa manter essas partições pequenas – 10 GB ou menos – e você deve tentar distribuir solicitações entre suas partições para obter os benefícios da escalabilidade do DynamoDB.


Isso geralmente significa que você deve usar um valor de alta cardinalidade na sua chave de partição. Pense em algo como um nome de usuário, um ID de pedido ou um ID de sensor. Há um grande número de valores para esses atributos, e o DynamoDB pode distribuir o tráfego pelas suas partições.


Muitas vezes vejo pessoas entenderem esse princípio em sua tabela principal, mas depois se esquecem completamente dele em seus índices secundários. Freqüentemente, eles desejam fazer pedidos em toda a mesa para um tipo de item. Se quiserem recuperar os usuários em ordem alfabética, usarão um índice secundário onde todos os usuários têm USERS como chave de partição e o nome de usuário como chave de classificação. Ou, se quiserem ordenar os pedidos mais recentes em uma loja de comércio eletrônico, usarão um índice secundário onde todos os pedidos têm ORDERS como chave de partição e o carimbo de data/hora como chave de classificação.


Esse padrão pode funcionar para aplicativos de tráfego pequeno, nos quais você não chegará perto dos limites de taxa de transferência da partição do DynamoDB , mas é um padrão perigoso para aplicativos de alto tráfego. Todo o seu tráfego pode ser canalizado para uma única partição física e você pode atingir rapidamente os limites de taxa de transferência de gravação dessa partição.


Além disso, e mais perigosamente, isso pode causar problemas à sua tabela principal. Se o seu índice secundário estiver sendo limitado por gravação durante a replicação, a fila de replicação fará backup. Se esta fila fizer backup demais, o DynamoDB começará a rejeitar gravações em sua tabela principal.


Isso foi projetado para ajudá-lo - o DynamoDB deseja limitar a desatualização do seu índice secundário, evitando que você tenha um índice secundário com grande atraso. No entanto, pode ser uma situação surpreendente que surge quando você menos espera.

Use índices esparsos como filtro global

As pessoas costumam pensar nos índices secundários como uma forma de replicar todos os seus dados com uma nova chave primária. No entanto, você não precisa que todos os seus dados acabem em um índice secundário. Se você tiver um item que não corresponda ao esquema de chave do índice, ele não será replicado no índice.


Isso pode ser muito útil para fornecer um filtro global em seus dados. O exemplo canônico que uso para isso é uma caixa de entrada de mensagens. Na sua tabela principal, você pode armazenar todas as mensagens de um determinado usuário ordenadas pela hora em que foram criadas.


Mas se você é como eu, tem muitas mensagens na sua caixa de entrada. Além disso, você pode tratar as mensagens não lidas como uma lista de tarefas, como pequenos lembretes para responder a alguém. Conseqüentemente, normalmente só quero ver as mensagens não lidas na minha caixa de entrada.


Você poderia usar seu índice secundário para fornecer este filtro global onde unread == true . Talvez sua chave de partição de índice secundário seja algo como ${userId}#UNREAD e a chave de classificação seja o carimbo de data/hora da mensagem. Quando você cria a mensagem inicialmente, ela incluirá o valor da chave de partição do índice secundário e, portanto, será replicada para o índice secundário das mensagens não lidas. Posteriormente, quando um usuário ler a mensagem, você poderá alterar o status para READ e excluir o valor da chave de partição do índice secundário. O DynamoDB irá então removê-lo do seu índice secundário.


Eu uso esse truque o tempo todo e é extremamente eficaz. Além disso, um índice esparso economizará dinheiro. Quaisquer atualizações nas mensagens lidas não serão replicadas no índice secundário e você economizará nos custos de gravação.

Limite suas projeções de índice secundário para reduzir o tamanho do índice e/ou gravações

Para nossa última dica, vamos levar um pouco mais longe o ponto anterior. Acabamos de ver que o DynamoDB não incluirá um item em seu índice secundário se o item não tiver os elementos-chave primários do índice. Este truque pode ser usado não apenas para elementos-chave primários, mas também para atributos não-chave nos dados!


Ao criar um índice secundário, você pode especificar quais atributos da tabela principal deseja incluir no índice secundário. Isso é chamado de projeção do índice. Você pode optar por incluir todos os atributos da tabela principal, apenas os atributos de chave primária ou um subconjunto dos atributos.


Embora seja tentador incluir todos os atributos em seu índice secundário, isso pode ser um erro caro. Lembre-se de que cada gravação em sua tabela principal que altere o valor de um atributo projetado será replicada em seu índice secundário. Um único índice secundário com projeção completa duplica efetivamente os custos de gravação da sua tabela. Cada índice secundário adicional aumenta seus custos de gravação em 1/N + 1 , onde N é o número de índices secundários antes do novo.


Além disso, seus custos de gravação são calculados com base no tamanho do seu item. Cada 1 KB de dados gravados em sua tabela usa um WCU. Se você estiver copiando um item de 4 KB para seu índice secundário, pagará os 4 WCUs completos na tabela principal e no índice secundário.


Portanto, há duas maneiras de economizar dinheiro, estreitando as projeções do índice secundário. Primeiro, você pode evitar completamente certas gravações. Se você tiver uma operação de atualização que não afeta nenhum atributo na projeção do índice secundário, o DynamoDB ignorará a gravação no índice secundário. Em segundo lugar, para as gravações que são replicadas no índice secundário, você pode economizar dinheiro reduzindo o tamanho do item replicado.


Este pode ser um equilíbrio difícil de acertar. As projeções do índice secundário não podem ser alteradas após a criação do índice. Se achar que precisa de atributos adicionais em seu índice secundário, você precisará criar um novo índice com a nova projeção e, em seguida, excluir o índice antigo.

Você deve usar um índice secundário?

Agora que exploramos alguns conselhos práticos sobre índices secundários, vamos dar um passo atrás e fazer uma pergunta mais fundamental: você deveria usar um índice secundário?


Como vimos, os índices secundários ajudam você a acessar seus dados de uma maneira diferente. No entanto, isso tem o custo de gravações adicionais. Portanto, minha regra prática para índices secundários é:


Use índices secundários quando os custos reduzidos de leitura superarem os custos aumentados de gravação.


Isso parece óbvio quando você diz isso, mas pode ser contra-intuitivo enquanto você modela. Parece tão fácil dizer “Jogue isso em um índice secundário” sem pensar em outras abordagens.


Para esclarecer isso, vejamos duas situações em que os índices secundários podem não fazer sentido.

Muitos atributos filtráveis em pequenas coleções de itens

Com o DynamoDB, você geralmente deseja que suas chaves primárias façam a filtragem para você. Fico um pouco irritado sempre que uso uma consulta no DynamoDB, mas depois executo minha própria filtragem em meu aplicativo - por que não poderia simplesmente incluir isso na chave primária?


Apesar da minha reação visceral, há algumas situações em que você pode querer ler demais seus dados e depois filtrar seu aplicativo.

O lugar mais comum que você verá isso é quando você deseja fornecer vários filtros diferentes em seus dados para seus usuários, mas o conjunto de dados relevante é limitado.


Pense em um rastreador de treino. Você pode permitir que os usuários filtrem vários atributos, como tipo de treino, intensidade, duração, data e assim por diante. No entanto, o número de treinos que um usuário realiza será gerenciável – mesmo um usuário avançado demorará um pouco para exceder 1.000 treinos. Em vez de colocar índices em todos esses atributos, você pode simplesmente buscar todos os treinos do usuário e depois filtrar em seu aplicativo.


É aqui que recomendo fazer as contas . O DynamoDB facilita o cálculo dessas duas opções e a noção de qual delas funcionará melhor para sua aplicação.

Muitos atributos filtráveis em grandes coleções de itens

Vamos mudar um pouco a nossa situação – e se a nossa coleção de itens for grande? E se estivermos criando um rastreador de treino para uma academia e quisermos permitir que o proprietário da academia filtre todos os atributos mencionados acima para todos os usuários da academia ?


Isto muda a situação. Agora estamos falando de centenas ou até milhares de usuários, cada um com centenas ou milhares de treinos. Não fará sentido ler demais toda a coleção de itens e fazer uma filtragem post-hoc nos resultados.


Mas os índices secundários também não fazem sentido aqui. Os índices secundários são bons para padrões de acesso conhecidos, onde você pode contar com a presença de filtros relevantes. Se quisermos que o proprietário da nossa academia seja capaz de filtrar uma variedade de atributos, todos opcionais, precisaríamos criar um grande número de índices para fazer isso funcionar.


Já falamos sobre as possíveis desvantagens dos planejadores de consultas, mas os planejadores de consultas também têm uma vantagem. Além de permitir consultas mais flexíveis, eles também podem fazer coisas como interseções de índices para observar resultados parciais de vários índices na composição dessas consultas. Você pode fazer a mesma coisa com o DynamoDB, mas isso resultará em muitas idas e vindas com seu aplicativo, além de alguma lógica de aplicativo complexa para descobrir.


Quando tenho esse tipo de problema, geralmente procuro uma ferramenta mais adequada para esse caso de uso. Rockset e Elasticsearch são minhas recomendações aqui para fornecer filtragem flexível, semelhante a um índice secundário, em seu conjunto de dados.

Conclusão

Nesta postagem, aprendemos sobre os índices secundários do DynamoDB. Primeiro, examinamos algumas partes conceituais para entender como o DynamoDB funciona e por que os índices secundários são necessários. Em seguida, revisamos algumas dicas práticas para entender como usar índices secundários de maneira eficaz e aprender suas peculiaridades específicas. Por fim, vimos como pensar sobre índices secundários para ver quando você deveria usar outras abordagens.


Os índices secundários são uma ferramenta poderosa na caixa de ferramentas do DynamoDB, mas não são uma solução mágica. Assim como acontece com toda modelagem de dados do DynamoDB, considere cuidadosamente seus padrões de acesso e conte os custos antes de começar.


Saiba mais sobre como você pode usar o Rockset para filtragem semelhante a índice secundário no blog de Alex DeBrie Filtragem do DynamoDB e consultas de agregação usando SQL no Rockset .