Tradeoffs entre Normalização e Desnormalização

TIL a diferença entre esquemas normalizados e desnormalizados para modelação de dados, e alguns dos tradeoffs com cada um deles.

Normalização

Normalização é uma forma de definir o seu esquema de base de dados de uma forma optimizada para uma integridade rápida e elevada writes, assegurando a ausência de dados redundantes nas tabelas.

O padrão para normalização é disparar para 3rd normal form (3nf).

Uma breve recapitulação dos critérios para alcançar 3nf:

  • Os registos numa tabela devem conter as teclas_primárias que os referenciem de forma única (1nf)

  • Não deve haver colunas repetidas numa tabela (1nf)

  • There não deve haver nenhuma chave funcionalmente dependente (2nf)

  • Não deve haver nenhuma chave funcionalmente dependente (3nf)

P>P>Posto que as tabelas não contêm quaisquer dados redundantes, o storage necessário para novos registos a qualquer tabela é relativamente pequeno.

Desde que as escritas são pequenas, são também fast.

Escritas são também garantidas para deixar a base de dados num consistent estado, devido a referential integrity garantias de restrições chave estrangeiras entre tabelas relacionadas.

Denormalização

Denormalização é o acto de adicionar redundâncias ou valores derivados ao seu esquema para optimizar para reads que de outra forma seria dispendioso num esquema normalizado.

Pense numa base de dados totalmente normalizada durante um minuto….

Tudo está na sua própria tabela…

Tudo tem a sua própria chave_primária…

As referências entre tabelas são mantidas por restrições de chaves_estrangeiras…

… isto é óptimo de uma perspectiva de armazenamento e integridade, mas pode levar a que pedaços de uma consulta sejam distribuídos por muitas mesas, o que leva a lentas e complexas uniões para obter o quadro completo de uma consulta.

Se for capaz de antecipar os tipos de consultas que os seus utilizadores possam estar a fazer, poderia fazer sentido armazenar alguns valores redundantemente no seu sistema para acelerar o desempenho da consulta.

Adicionar valores desnormalizados torna as inserções e actualizações mais complicadas: é necessário assegurar que os valores desnormalizados são devidamente mantidos, uma vez que a integridade destes valores não é automaticamente imposta pelo esquema.

A escolha de desnormalizar deve ser feita conscientemente. Deve ser documentada, testada, e deve ser comunicada através de uma equipa para que todos estejam cientes desta consideração adicional ao escrever para tabelas desnormalizadas.

Exemplo desnormalizado

Estou a construir uma aplicação chamada CashBackHero, que envolve a modelação de relações entre cartões de crédito e utilizadores através de uma entidade chamada carteira. Um esquema 3nf normalized para estas entidades pode parecer algo parecido com isto:

CashBack

th>id th>value
1 3
2 4
3 5

Cartões

id name cash_back_id
1 Chase 2
2 Discover 1
3 Amex 3

Wallets

th>id name
1 Brian’s Wallet
2 Alex’s Wallet
3 Lauren’s Wallet

CardWallets (associações entre carteiras e cartões)

>th>wallet_id

id card_id
1 1 1
2 1 3
3 2 2
4 3 2
5 3 1

As tabelas acima são normalized, uma vez que não contêm quaisquer dados redundantes. As relações entre tabelas são mantidas através de foreign_key restrições a outras tabelas. Isto armazena dados na sua maioria compact form.

A falta de dados redundantes também optimiza para writes a base de dados. As escritas mais frequentes são adições e remoções à tabela CardWallets, que apenas envolve colunas de id que contêm referências a outras tabelas contendo informação.

Normalizar dados também torna as actualizações uma brisa, uma vez que cada tabela é uma única fonte de verdade para a informação contida na mesma.

Uma base de dados normalizada também optimiza para alguns tipos de leituras, como por exemplo, a colocação de uma lista de todos os valores de uma determinada tabela (como obter todas as cartas).

Lidas em que os dados se encontram em múltiplas tabelas, no entanto, tornam-se mais desafiantes. Uma consulta ao esquema normalizado acima para obter os nomes e valores de retorno de dinheiro das cartas para a carteira 1 envolve juntar a tabela Wallets e Cards tabelas à tabela CardWallets, depois juntar a tabela CashBackValue à tabela Cards. Se estas tabelas se tornarem grandes, as consultas podem tornar-se lentas quando comparadas com uma versão desnormalizada onde todos os dados vivem numa só tabela.

Fala de desnormalizado…

Exemplo desnormalizado

Verifica como seria o esquema normalizado apresentado acima se fosse totalmente desnormalizado:

CashBackSchema

2


id card_name wallet_name cash_back_value
1 Chase Brian’s Wallet 4
Amex Brian’s Carteira 5
3 Discover Carteira do Alex 3
4 Discover Carteira do Lauren 3
5 Chase Lauren’s Wallet 4

É uma mesa monstro!

p>Ok… são apenas 5 filas… mas fica-se com a ideia de que uma tabela desnormalizada pode muitas vezes ser mais fácil de ler, uma vez que todos os dados existem num só lugar.

Em vez de precisarmos de juntar 4 mesas para obter as cartas e o valor de cash_back_value para a carteira de um utilizador, podemos agora apenas executar uma declaração selectiva para a tabela acima de CashBackSchema.

A consulta de cartões únicos torna-se um pouco mais lenta, uma vez que toda a tabela precisa de ser lida para determinar nomes_de_cartões únicos.

As actualizações são também um pouco mais complicadas: precisamos de assegurar a integridade dos nossos dados logically. Por exemplo, se quisermos actualizar o valor cash_back_value do cartão Chase na carteira de Brian, precisamos também de considerar se precisamos de actualizar o valor cash_back_value do cartão Chase também na carteira de Lauren. As actualizações precisam de ser feitas em vários locais, o que pode ser lento para tabelas grandes.

Num esquema normalizado, as actualizações para algo como “wallet_display_name” podem ser feitas apenas num só local (a Wallets tabela), em vez de precisarmos de pentear toda a CashBackSchema para assegurar que cada fila com o nome “Brian’s Wallet” é actualizada apropriadamente.