Modelação de dados no Azure Cosmos DB

APLICA-SE A: NoSQL

Embora as bases de dados sem esquema, como o Azure Cosmos DB, tornem mais fácil armazenar e consultar dados não estruturados e semiestruturados, deve passar algum tempo a pensar no modelo de dados para tirar o máximo partido do serviço em termos de desempenho e escalabilidade e custo mais baixo.

Como é que os dados vão ser armazenados? Como é que a sua aplicação vai obter e consultar dados? A sua aplicação é pesada de leitura ou escrita pesada?

Depois de ler este artigo, poderá responder às seguintes perguntas:

  • O que é a modelação de dados e por que motivo devo importar-me?
  • Como é que a modelação de dados no Azure Cosmos DB é diferente de uma base de dados relacional?
  • Como devo proceder para relações de dados expressas numa base de dados não relacional?
  • Quando devo incorporar dados e quando posso ligar a dados?

Números em JSON

O Azure Cosmos DB guarda documentos no JSON. O que significa que é necessário determinar cuidadosamente se é necessário converter números em cadeias antes de os armazenar em json ou não. Idealmente, todos os números devem ser convertidos num String, se houver alguma hipótese de estarem fora dos limites de números de precisão dupla, de acordo com o IEEE 754 binary64. A especificação Json chama a atenção para as razões pelas quais a utilização de números fora deste limite em geral é uma má prática no JSON devido a problemas de interoperabilidade prováveis. Estas preocupações são especialmente relevantes para a coluna da chave de partição, porque é imutável e requer que a migração de dados a altere mais tarde.

Incorporar dados

Quando começa a modelar dados no Azure Cosmos DB, tente tratar as suas entidades como itens autónomos representados como documentos JSON.

Para comparação, vamos primeiro ver como podemos modelar dados numa base de dados relacional. O exemplo seguinte mostra como uma pessoa pode ser armazenada numa base de dados relacional.

Modelo de base de dados relacional

A estratégia, ao trabalhar com bases de dados relacionais, é normalizar todos os seus dados. Normalmente, a normalização dos seus dados envolve tomar uma entidade, como uma pessoa, e dividi-la em componentes discretos. No exemplo acima, uma pessoa pode ter vários registos de detalhes de contacto e vários registos de endereços. Os detalhes de contacto podem ser divididos ao extrair ainda mais campos comuns, como um tipo. O mesmo se aplica ao endereço, cada registo pode ser do tipo Casa ou Empresa.

A premissa orientadora ao normalizar dados é evitar armazenar dados redundantes em cada registo e, em vez disso, referir-se aos dados. Neste exemplo, para ler uma pessoa, com todos os respetivos detalhes de contacto e endereços, tem de utilizar o JOINS para compor eficazmente (ou desnormalizar) os seus dados no tempo de execução.

SELECT p.FirstName, p.LastName, a.City, cd.Detail
FROM Person p
JOIN ContactDetail cd ON cd.PersonId = p.Id
JOIN ContactDetailType cdt ON cdt.Id = cd.TypeId
JOIN Address a ON a.PersonId = p.Id

São necessárias operações de escrita em muitas tabelas individuais para atualizar os detalhes e endereços de contacto de uma única pessoa.

Agora, vamos ver como modelaríamos os mesmos dados que uma entidade autónoma no Azure Cosmos DB.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "addresses": [
        {
            "line1": "100 Some Street",
            "line2": "Unit 1",
            "city": "Seattle",
            "state": "WA",
            "zip": 98012
        }
    ],
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555", "extension": 5555}
    ]
}

Através da abordagem acima, desnormalizámos o registo da pessoa ao incorporar todas as informações relacionadas com esta pessoa, como os respetivos detalhes de contacto e endereços, num único documento JSON . Além disso, como não estamos confinados a um esquema fixo, temos a flexibilidade de fazer coisas como ter detalhes de contacto de formas diferentes completamente.

Obter um registo de pessoa completa da base de dados é agora uma única operação de leitura num único contentor e para um único item. Atualizar os detalhes de contacto e os endereços de um registo de pessoa é também uma única operação de escrita num único item.

Ao desnormalizar dados, a sua aplicação poderá ter de emitir menos consultas e atualizações para concluir operações comuns.

Quando incorporar

