Intermediário8 min de leitura

Relacionamentos muitos-para-muitos em DynamoDB

Um aluno se matricula em vários cursos; um curso comporta muitos alunos. Em SQL você alcança para uma tabela de junção e um JOIN de três tabelas.

DynamoDB não tem junções, então o relacionamento tem que viver nas chaves — e o O truque é armazenar cada borda de inscrição em uma forma que ambos os lados possam Query diretamente.

Este guia aborda os problemas dos alunos ↔ cursos de ponta a ponta: os padrões de acesso, o

padrão que os resolve, um esquema-chave original que você pode copiar e como ler ambas as direções sem nunca examinar a tabela.

Como você modela um relacionamento muitos para muitos em DynamoDB?

DynamoDB não tem junções, então você modela um relacionamento muitos para muitos com opadrão: armazene cada link como seu próprio item de borda codificado por um lado e, em seguida, adicione um GSI invertido que troca as chaves. Uma única borda, escrita uma vez, responde a perguntas de ambas as direções de maneira barata.

  • Armazene cada inscrição como seu próprio item de borda, não como um atributo de lista em qualquer um dos lados.
  • Codifique a borda pelo aluno (PK = STU#…, SK = ENROLL#CRS#…) então um Query retorna a lista completa de cursos de um aluno.
  • Adicione um invertido que troca as funções (GSI1PK = CRS#…) para que a mesma borda também responde "quem está neste curso?".
  • Uma borda, escrita uma vez, é lida de forma barata nos dois sentidos — esse é o jogo inteiro.

Enquadre os padrões de acesso primeiro

A modelagem DynamoDB prioriza o padrão de acesso: você decide as leituras antes de escolher um nome de atributo único. Um relacionamento muitos-para-muitos quase sempre tem dois simétricos lê mais as pesquisas de entidade:

  • Obtenha o perfil de um aluno e liste todos os cursos em que o aluno está matriculado.
  • Obtenha os metadados de um curso e liste todos os alunos matriculados nesse curso.
  • Procure uma única vantagem de matrícula - para atualizar uma nota ou abandonar o curso.

As duas leituras da lista apontam em direções opostas no mesmo conjunto de bordas. Um design ingênuo serve um barato e força um Scan para o outro - o exato arma de fogo coberta em Query vs Scan.

O trabalho é transformar ambas direções em um único Query.

Use o padrão lista de adjacências

A orientação do próprio DynamoDB para relacionamentos é a lista de adjacências: modele cada relacionamento como um item cuja chave de partição é um ponto final e cuja chave de classificação é o outro.

AWS documenta isso no Práticas recomendadas para gerenciar relacionamentos muitos para muitos página do Guia do desenvolvedor DynamoDB.

Por que chaves e não uma segunda tabela? Porque o primitivo DynamoDB fornece um Query contra uma única partição.

Um Query lê um intervalo contíguo de valores de chaves de classificação sob uma chave de partição em um operação faturada - essa é a única "junção" que o mecanismo oferece.

Para obter um relacionamento barato de ambos os lados, você duplica a vantagem: escreva-o uma vez digitado pelo aluno e, em seguida, use um índice secundário para projetar a mesma aresta chaveado pelo curso.

Este é o pensamento de chave sobrecarregada de Design de tabela única, aplicado a um relacionamento de uma hierarquia pai-filho.

A forma consiste em duas vistas empilhadas da mesma aresta — a tabela base digitada pelo aluno, a GSI invertido digitado por curso:

mesma borda, chaves trocadasmesma borda, chaves trocadasInverted GSI1 keyed by courseGSI1PK CRS#math204GSI1SK STU#a91GSI1PK CRS#cs101GSI1SK STU#a91Tabela base chaveada por alunoPK STU#a91SK ENROLL#CRS#math204PK STU#a91SK ENROLL#CRS#cs101

Cada aresta é escrita uma vez na tabela base e projetada no GSI com suas chaves trocado, então um Query contra qualquer partição lê o relacionamento de forma barata.

A linhagem remonta à Amazônia de 2007 Artigo do Dynamo: a chave de partição é a unidade de distribuição e o acesso de chave única é o caminho mais rápido.

Relacionamentos em DynamoDB são um exercício de transformar leituras muitos-para-muitos tão rapidamente caminho.

Trabalhe o exemplo: alunos ↔ cursos

Use uma tabela com chaves genéricas, PK e SK, e codifique o tipo de entidade no valor. A vantagem da inscrição é o cerne disso:

PKSKattributes
STU#a91PROFILEname, year, major
STU#a91ENROLL#CRS#math204 enrolledOn, grade
STU#a91ENROLL#CRS#cs101enrolledOn, grade
CRS#math204METADATAtitle, credits, term
CRS#cs101METADATAtitle, credits, term

Um único Query PK = "STU#a91" retorna o perfil do aluno e cada matrícula em uma leitura. Limite-o com SK begins_with "ENROLL#" para obter apenas as bordas do curso. Isso resolve "listar os cursos de um aluno".

Mas “listar os alunos de um curso” aponta para o outro lado – e a tabela base não pode responder isso, porque o ID do aluno está na chave de partição, não na chave de classificação.

Adicione um índice secundário global invertido que troque as funções. Dê aos itens de borda um par genérico GSI1PK/GSI1SK segurando o curso no lado da partição e o aluno no lado da classificação:

PKSKGSI1PKGSI1SK
STU#a91ENROLL#CRS#math204CRS#math204STU#a91
STU#b30ENROLL#CRS#math204CRS#math204STU#b30
STU#a91ENROLL#CRS#cs101CRS#cs101STU#a91

Agora Query GSI1 WHERE GSI1PK = "CRS#math204" lista todos os alunos desse curso - o leia a tabela base não pôde servir. Um item extremo, escrito uma vez, responde a ambos direções.

Tem que ser um GSI, não um LSI: a partição do curso é totalmente diferente da partição de estudante e um LSI compartilha a chave de partição da tabela base.

O índice abrange múltiplas partições, portanto deve ser global — consulte GSI vs LSI.

GSIs em DynamoDB são preenchidos de forma assíncrona. Uma nova inscrição pode demorar um pouco momento para aparecer na direção CRS#….

Trate a lista do curso lida como- que o Guia do desenvolvedor chama explicitamente para índices secundários globais.

Escreva e leia em DynoTable

Escrever o registro significa definir quatro atributos principais mais os próprios dados da borda. O condição que impede um aluno de se matricular duas vezes no mesmo curso é uma attribute_not_exists(PK) guarda na chave composta.

Esse é exatamente o tipo de condição que você pode montar visualmente com o DynamoDB Expression Builder em vez de escrevendo à mão os ExpressionAttributeNames e os valores do espaço reservado.

Em DynoTable você aponta um Query para GSI1, defina GSI1PK = "CRS#math204", e o lista volta como uma tabela que você pode ler, classificar e editar no lugar - ambas as direções o relacionamento navegável a partir de um esquema.

Query invertendo GSI em DynoTable para listar todos os alunos matriculados em um curso.
Query invertendo GSI em DynoTable para listar todos os alunos matriculados em um curso.

Armadilhas e próximos passos

  • Não armazene um lado como um atributo de lista. Um array courseIds no item do aluno parece organizado até que um curso precise de sua lista, a matriz atinja o teto de itens de 400 KB ou duas inscrições competem e se derrotam. Dimensionamento e atualização de itens de borda discretos de forma independente.
  • Mantenha os dados de borda na borda. A nota e enrolledOn da inscrição pertencem a o item de borda, não duplicado no aluno ou curso - há exatamente uma linha por par (aluno, curso) a ser atualizado.
  • Lembre-se da propagação GSI. A direção do índice invertido é eventualmente consistente, então um lida imediatamente após uma inscrição pode atrasar uma fração de segundo.
  • Projete apenas o que a escalação precisa. Um KEYS_ONLY ou projeção estreita mantém o GSI pequeno quando a visualização da lista precisa apenas de ids.

Para se aprofundar nos padrões circundantes, leia Design de tabela única para chaves sobrecarregadas e GSI vs LSI para quando o índice invertido precisa ser global. E começar a partir de seus próprios relacionamentos, o livre Ferramenta de design de tabela única transforma um lista de padrões de acesso como "listar os cursos de um aluno / listar os alunos de um curso" em um plano PK/SK/GSI com itens de exemplo.

Então download DynoTable para modelar o esquema dos alunos ↔ cursos de verdade - escreva as arestas, construa a condição com Expression Builder e consulte ambos direções do relacionamento sem uma única varredura. E quando você quiser o visualização JOIN clássica de três tabelas de qualquer maneira, DynoTable's SQL Workbench executa-o em suas mesas ativas.

Atualizado

Experimente este design de forma interativa

Esboce suas entidades e padrões de acesso na ferramenta gratuita de Single-Table Design do DynamoDB — ela sugere modelos de chave PK/SK, prevê as coleções de itens e mostra quais padrões precisam de um GSI.

Abrir a ferramenta de Single-Table Design