Dimensões que mudam lentamente e seu uso no MongoDB
Avalie esse Artigo
O conceito de “slowly changing dimensions” (geralmente abreviado como SCD) existe há muito tempo e é um elemento básico no armazenamento de dados baseado em SQL. A ideia fundamental é acompanhar todas as alterações nos dados do data warehouse ao longo do tempo. A parte “slowly changing” do nome se refere à suposição de que os dados cobertos por esse modelo de dados mudam com uma frequência baixa, mas sem nenhum padrão aparente no tempo. Esse modelo de dados é usado quando os requisitos do data warehouse abrangem a funcionalidade de rastrear e reproduzir saídas com base nos estados históricos dos dados.
Um caso comum disso é para fins de relatório, em que o data warehouse deve explicar a diferença de um relatório produzido no mês passado e por que os valores agregados são diferentes na versão atual do relatório. Requisitos como estes são frequentemente encontrados em sistemas de relatórios financeiros.
Há muitas maneiras de implementar dimensões de mudança lenta no SQL, conhecidas como "types. " Os tipos 0 e 1 são os mais básicos que apenas mantêm o controle do estado atual dos dados (no Tipo 1) ou no estado original (Tipo 0). O mais comumente aplicado é o Tipo 2. O tipo de SCD 2 implementa três novos campos, "validFrom, "validTo, " e um sinalizador opcional no conjunto de dados mais recente, que geralmente é chamado de "isValid " ou "isEffective. "
Tabela de tipos de SCD:
Tipo SCD | Descrição |
Tipo de SCD 0 | Mantenha apenas o estado original, os dados não podem ser alterados |
Tipo de SCD 1 | Apenas mantenha o estado atualizado, o histórico não pode ser armazenado |
Tipo de SCD 2 | Manter histórico na nova linha/documento |
Tipo de SCD 3 | Mantenha o histórico em novos campos na mesma linha/documento |
Tipo de SCD 4 | Mantenha o histórico em uma coleção separada |
Tipos de SCD >4 | Combinações de tipos anteriores — por exemplo, Tipo 5 é Tipo 1 mais Tipo 4 |
Nessa implementação mais simples do SCD, cada registro contém as informações sobre o período de validade desse conjunto de dados e todas as diferentes validades são mantidas na mesma coleção ou tabela.
Ao aplicar este mesmo conceito ao document model do MongoDB, a abordagem é exatamente a mesma de um banco de dados relacional. Na comparação de modelos de dados, a normalização que é o básico dos bancos de dados relacionais não é a abordagem recomendada no document model, mas os detalhes disso foram abordados em muitas postagens de blog — por exemplo, as 6 de Thumb para MongoDB Schema Design. O conceito de dimensões que mudam lentamente se aplica por documento no document model escolhido e otimizado para o caso de uso específico. A melhor maneira de ilustrar isso é em um pequeno exemplo.
Considere o seguinte caso de uso: seu MongoDB armazena os preços de um conjunto de itens, e você precisa acompanhar as alterações de preço de um item ao longo do tempo, para poder processar as devoluções de um item, à medida que o dinheiro o reembolsado precisa ser o preço do item no momento da compra. Você tem uma coleção simples chamada "prices" e cada documento tem um itemID e um preço.
1 db.prices.insertMany([ 2 { 'item': 'shorts', 'price': 10 }, 3 { 'item': 't-shirt', 'price': 2 }, 4 { 'item': 'pants', 'price': 5 } 5 ]);
Agora, o preço de “pants” muda de 5 para 7. Isso pode ser feito e rastreado assumindo valores padrão para os campos de dados necessários para o SCD Tipo 2. O valor padrão para “validFrom” é 01.01.1900, “validTo” é 01.01.9999, e isValid é "true. "
A alteração no preço do item "pants" é então executada como uma inserção do novo documento e uma atualização do anteriormente válido.
1 let now = new Date(); 2 db.prices.updateOne( 3 { 'item': 'pants', "$or":[{"isValid":false},{"isValid":null}]}, 4 {"$set":{"validFrom":new Date("1900-01-01"), "validTo":now,"isValid":false}} 5 ); 6 db.prices.insertOne( 7 { 'item': 'pants', 'price': 7 ,"validFrom":now, "validTo":new Date("9999-01-01"),"isValid":true} 8 );
Como é essencial que a cadeia de validade não seja interrompida, as duas operações do banco de dados devem acontecer com o mesmo carimbo de data/hora. Dependendo dos requisitos do aplicativo, pode fazer sentido encapsular esses dois comandos em uma transação para garantir que ambas as alterações sejam sempre aplicadas juntas. Também existem maneiras de colocar esse processo em segundo plano, mas de acordo com a suposição inicial nas dimensões que mudam lentamente, mudanças como essa são raras e a consistência dos dados é a maior prioridade. Portanto, o impacto no desempenho de uma transação é aceitável para esse caso de uso.
Se você quiser consultar o preço mais recente de um item, basta especificar:
1 db.prices.find({ 'item': 'pants','isValid':true});
E se você quiser consultar o estado em um momento específico:
1 let time = new Date("2022-11-16T13:00:00") 2 db.prices.find({ 'item': 'pants','validFrom':{'$lte':time}, 'validTo':{'$gt':time}});
Este exemplo mostra que a flexibilidade do document model nos permite pegar um conceito relacional e aplicá-lo diretamente aos dados dentro do MongoDB. Mas também abre outros métodos que não são possíveis em relational database. Considere o seguinte: e se você só precisar controlar as alterações em pouquíssimos campos em um documento? Em seguida, você pode simplesmente incorporar o histórico de um campo como uma array no primeiro documento. Isso implementa o Tipo SCD 3, armazenando o histórico em novos campos, mas sem a limitação e a sobrecarga de criar novas colunas em um relational database. O SCD Tipo 3 em RDMBS geralmente é limitado a armazenar apenas as últimas alterações, pois não é possível adicionar novas colunas dinamicamente.
O seguinte pipeline de agregação faz exatamente isso. Ele altera o preço para 7e armazena o valor anterior do preço com um carimbo de data/hora de quando o preço antigo se tornou inválido em uma matriz chamada "priceHistory":
1 db.prices.aggregate([ 2 { $match: {'item': 'pants'}}, 3 { $addFields: { price: 7 , 4 priceHistory: { $concatArrays: 5 [{$ifNull: ['$priceHistory', []]}, 6 [{price: "$price",time: now}]]} 7 } 8 }, 9 { $merge: { 10 into: "prices", 11 on: "_id", 12 whenMatched: "merge", 13 whenNotMatched: "fail" 14 }}])
Existem algumas advertências a essa solução que abrangem arrays grandes, mas existem soluções conhecidas para lidar com esse tipo de desafio de modelagem de dados. Para evitar arrays grandes, você pode aplicar os padrões "Outlier " ou "Bucketing " das muitas possibilidades no design do esquema MongoDB e de muitas explicações úteis sobre o que evitar.
Dessa forma, você poderia armazenar o histórico mais recente de alterações de dados nos próprios documentos e, se alguma análise se aprofunde nas alterações passadas, ela teria que carregar o histórico de alterações mais antigo de uma coleção separada. Essa abordagem pode soar semelhante à questão declarada de adicionar novos campos em um banco de dados relacional, mas há duas diferenças: em primeiro lugar, o MongoDB não encontra esse problema até que mais de 100 alterações sejam feitas em um único documento. E em segundo lugar, o MongoDB tem ferramentas para lidar dinamicamente com arrays grandes, enquanto em DBs relacionais, a solução seria escolher uma abordagem diferente, pois mesmo a pré-alocação de mais de 10 colunas para alterações não é uma boa ideia no SQL.
Mas em ambos os mundos, lidar com muitas mudanças no tipo de SCD 3 requer uma extensão para um tipo de SCD diferente, pois ter uma coleção separada para o histórico é o tipo de SCD 4.
O exemplo mostrado se concentra em uma representação estrita e precisa das mudanças. Às vezes, há requisitos menos rígidos sobre a necessidade de mostrar mudanças históricas nos dados. Pode ser que 95% das vezes, os aplicativos que usam o MongoDB database estejam interessados apenas no estado atual dos dados, mas algumas consultas (analíticas) ainda precisem ser executadas no histórico completo dos dados.
Nesse caso, pode ser mais eficiente armazenar a versão atual dos dados em uma collection e as mudanças históricas em outra. Em seguida, a coleção histórica pode até ser removida do cluster ativo do MongoDB usando as funcionalidades do MongoDB Atlas Federated Database e, na versão totalmente gerenciada, usando o Atlas Online Archive.
Se o requisito de rastreamento das alterações for diferente de uma forma que nem todas as alterações precisem ser rastreadas, mas sim uma série de checkpoints seja necessária para mostrar o estado dos dados em momentos específicos, o Atlas Data Lake pode ser a solução correta. Com oAtlas Data Lake, você pode extrair um snapshot dos dados em pontos específicos no tempo, proporcionando um nível semelhante de rastreabilidade, embora em intervalos de tempo fixos. Inicialmente, o conceito de SCD foi desenvolvido para evitar a duplicação de dados nesse caso, pois ele não armazena um documento adicional se nada mudar. No mundo de hoje, onde o armazenamento frio se tornou muito mais acessível, o Data Lake oferece a possibilidade de analisar dados do seu sistema produtivo, usando snapshots regulares, sem fazer alterações no sistema ou mesmo aumentar a carga no banco de dados principal.
Em suma, o conceito de dimensões que mudam lentamente permite que você cubra parte dos principais requisitos de um data warehouse, fornecendo as ferramentas necessárias para acompanhar todas as mudanças.
Embora o conceito fundamental de dimensões que mudam lentamente tenha sido desenvolvido com data warehouses em mente, outra área em que derivados das técnicas desenvolvidas podem ser úteis é em aplicativos orientados a eventos. Como você tem eventos pouco frequentes, em diferentes tipos de categorias, muitas vezes é uma ação cara no banco de dados encontrar o evento mais recente por categoria. O processo para isso pode exigir o agrupamento e/ou classificação de seus dados para encontrar o estado atual.
Nesse caso, pode fazer sentido alterar o modelo de dados por um sinalizador semelhante ao sinalizador "isValid" do exemplo SCD Type 2 acima, ou até mesmo ir uma etapa além e não apenas armazenar a hora do evento por documento, mas adicionar a hora do próximo evento de forma semelhante à implementação do SCD Type 2 . O sinalizador permite consultas muito rápidas do conjunto de dados mais recente por tipo de evento, e a data garante que, se você executar uma pesquisa para um ponto específico no tempo, será fácil e eficiente obter o respectivo evento que você está procurando.
Nesse caso, pode fazer sentido separar os "events " e suas versões processadas que incluem o sinalizador isValid e a data de término de validade em collections separadas, utilizando mais metodologias dos diferentes tipos de implementações de SCD.
Portanto, da próxima vez que você encontrar um modelo de dados que exija o controle das alterações, pense: "SCD could be useful and can easily be applied in the document model." Se você deseja implementar dimensões que mudam lentamente em seu caso de uso do MongoDB, considere obter suporte da equipe de Serviços Profissionais do MongoDB .