Em geral, utilize modelos de dados incorporados quando:

  • Existem relações contidas entre entidades.
  • Existem relações um-para-poucos entre entidades.
  • Existem dados incorporados que são alterados com pouca frequência.
  • Existem dados incorporados que não vão crescer sem vinculação.
  • Existem dados incorporados que são consultados frequentemente em conjunto.

Nota

Normalmente, os modelos de dados desnormalizados proporcionam um melhor desempenho de leitura .

Quando não quer incorporar

Embora a regra de polegar no Azure Cosmos DB seja desnormalizar tudo e incorporar todos os dados num único item, isto pode levar a algumas situações que devem ser evitadas.

Pegue este fragmento JSON.

{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "comments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        …
        {"id": 100001, "author": "jane", "comment": "and on we go ..."},
        …
        {"id": 1000000001, "author": "angry", "comment": "blah angry blah angry"},
        …
        {"id": ∞ + 1, "author": "bored", "comment": "oh man, will this ever end?"},
    ]
}

Isto pode ser o aspeto de uma entidade de publicação com comentários incorporados se estivéssemos a modelar um blogue típico, ou CMS, sistema. O problema com este exemplo é que a matriz de comentários não está vinculada, o que significa que não existe um limite (prático) para o número de comentários que qualquer publicação pode ter. Isto pode tornar-se um problema, uma vez que o tamanho do item pode crescer infinitamente grande, pelo que é um design que deve evitar.

À medida que o tamanho do item aumenta a capacidade de transmitir os dados através do fio e ler e atualizar o item, em escala, será afetado.

Neste caso, seria melhor considerar o seguinte modelo de dados.

Post item:
{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "recentComments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        {"id": 3, "author": "jane", "comment": "....."}
    ]
}

Comment items:
[
    {"id": 4, "postId": "1", "author": "anon", "comment": "more goodness"},
    {"id": 5, "postId": "1", "author": "bob", "comment": "tails from the field"},
    ...
    {"id": 99, "postId": "1", "author": "angry", "comment": "blah angry blah angry"},
    {"id": 100, "postId": "2", "author": "anon", "comment": "yet more"},
    ...
    {"id": 199, "postId": "2", "author": "bored", "comment": "will this ever end?"}   
]

Este modelo tem um documento para cada comentário com uma propriedade que contém o identificador de publicação. Isto permite que as publicações contenham qualquer número de comentários e possam crescer de forma eficiente. Os utilizadores que pretendam ver mais do que os comentários mais recentes consultariam este contentor ao transmitir o postId, que deve ser a chave de partição do contentor de comentários.

Outro caso em que a incorporação de dados não é uma boa ideia é quando os dados incorporados são utilizados frequentemente em itens e mudam frequentemente.

Pegue este fragmento JSON.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        {
            "numberHeld": 100,
            "stock": { "symbol": "zbzb", "open": 1, "high": 2, "low": 0.5 }
        },
        {
            "numberHeld": 50,
            "stock": { "symbol": "xcxc", "open": 89, "high": 93.24, "low": 88.87 }
        }
    ]
}

Isto pode representar a carteira de acções de uma pessoa. Escolhemos incorporar as informações de ações em cada documento de portefólio. Num ambiente em que os dados relacionados mudam frequentemente, como uma aplicação de negociação de ações, incorporar dados que mudam frequentemente significa que está constantemente a atualizar cada documento de portefólio sempre que uma ação é negociada.

As ações zbzb podem ser negociadas muitas centenas de vezes num único dia e milhares de utilizadores podem ter zbzb no seu portefólio. Com um modelo de dados como o acima, teríamos de atualizar muitos milhares de documentos de portefólio muitas vezes todos os dias, levando a um sistema que não será dimensionado corretamente.

Dados de referência

A incorporação de dados funciona bem em muitos casos, mas existem cenários em que a desnormalização dos seus dados irá causar mais problemas do que vale a pena. O que fazemos agora?

As bases de dados relacionais não são o único local onde pode criar relações entre entidades. Numa base de dados de documentos, pode ter informações num documento relacionados com dados noutros documentos. Não recomendamos a criação de sistemas mais adequados a uma base de dados relacional no Azure Cosmos DB ou a qualquer outra base de dados de documentos, mas as relações simples são adequadas e podem ser úteis.

No JSON abaixo, optámos por utilizar o exemplo de uma carteira de ações de anteriormente, mas desta vez referimo-nos ao item de ações no portefólio em vez de o incorporarmos. Desta forma, quando o item de ações muda frequentemente ao longo do dia, o único documento que precisa de ser atualizado é o documento de ações único.

Person document:
{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        { "numberHeld":  100, "stockId": 1},
        { "numberHeld":  50, "stockId": 2}
    ]
}

Stock documents:
{
    "id": "1",
    "symbol": "zbzb",
    "open": 1,
    "high": 2,
    "low": 0.5,
    "vol": 11970000,
    "mkt-cap": 42000000,
    "pe": 5.89
},
{
    "id": "2",
    "symbol": "xcxc",
    "open": 89,
    "high": 93.24,
    "low": 88.87,
    "vol": 2970200,
    "mkt-cap": 1005000,
    "pe": 75.82
}

No entanto, uma desvantagem imediata desta abordagem é se a sua aplicação for necessária para mostrar informações sobre cada stock que é mantido ao apresentar o portefólio de uma pessoa; neste caso, teria de fazer várias viagens à base de dados para carregar as informações de cada documento de stock. Aqui, tomámos a decisão de melhorar a eficiência das operações de escrita, que ocorrem frequentemente ao longo do dia, mas por sua vez comprometidas nas operações de leitura que potencialmente têm menos impacto no desempenho deste sistema específico.

Nota

Os modelos de dados normalizados podem exigir mais viagens de ida e volta ao servidor.

E as chaves externas?

Uma vez que atualmente não existe nenhum conceito de restrição, chave externa ou outra, quaisquer relações entre documentos que tenha nos documentos são efetivamente "ligações fracas" e não serão verificadas pela própria base de dados. Se quiser garantir que os dados a que um documento se refere realmente existem, tem de o fazer na sua aplicação ou através de acionadores do lado do servidor ou procedimentos armazenados no Azure Cosmos DB.

Quando referenciar

Em geral, utilize modelos de dados normalizados quando:

  • Representando relações um-para-muitos .
  • Representando relações muitos-para-muitos .
  • Os dados relacionados mudam frequentemente.
  • Os dados referenciados podem não estar vinculados.

Nota

Normalmente, a normalização proporciona um melhor desempenho de escrita .

Onde coloco a relação?

O crescimento da relação ajudará a determinar em que documento armazenar a referência.

Se observarmos o JSON abaixo, esse modelo é publicador e livros.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press",
    "books": [ 1, 2, 3, ..., 100, ..., 1000]
}

Book documents:
{"id": "1", "name": "Azure Cosmos DB 101" }
{"id": "2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "3", "name": "Taking over the world one JSON doc at a time" }
...
{"id": "100", "name": "Learn about Azure Cosmos DB" }
...
{"id": "1000", "name": "Deep Dive into Azure Cosmos DB" }

Se o número de livros por publicador for pequeno com um crescimento limitado, o armazenamento da referência do livro no documento do publicador poderá ser útil. No entanto, se o número de livros por publicador não estiver vinculado, este modelo de dados conduzirá a matrizes mutáveis e crescentes, como no documento do publicador de exemplo acima.

Mudar um pouco de itens resultaria num modelo que ainda representa os mesmos dados, mas que agora evita estas grandes coleções mutáveis.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press"
}

Book documents:
{"id": "1","name": "Azure Cosmos DB 101", "pub-id": "mspress"}
{"id": "2","name": "Azure Cosmos DB for RDBMS Users", "pub-id": "mspress"}
{"id": "3","name": "Taking over the world one JSON doc at a time", "pub-id": "mspress"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

No exemplo acima, retirámos a coleção não vinculada no documento do publicador. Em vez disso, temos apenas uma referência ao publicador em cada documento de livro.

Como devo proceder para modelar relações muitos-para-muitos?

Numa base de dados relacional, as relações muitos-para-muitos são muitas vezes modeladas com tabelas de associação, que apenas associam registos de outras tabelas em conjunto.

Associar tabelas

Poderá sentir-se tentado a replicar a mesma coisa com documentos e a produzir um modelo de dados semelhante ao seguinte.

Author documents:
{"id": "a1", "name": "Thomas Andersen" }
{"id": "a2", "name": "William Wakefield" }

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101" }
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "b3", "name": "Taking over the world one JSON doc at a time" }
{"id": "b4", "name": "Learn about Azure Cosmos DB" }
{"id": "b5", "name": "Deep Dive into Azure Cosmos DB" }

Joining documents:
{"authorId": "a1", "bookId": "b1" }
{"authorId": "a2", "bookId": "b1" }
{"authorId": "a1", "bookId": "b2" }
{"authorId": "a1", "bookId": "b3" }

Isto funcionaria. No entanto, carregar um autor com os seus livros, ou carregar um livro com o autor, exigiria sempre pelo menos duas consultas adicionais na base de dados. Uma consulta ao documento de associação e, em seguida, outra consulta para obter o documento real que está a ser associado.

Se esta associação só está a colar duas partes de dados, por que não largue-a completamente? Considere o seguinte exemplo.

Author documents:
{"id": "a1", "name": "Thomas Andersen", "books": ["b1", "b2", "b3"]}
{"id": "a2", "name": "William Wakefield", "books": ["b1", "b4"]}

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101", "authors": ["a1", "a2"]}
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users", "authors": ["a1"]}
{"id": "b3", "name": "Learn about Azure Cosmos DB", "authors": ["a1"]}
{"id": "b4", "name": "Deep Dive into Azure Cosmos DB", "authors": ["a2"]}

Agora, se eu tivesse um autor, sei imediatamente que livros escreveram e, por outro lado, se tivesse um documento de livro carregado, saberia os IDs dos autores. Isto guarda essa consulta intermediária na tabela de associação, reduzindo o número de viagens de ida e volta do servidor que a sua aplicação tem de fazer.

Modelos de dados híbridos

Analisámos agora a incorporação (ou desnormalização) e a referência (ou normalização) de dados. Cada abordagem tem vantagens e compromissos.

Nem sempre tem de ser ou não ter medo de misturar um pouco as coisas.

Com base nos padrões de utilização e cargas de trabalho específicos da sua aplicação, podem existir casos em que a mistura de dados incorporados e referenciados faz sentido e pode levar a uma lógica de aplicação mais simples, com menos percursos de ida e volta do servidor, mantendo um bom nível de desempenho.

Considere o seguinte JSON.

Author documents:
{
    "id": "a1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "countOfBooks": 3,
    "books": ["b1", "b2", "b3"],
    "images": [
        {"thumbnail": "https://....png"}
        {"profile": "https://....png"}
        {"large": "https://....png"}
    ]
},
{
    "id": "a2",
    "firstName": "William",
    "lastName": "Wakefield",
    "countOfBooks": 1,
    "books": ["b1"],
    "images": [
        {"thumbnail": "https://....png"}
    ]
}

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
        {"id": "a2", "name": "William Wakefield", "thumbnailUrl": "https://....png"}
    ]
},
{
    "id": "b2",
    "name": "Azure Cosmos DB for RDBMS Users",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
    ]
}

Aqui, seguimos (principalmente) o modelo incorporado, em que os dados de outras entidades são incorporados no documento de nível superior, mas outros dados são referenciados.

Se observar o documento do livro, podemos ver alguns campos interessantes quando observarmos a matriz de autores. Existe um id campo que é o campo que utilizamos para fazer referência a um documento de autor, prática padrão num modelo normalizado, mas depois também temos name e thumbnailUrl. Poderíamos ter ficado e id deixado a aplicação para obter informações adicionais necessárias do respetivo documento de autor através da "ligação", mas como a nossa aplicação apresenta o nome do autor e uma imagem em miniatura com cada livro apresentado, podemos guardar uma ida e volta ao servidor por livro numa lista ao desnormalizar alguns dados do autor.

Claro que, se o nome do autor fosse alterado ou quisessem atualizar a sua fotografia, teríamos de atualizar todos os livros que alguma vez publicaram, mas para a nossa aplicação, com base no pressuposto de que os autores não mudam de nome com frequência, esta é uma decisão de design aceitável.

No exemplo, existem valores agregados pré-calculados para poupar o processamento dispendioso numa operação de leitura. No exemplo, alguns dos dados incorporados no documento do autor são dados calculados em tempo de execução. Sempre que um novo livro é publicado, é criado um documento de livro e o campo countOfBooks é definido para um valor calculado com base no número de documentos do livro que existem para um autor específico. Esta otimização seria boa em sistemas de leitura intensiva, onde podemos dar-nos ao luxo de fazer cálculos em escritas para otimizar as leituras.

A capacidade de ter um modelo com campos pré-calculados é possível porque o Azure Cosmos DB suporta transações de vários documentos. Muitas lojas NoSQL não conseguem efetuar transações em documentos e, por conseguinte, defendem decisões de design, como "incorporar sempre tudo", devido a esta limitação. Com o Azure Cosmos DB, pode utilizar acionadores do lado do servidor ou procedimentos armazenados que inserem livros e atualizam autores numa transação ACID. Agora, não tem de incorporar tudo num documento apenas para se certificar de que os seus dados permanecem consistentes.

Distinguir entre diferentes tipos de documentos

Em alguns cenários, poderá querer misturar diferentes tipos de documentos na mesma coleção; Normalmente, este é o caso quando pretende que vários documentos relacionados se situem na mesma partição. Por exemplo, pode colocar livros e críticas de livros na mesma coleção e particioná-lo por bookId. Nesta situação, normalmente pretende adicionar aos seus documentos um campo que identifique o respetivo tipo para os diferenciar.

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
},
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}

Azure Synapse Link para o Azure Cosmos DB é uma capacidade de processamento analítico e transacional híbrido nativo da cloud que lhe permite executar análises quase em tempo real sobre dados operacionais no Azure Cosmos DB. O Azure Synapse Link cria uma integração totalmente integrada entre o Azure Cosmos DB e o Azure Synapse Analytics.

Esta integração ocorre através do arquivo analítico do Azure Cosmos DB, uma representação em colunas dos seus dados transacionais que permite análises em grande escala sem qualquer impacto nas cargas de trabalho transacionais. Este arquivo analítico é adequado para consultas rápidas e económicas em grandes conjuntos de dados operacionais, sem copiar dados e afetar o desempenho das cargas de trabalho transacionais. Quando cria um contentor com o arquivo analítico ativado ou quando ativa o arquivo analítico num contentor existente, todas as inserções transacionais, atualizações e eliminações são sincronizadas com o arquivo analítico quase em tempo real, não são necessárias tarefas de Feed de Alterações ou ETL.

Com o Azure Synapse Link, agora pode ligar diretamente aos contentores do Azure Cosmos DB a partir do Azure Synapse Analytics e aceder ao arquivo analítico, sem custos de Unidades de Pedido (unidades de pedido). Atualmente, o Azure Synapse Analytics suporta o Azure Synapse Link com o Synapse Apache Spark e conjuntos de SQL sem servidor. Se tiver uma conta do Azure Cosmos DB distribuída globalmente, depois de ativar o arquivo analítico para um contentor, este estará disponível em todas as regiões dessa conta.

Inferência automática do esquema do arquivo analítico

Embora o arquivo transacional do Azure Cosmos DB seja considerado dados semiestruturados orientados para linhas, o arquivo analítico tem formato estruturado e em colunas. Esta conversão é feita automaticamente para os clientes, utilizando as regras de inferência de esquemas para o arquivo analítico. Existem limites no processo de conversão: número máximo de níveis aninhados, número máximo de propriedades, tipos de dados não suportados e muito mais.

Nota

No contexto do arquivo analítico, consideramos as seguintes estruturas como propriedade:

  • JSON "elementos" ou "pares string-value separados por um : ".
  • Objetos JSON, delimitados por { e }.
  • Matrizes JSON, delimitadas por [ e ].

Pode minimizar o impacto das conversões de inferência de esquemas e maximizar as capacidades analíticas com as seguintes técnicas.

Normalização

A normalização torna-se insignificante, uma vez que, com o Azure Synapse Link, pode associar-se entre os contentores através do T-SQL ou do Spark SQL. Os benefícios esperados da normalização são:

  • Requisitos de espaço de dados mais pequenos no arquivo transacional e analítico.
  • Transações mais pequenas.
  • Menos propriedades por documento.
  • Estruturas de dados com menos níveis aninhados.

Tenha em atenção que estes dois últimos fatores, menos propriedades e menos níveis, ajudam no desempenho das consultas analíticas, mas também diminuem as hipóteses de partes dos seus dados não serem representadas no arquivo analítico. Conforme descrito no artigo sobre as regras de inferência automática de esquemas, existem limites para o número de níveis e propriedades que são representados no arquivo analítico.

Outro fator importante para a normalização é que os conjuntos sem servidor SQL no Azure Synapse suportam conjuntos de resultados com até 1000 colunas e a exposição de colunas aninhadas também conta para esse limite. Por outras palavras, tanto o arquivo analítico como os conjuntos sem servidor do Synapse SQL têm um limite de 1000 propriedades.

Mas o que fazer, uma vez que a desnormalização é uma técnica de modelação de dados importante para o Azure Cosmos DB? A resposta é que tem de encontrar o equilíbrio certo para as cargas de trabalho transacionais e analíticas.

Chave de Partição

A chave de partição (PK) do Azure Cosmos DB não é utilizada no arquivo analítico. Agora, pode utilizar a criação de partições personalizadas do arquivo analítico para cópias do arquivo analítico com qualquer PK que pretenda. Devido a este isolamento, pode escolher um PK para os seus dados transacionais com foco na ingestão de dados e leituras de pontos, enquanto as consultas entre partições podem ser feitas com o Azure Synapse Link. Vejamos um exemplo:

Num cenário hipotético de IoT global, é um bom PK, device id uma vez que todos os dispositivos têm um volume de dados semelhante e, com isso, não terá um problema de partição frequente. No entanto, se quiser analisar os dados de mais do que um dispositivo, como "todos os dados de ontem" ou "totais por cidade", poderá ter problemas, uma vez que são consultas entre partições. Essas consultas podem prejudicar o desempenho transacional, uma vez que utilizam parte do débito em unidades de pedido para serem executadas. Mas com o Azure Synapse Link, pode executar estas consultas analíticas sem custos de unidades de pedido. O formato columnar do arquivo analítico está otimizado para consultas analíticas e o Azure Synapse Link aplica esta característica para permitir um excelente desempenho com os runtimes do Azure Synapse Analytics.

Tipos de dados e nomes de propriedades

O artigo regras de inferência de esquemas automáticos lista quais são os tipos de dados suportados. Embora o tipo de dados não suportado bloqueie a representação no arquivo analítico, os tipos de dados suportados podem ser processados de forma diferente pelos runtimes Azure Synapse. Um exemplo é: ao utilizar cadeias DateTime que seguem a norma ISO 8601 UTC, os conjuntos do Spark no Azure Synapse irão representar estas colunas como cadeias e os conjuntos sem servidor SQL no Azure Synapse representarão estas colunas como varchar(8000).

Outro desafio é que nem todos os carateres são aceites pelo Azure Synapse Spark. Embora os espaços brancos sejam aceites, carateres como dois pontos, acento grave e vírgula não são. Digamos que o seu documento tem uma propriedade com o nome "Nome Próprio, Apelido". Esta propriedade será representada no arquivo analítico e o conjunto sem servidor do Synapse SQL pode lê-la sem problemas. No entanto, uma vez que está no arquivo analítico, o Azure Synapse Spark não consegue ler dados do arquivo analítico, incluindo todas as outras propriedades. No final do dia, não pode utilizar Azure Synapse Spark quando tem uma propriedade com os carateres não suportados nos respetivos nomes.

Aplanamento de dados

Todas as propriedades no nível de raiz dos seus dados do Azure Cosmos DB serão representadas no arquivo analítico como uma coluna e tudo o resto que estiver em níveis mais profundos do modelo de dados do documento será representado como JSON, também em estruturas aninhadas. As estruturas aninhadas exigem processamento adicional de Azure Synapse runtimes para aplanar os dados em formato estruturado, o que pode ser um desafio em cenários de macrodados.

O documento abaixo terá apenas duas colunas no arquivo id analítico e contactDetails. Todos os outros dados, email e phone, precisarão de processamento adicional através das funções SQL para serem lidos individualmente.


{
    "id": "1",
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555"}
    ]
}

O documento abaixo terá três colunas no arquivo analítico, id, emaile phone. Todos os dados são diretamente acessíveis como colunas.


{
    "id": "1",
    "email": "thomas@andersen.com",
    "phone": "+1 555 555-5555"
}

Arrumo de dados

Azure Synapse Link permite-lhe reduzir os custos das seguintes perspetivas:

  • Menos consultas em execução na base de dados transacional.
  • Um PK otimizado para ingestão de dados e leituras de pontos, redução da quantidade de dados, cenários de partições frequentes e divisões de partições.
  • O arrumo de dados, uma vez que o time-to-live analítico (attl) é independente do time-to-live transacional (tttl). Pode manter os dados transacionais no arquivo transacional durante alguns dias, semanas, meses e manter os dados no arquivo analítico durante anos ou para sempre. O formato columnar do arquivo analítico traz uma compressão de dados naturais, de 50% até 90%. E o custo por GB é de ~10% do preço real da loja transacional. Para obter mais informações sobre as limitações atuais da cópia de segurança, veja Analytical store overview (Descrição geral do arquivo analítico).
  • Não existem tarefas ETL em execução no seu ambiente, o que significa que não precisa de aprovisionar unidades de pedido para as mesmas.

Redundância controlada

Esta é uma ótima alternativa para situações em que um modelo de dados já existe e não pode ser alterado. E o modelo de dados existente não se encaixa bem no arquivo analítico devido a regras de inferência de esquemas automáticas, como o limite de níveis aninhados ou o número máximo de propriedades. Se for este o seu caso, pode utilizar o Feed de Alterações do Azure Cosmos DB para replicar os seus dados para outro contentor, aplicando as transformações necessárias para um modelo de dados amigável do Azure Synapse Link. Vejamos um exemplo:

Scenario

O contentor CustomersOrdersAndItems é utilizado para armazenar encomendas online, incluindo detalhes de clientes e itens: endereço de faturação, endereço de entrega, método de entrega, estado de entrega, preço dos itens, etc. Apenas as primeiras 1000 propriedades são representadas e as informações principais não estão incluídas no arquivo analítico, bloqueando a utilização do Azure Synapse Link. O contentor tem PBs de registos que não é possível alterar a aplicação e remodelar os dados.

Outra perspetiva do problema é o volume de macrodados. Milhares de milhões de linhas são constantemente utilizadas pelo Departamento de Análise, o que as impede de utilizar tttl para eliminação de dados antiga. Manter todo o histórico de dados na base de dados transacional devido às necessidades analíticas obriga-os a aumentar constantemente o aprovisionamento de unidades de pedido, afetando os custos. As cargas de trabalho transacionais e analíticas competem pelos mesmos recursos ao mesmo tempo.

O que fazer?

Solução com Feed de Alterações

  • A equipa de engenharia decidiu utilizar o Feed de Alterações para preencher três novos contentores: Customers, Orderse Items. Com o Feed de Alterações, estão a normalizar e a aplanar os dados. As informações desnecessárias são removidas do modelo de dados e cada contentor tem cerca de 100 propriedades, evitando a perda de dados devido a limites automáticos de inferência de esquemas.
  • Estes novos contentores têm o arquivo analítico ativado e agora o Departamento de Análise está a utilizar o Synapse Analytics para ler os dados, reduzindo a utilização das unidades de pedido, uma vez que as consultas analíticas estão a ocorrer no Synapse Apache Spark e nos conjuntos de SQL sem servidor.
  • O contentor CustomersOrdersAndItems tem agora o tttl definido para manter os dados apenas durante seis meses, o que permite a redução da utilização de outras unidades de pedido, uma vez que existe um mínimo de 10 unidades de pedido por GB no Azure Cosmos DB. Menos dados, menos unidades de pedido.

Conclusões

As maiores conclusões deste artigo são compreender que a modelação de dados num mundo sem esquemas é tão importante como sempre.

Tal como não existe uma única forma de representar um conjunto de dados num ecrã, não existe uma única forma de modelar os seus dados. Tem de compreender a sua aplicação e como irá produzir, consumir e processar os dados. Em seguida, ao aplicar algumas das diretrizes aqui apresentadas, pode definir sobre a criação de um modelo que responde às necessidades imediatas da sua aplicação. Quando as suas aplicações precisarem de ser alteradas, pode utilizar a flexibilidade de uma base de dados sem esquema para adotar essa alteração e evoluir facilmente o seu modelo de dados.

Passos seguintes