3.4 Arquitetura de dados e armazenamento
Visão geral e motivação
Os dados sobrevivem ao código. As aplicações são reescritas a cada poucos anos, mas os dados que elas gerenciam (registros de clientes, livros-razão financeiros, históricos de benefícios, prontuários de saúde) persistem por décadas. Muitas vezes são o ativo mais valioso e mais regulamentado da organização. A arquitetura de dados é a disciplina de decidir como esses dados são modelados, onde são armazenados, como são mantidos consistentes, como evoluem e como são servidos com rapidez suficiente em escala. Para uma grande organização essas decisões são fundamentais. A sua escolha de motores de armazenamento e de modelos de dados restringe o que o negócio pode fazer, com que rapidez pode se mover e quanto custa, durante toda a vida do sistema.
O que está em jogo é maior em empresas e governo por causa da escala, da longevidade e da regulamentação. O armazenamento de transações de um banco nunca deve perder nem contar em dobro um centavo. Um registro governamental precisa reter registros pelos períodos estatutários e provar sua integridade aos auditores. Um sistema de saúde precisa impor acesso detalhado e regras de residência. Ao mesmo tempo, essas organizações atendem enormes volumes de leitura e de escrita e não podem se dar ao luxo de fazer toda consulta bater num único banco de dados relacional. Então a arquitetura de dados precisa conciliar correção e durabilidade com desempenho e escala, e fazê-lo enquanto o esquema continua mudando para atender a novos mandatos.
Este capítulo cobre os principais paradigmas de armazenamento e quando usar cada um, a disciplina da persistência poliglota, a modelagem de dados e o problema frequentemente subestimado da evolução e migração de esquemas, o cache e as CDNs (redes de distribuição de conteúdo) com o notoriamente difícil problema da invalidação e como as transações, o travamento e a concorrência se comportam quando você os leva à escala. O fio condutor é simples: não existe banco de dados universal. Existem compromissos, e uma boa arquitetura de dados significa escolhê-los conscientemente, uma carga de trabalho por vez.
Princípios fundamentais
- Modele os dados para se ajustar aos padrões de acesso, e não o contrário. Projete o armazenamento em torno de como os dados serão lidos e escritos, não de um modelo “correto” abstrato.
- Não existe um banco de dados para governar todos. Cargas de trabalho diferentes querem motores diferentes. A persistência poliglota é normal em escala.
- Correção primeiro para os sistemas de registro. Para dados autoritativos, a durabilidade e a consistência são inegociáveis. Otimize o desempenho em torno delas, não através delas.
- O esquema vai mudar, então planeje isso. As migrações são uma atividade de engenharia de primeira classe e contínua, não um evento único.
- Seja dono dos seus dados atrás de uma fronteira de serviço. Cada contexto delimitado (um modelo de domínio autocontido com sua própria fronteira explícita) é dono dos seus dados. Compartilhar um banco de dados acopla as equipes e destrói a autonomia.
- O cache é um problema de correção disfarçado de ganho de desempenho. Todo cache introduz obsolescência e risco de invalidação. Trate-o deliberadamente.
- A desnormalização é uma troca, não um pecado. Duplicar dados para desempenho de leitura é legítimo se você assume as consequências de consistência.
- Consistência e escala se trocam. Quanto mais forte a garantia transacional, mais difícil é distribuí-la. Compre apenas o que a carga de trabalho precisa.
Recomendações
Escolha o paradigma de armazenamento a partir da carga de trabalho
Ajuste cada carga de trabalho ao modelo que lhe serve. Os bancos relacionais dão forte consistência, junções e transações maduras. São o padrão para sistemas de registro e tudo que tenha regras complexas de integridade. Os armazenamentos de documentos servem a dados hierárquicos e de esquema flexível lidos como uma unidade (um pedido inteiro, um perfil inteiro). Os armazenamentos de chave-valor dão velocidade extrema para buscas simples (sessões, sinalizadores de funcionalidade, caches). Os bancos de grafos se destacam onde as relações são a consulta (redes de fraude, organogramas, direitos de acesso, cadeias de suprimentos). Os armazenamentos colunares sustentam consultas analíticas que varrem poucas colunas em bilhões de linhas (data warehouses, relatórios). Os bancos de séries temporais otimizam dados com muito acréscimo e carimbo de tempo (métricas, telemetria, sensores da Internet das Coisas (IoT), dados de mercado). Resista a forçar um só motor a fazer todos os trabalhos. Usar um banco relacional como fila, ou um armazenamento de documentos como livro-razão, convida à dor.
Adote a persistência poliglota deliberadamente
Sistemas grandes legitimamente usam vários armazenamentos: um sistema de registro relacional, um índice de busca, um cache, um data warehouse analítico e talvez um motor de grafos ou de séries temporais. Isso é persistência poliglota, e é o padrão certo quando as cargas de trabalho genuinamente diferem. O custo é operacional, porque agora você tem mais motores para operar, proteger, copiar e dotar de pessoal. Gerencie esse custo tratando cada armazenamento como de propriedade de um serviço, padronizando as ferramentas operacionais e mantendo o número de tecnologias às que merecem seu lugar. Desconfie de adotar um novo banco de dados para toda necessidade menor. Cada um é um compromisso operacional permanente.
Modele os dados e trate a evolução do esquema como contínua
Invista em modelagem de dados logo no início para os sistemas de registro. Normalize para proteger a integridade e depois desnormalize seletivamente para os pontos quentes de leitura comprovados. Seja qual for o modelo, o esquema evolui para sempre, então torne as migrações seguras e rotineiras. Use scripts de migração versionados, automatizados e somente para a frente, guardados no controle de código-fonte e aplicados pelo pipeline de implantação. Para mudanças sem indisponibilidade em tabelas grandes, use o padrão expandir-contrair (mudança paralela): acrescente a nova coluna ou tabela, preencha o histórico e escreva em duplicidade, migre os leitores e depois remova a forma antiga. Nunca um único alter que quebra. Torne as mudanças de esquema retrocompatíveis entre implantações para que o código antigo e o novo rodem ao mesmo tempo. Em sistemas baseados em eventos ou em mensagens, versione explicitamente os esquemas de eventos e de mensagens e apoie o upcasting dos eventos antigos (transformá-los para o esquema atual na leitura).
Projete o cache e a invalidação de olhos abertos
O cache e as CDNs são as ferramentas de desempenho de maior alavancagem. Uma CDN serve conteúdo estático e passível de cache a partir da borda, perto dos usuários, e os caches de aplicação poupam o banco de dados de leituras repetidas. Mas a parte difícil é a invalidação: saber quando os dados em cache estão obsoletos. Escolha uma estratégia por caso. Use expiração por tempo (TTL) onde uma leve obsolescência é aceitável e é o mais simples. Use invalidação explícita ou escrita direta (write-through) onde a atualidade importa. Use cache-aside onde a aplicação gerencia o preenchimento. Defina os TTLs conscientemente, proteja-se contra as debandadas de cache (muitos clientes reconstruindo ao mesmo tempo a mesma entrada expirada) com travas ou coalescência de requisições e previna rebanhos trovejantes em caches frios. Nunca faça cache de dados cuja obsolescência possa causar uma falha de correção ou de conformidade (direitos de acesso, saldos, consentimento) sem um caminho de invalidação explícito e testado. Trate as chaves de cache, os TTLs e a invalidação como artefatos projetados, não como configuração incidental.
Gerencie transações, travamento e concorrência para a escala
Entenda os níveis de isolamento e escolha o mais fraco que ainda seja correto para cada transação, porque um isolamento mais alto custa concorrência. Prefira a concorrência otimista (verificações de versão na escrita) para cargas de baixa contenção e muita leitura e recorra ao travamento pessimista apenas sob contenção quente genuína, mantendo as travas curtas e consistentemente ordenadas para evitar impasses. À medida que você escala, um único banco de dados gravável vira o gargalo. Introduza réplicas de leitura para o escalamento de leituras (aceitando a defasagem de replicação) e faça sharding/particionamento por uma chave que distribua a carga de forma uniforme e mantenha juntos os dados relacionados para evitar transações entre shards. Lembre que o sharding abre mão das junções fáceis entre shards e das transações ACID (Atomicidade, Consistência, Isolamento, Durabilidade) entre vários shards, que é muitas vezes a razão pela qual aparecem sagas e desnormalização. Introduza essas técnicas apenas à medida que a carga de trabalho exigir. O sharding prematuro acrescenta complexidade permanente.
Compromissos: prós e contras
| Tipo de armazenamento | Melhor para | Pontos fortes | Pontos fracos |
|---|---|---|---|
| Relacional | Sistemas de registro, integridade complexa | ACID, junções, ferramentas maduras | Mais difícil de escalar escritas horizontalmente |
| Documentos | Leituras de agregados, esquema flexível | Leitura/escrita rápida de objetos inteiros, flexível | Junções/transações fracas entre documentos |
| Chave-valor | Sessões, caches, buscas simples | Velocidade e escala extremas | Sem consulta além da chave |
| Grafos | Consultas carregadas de relações | Travessias rápidas, expressivo | Habilidades operacionais de nicho, limites de escala |
| Colunar | Análise, relatórios | Varreduras agregadas rápidas, compressão | Ruim para escritas transacionais por linha |
| Séries temporais | Métricas, telemetria, IoT | Acréscimo e consultas por tempo eficientes | Propósito estreito |
A troca dominante é consistência e consultas ricas versus escalabilidade horizontal e velocidade. Os sistemas relacionais dão as garantias mais fortes e as consultas mais flexíveis, mas são os mais difíceis de escalar em escritas entre muitas máquinas. As famílias NoSQL (não relacionais) relaxam junções, transações ou esquema para ganhar escala e velocidade. O cache troca atualidade por latência. O sharding troca transações entre partições por vazão de escrita. Nenhum desses é universalmente certo. A arte é colocar cada carga de trabalho no ponto da curva que as suas necessidades reais de correção e desempenho exigem.
Perguntas para discutir com sua equipe
Para cada conjunto de dados crítico, todos conseguem nomear o único sistema de registro, ou caches e projeções são tratados em silêncio como verdade? Os dados sobrevivem ao código, e os incidentes de dados mais danosos vêm da deriva: um cache, um índice de busca ou uma projeção de leitura é tomado por autoritativo e diverge em silêncio da fonte real. Numa equipe grande isso acontece quando a responsabilidade é difusa e vários serviços escrevem cópias sobrepostas, de modo que ninguém consegue dizer qual valor é o correto durante um incidente. Leve um mapa dos seus dados importantes e, para cada item, o único armazenamento que é autoritativo mais as cópias derivadas que precisam ser reconstruíveis a partir dele. Em finanças e governo, poder provar qual registro é a fonte legal e reconstruir o resto costuma ser uma exigência regulatória, não uma conveniência. Qualquer coisa que vocês não consigam reconstruir a partir do sistema de registro é ela mesma um sistema de registro, quer vocês pretendessem ou não.
Quanto custa de fato operar, proteger e copiar cada motor de banco de dados do seu parque, e todos ainda merecem seu lugar? A persistência poliglota é certa quando as cargas de trabalho genuinamente diferem, mas cada motor é um compromisso operacional permanente: aplicação de correções, cópias de segurança, monitoramento, revisão de segurança e pessoas que o conheçam às 3 da manhã. Uma grande organização pode derivar para um zoológico de armazenamentos adotados cada um para uma funcionalidade, e o marginal acrescenta custo para sempre enquanto atende a uma carga que um armazenamento que vocês já operam poderia tratar. Listem cada motor, a carga que o justifica e quem está de sobreaviso por ele e depois sinalizem qualquer um adotado para uma necessidade que um armazenamento principal agora poderia atender. A adoção de um novo banco de dados deve superar uma barra alta, porque removê-lo depois significa outra migração. Padronizar as ferramentas operacionais entre os armazenamentos que vocês mantêm é como se segura o custo sem forçar um só motor a fazer todos os trabalhos.
Onde um usuário pode ler um valor obsoleto de uma réplica logo depois da própria escrita, e isso quebra uma promessa que vocês fizeram a ele? As réplicas de leitura escalam as leituras mas ficam defasadas em relação ao primário, então um usuário que atualiza um perfil e recarrega de imediato pode ver o valor antigo, o que se lê como um bug ou, para um saldo ou um sinalizador de consentimento, uma falha de conformidade. Decidam por fluxo se ler-suas-escritas importa e encaminhem essas leituras para o primário ou usem um mecanismo de consistência de sessão. Levem a lista dos fluxos servidos por réplicas e marquem quais um usuário aciona logo depois de escrever. Para saldos, direitos de acesso e consentimento, tratem leituras obsoletas como falhas de correção, não cosméticas. O ponto é comprar a consistência que cada carga de trabalho realmente precisa e tornar explícita, e não acidental, a obsolescência que vocês aceitam.
Vocês conseguem mudar hoje o esquema da sua maior e mais movimentada tabela sem indisponibilidade, e quem de fato ensaiou os passos de expandir-contrair? O esquema evolui para sempre, e a falha que mais dói é um alter em big-bang que trava uma tabela enorme, congela o serviço e não pode ser revertido de forma limpa. Numa equipe grande o risco se multiplica porque vários serviços leem a mesma forma, então uma mudança que quebra exige que o código antigo e o novo rodem lado a lado ao longo de uma implantação em estágios. A atração concorrente é a velocidade: um único alter é rápido de escrever, enquanto expandir-contrair (acrescentar a nova forma, preencher o histórico, escrever em duplicidade, migrar os leitores, descartar a forma antiga) tem mais passos e exige mais paciência. Levem a sua maior tabela, uma estimativa honesta de quanto tempo um alter ingênuo a travaria e uma migração específica que alguém executou de ponta a ponta num ensaio e não em teoria. Em sistemas corporativos e governamentais que funcionam continuamente e carregam metas estatutárias de disponibilidade, a indisponibilidade por uma migração é uma violação, então a disciplina do expandir-contrair é o preço de ter permissão para mudar o esquema.
Quais valores em cache ou replicados, se servidos obsoletos, causariam uma falha de conformidade ou de segurança e não uma cosmética, e todo caminho de invalidação deles está testado? O cache é um problema de correção vestido de fantasia de desempenho: o perigo não é a lentidão, mas servir um direito de acesso, um saldo, um sinalizador de consentimento ou uma decisão de acesso depois de ter mudado. Para uma grande organização o risco é difuso, porque caches e camadas de borda se acumulam entre equipes e ninguém consegue listar o que está em cache onde nem quando é limpo. A tensão é real: o cache agressivo e os TTLs longos compram latência e protegem o banco de dados, enquanto a atualidade estrita custa as duas coisas. Levem um inventário dos dados em cache e servidos por CDN, marcados quanto a quais entradas carregam consequência de conformidade ou de segurança, mais evidências de que o caminho de invalidação de cada uma delas foi exercitado na prática e não meramente configurado. Em contextos regulamentados e públicos, um valor obsoleto de consentimento ou de elegibilidade é uma falha auditável, então esses itens precisam de um caminho de invalidação explícito e testado ou não devem ser colocados em cache.
Qual é a sua estratégia de retenção, arquivamento e residência de dados para cada armazenamento autoritativo, e vocês conseguem prová-la a um auditor? Os dados sobrevivem ao código e muitas vezes à equipe que o escreveu, então o crescimento sem limite e regras vagas de residência viram em silêncio o problema de que ninguém é dono até uma tabela ficar ingerenciável ou um registro estar na jurisdição errada. Uma grande organização abrange muitos armazenamentos e regiões, e as considerações concorrentes são custo (o armazenamento quente é caro, então arquive e escalone), desempenho (tabelas inchadas deixam tudo lento) e dever legal (pisos estatutários de retenção e tetos de residência que podem entrar em conflito). Levem, por conjunto de dados autoritativo, o período de retenção, onde os dados fisicamente moram, o mecanismo de arquivamento e de exclusão e o nome da pessoa responsável. Para empresas e especialmente sistemas governamentais, a retenção e a residência costumam ser mandatos legais com exigências de auditoria e de soberania, então poder provar onde mora cada registro, por quanto tempo é mantido e quando é destruído é uma licença para operar, não um requinte.
Perspectiva por setor
Startup. Rode um banco de dados e resista ao zoológico. Um único armazenamento relacional gerenciado dá transações, uma coisa para copiar e um lugar para raciocinar sobre a consistência, que é exatamente o que uma equipe de três pessoas consegue guardar na cabeça. Acrescente um cache, uma réplica de leitura ou um índice de busca apenas quando uma consulta lenta específica ou um volume real de leitura os forçar, para que a complexidade chegue com uma razão que paga. Mantenha as migrações versionadas desde o primeiro dia, porque adaptar a disciplina de migração a um produto em operação é muito mais difícil do que começar com ela.
Pequena empresa. Você não tem especialista em banco de dados nem tempo para operar vários motores, então favoreça um armazenamento gerenciado e deixe seu provedor de plataforma cuidar das cópias de segurança, das correções e da replicação. Trate a seleção do armazenamento como uma decisão de compra: escolha o motor chato e bem apoiado com o qual suas ferramentas já se integram em vez do mais rápido num benchmark. Defina uma política simples de retenção e de cópias que você consiga de fato verificar e nunca faça cache de nada ligado a dinheiro ou a permissões sem um jeito claro de limpá-lo, porque um preço ou direito de acesso obsoleto custa um cliente.
Grande empresa. O desafio central é a persistência poliglota entre muitas equipes: um sistema de registro relacional mais motores de busca, cache, warehouse e talvez de grafos ou de séries temporais, cada um de propriedade de um serviço e não compartilhado. Padronize as ferramentas operacionais, as cópias e o monitoramento entre os armazenamentos que vocês mantêm, mantenha alta a barra para a adoção de novos motores e faça das migrações expandir-contrair e da invalidação explícita de cache o padrão. Gerencie o parque como um portfólio com propriedade clara dos dados, para que nenhum motor sobreviva à carga que o justificou e nenhuma equipe fique acoplada por um banco de dados compartilhado.
Governo. A residência de dados, a retenção estatutária e a integridade demonstrável moldam toda escolha. Provisione todo armazenamento em regiões soberanas, configure as CDNs para fazer cache apenas de dados não pessoais e mantenha um histórico de auditoria imutável para registros que precisam ser reconstruíveis para os reguladores. As migrações exigidas por nova legislação precisam ser aplicadas de forma retrocompatível pelo pipeline para que o serviço continue disponível ao longo dos prazos legislativos, e o sistema de registro precisa ser identificável para que vocês possam provar qual valor é a fonte legal e reconstruir toda cópia derivada a partir dele.
Exemplos
Startup. Uma startup em estágio semente roda tudo numa única instância gerenciada de PostgreSQL e resiste à vontade de acrescentar um mecanismo de busca, um cache e um warehouse separados antes de precisar deles. Um banco de dados significa uma coisa para copiar, um lugar para raciocinar sobre a consistência e transações que simplesmente funcionam, o que importa quando a equipe inteira são três engenheiros. Elas acrescentam um cache Redis e uma réplica de leitura apenas quando uma consulta lenta específica e um volume real de leitura os justificam, de modo que a complexidade chega com uma razão que paga e não antes dela.
Grande empresa. Um banco de varejo mantém seu livro-razão autoritativo num banco de dados relacional fortemente consistente: cada lançamento é uma transação ACID de verdade, com sharding por faixa de contas para escala de escrita. Em volta dele fica um parque poliglota: um índice de busca para consulta de clientes, um cache Redis (escrita direta, TTL curto) para os resumos de conta no aplicativo móvel, um warehouse colunar para relatórios regulatórios e analíticos e um banco de grafos para detecção de fraude na rede de transações. As mudanças de esquema do livro-razão usam expandir-contrair com escritas em duplicidade, de modo que o sistema 24/7 nunca para por uma migração.
Governo. Um registro nacional de veículos guarda os registros autoritativos num sistema de registro relacional com retenção estatutária e histórico de auditoria completo. As consultas públicas “verificar um veículo” são servidas por uma réplica de leitura e por um cache de borda com TTL curto, porque dados públicos levemente obsoletos são aceitáveis e o volume de leitura faz sombra ao de escrita. A lei de residência de dados exige que todos os registros permaneçam no país, então todo armazenamento é provisionado em regiões soberanas e a CDN é configurada para fazer cache apenas de dados não pessoais. As migrações para acrescentar novos campos exigidos pela política de transportes são aplicadas de forma retrocompatível pelo pipeline, de modo que o serviço continua disponível durante os prazos legislativos.
Justificativa de negócio: motivações, ROI e TCO
As decisões de arquitetura de dados têm algumas das caudas de custo mais longas e maiores do software, porque os dados e o seu esquema são as coisas mais difíceis de mudar depois que sistemas e integrações dependem deles. O custo de adoção das boas práticas (seleção deliberada de armazenamento, migrações disciplinadas, cache projetado e sharding apropriado) é principalmente tempo de engenharia sênior e algumas ferramentas operacionais adicionais. O custo de não adotá-las aparece como um único banco de dados sobrecarregado estrangulando o negócio inteiro, replataformização de emergência quando o armazenamento errado é descoberto tarde demais, quedas prolongadas por uma migração malfeita e, o mais danoso, corrupção de dados ou uma violação de conformidade por um cache mal invalidado ou uma transação perdida.
Defenda o caso junto à liderança em termos de folga de escalabilidade, risco de incidentes e exposição regulatória. As escolhas certas de armazenamento são o que permite ao negócio crescer o volume de leitura e de escrita sem uma reescrita. As migrações disciplinadas são o que permite ao esquema acompanhar novos mandatos sem indisponibilidade. O cache correto é o que entrega experiências rápidas ao usuário sem bugs silenciosos de obsolescência. Quantifique o TCO ao longo da vida do sistema. Uma única arquitetura de dados bem escolhida evita o custo recorrente de contornar uma ruim, e um incidente de corrupção de dados evitado costuma fazer sombra ao custo inteiro de fazer bem. Em setores regulamentados, poder provar a integridade e a residência dos dados não é um centro de custos, mas uma licença para operar.
Antipadrões e armadilhas
- Banco de dados compartilhado entre serviços. Vários serviços lendo e escrevendo num só esquema, acoplando equipes e fazendo de toda mudança uma crise de coordenação.
- Um banco de dados para tudo. Forçar análise, filas, busca e transações num único motor relacional até ele colapsar.
- Migrações em big-bang. Mudanças únicas e quebradoras de esquema que exigem indisponibilidade e não podem ser revertidas com segurança.
- Cache sem estratégia de invalidação. Dados obsoletos servidos indefinidamente, ou bugs de correção porque ninguém é dono de quando o cache é limpo.
- Sharding prematuro. Distribuir os dados antes que a carga o exija, perdendo permanentemente junções e transações sem benefício.
- Ignorar a defasagem de replicação. Ler a própria escrita de uma réplica defasada e obter dados obsoletos, quebrando as expectativas do usuário.
- Crescimento ilimitado de dados. Sem estratégia de arquivamento nem de retenção, de modo que as tabelas crescem até o desempenho e o custo se tornarem insustentáveis.
- Guardar dados derivados como verdade. Tratar um cache, um índice ou uma projeção como sistema de registro e depois descobrir que derivou.
Modelo de maturidade
- Nível 1: Iniciar. Um só banco de dados é usado para todos os fins. As mudanças de esquema são manuais e ad hoc, sem disciplina de migração. O cache é incidental e a invalidação é uma reflexão tardia. Os problemas de desempenho são resolvidos de forma reativa comprando uma máquina maior, e ninguém consegue nomear com confiabilidade o sistema de registro de um dado conjunto de dados.
- Nível 2: Desenvolver. Algumas escolhas de armazenamento são deliberadas e apareceu um cache ou um warehouse, mas a prática varia por equipe. As migrações são versionadas mas às vezes exigem indisponibilidade, e o expandir-contrair é usado por quem por acaso o conhece. Vários serviços ainda compartilham um banco de dados, e as estratégias de cache diferem de uma equipe para outra.
- Nível 3: Padronizar. A persistência poliglota é ajustada às cargas de trabalho, cada armazenamento de propriedade de um serviço e nunca compartilhado. As migrações expandir-contrair automatizadas, retrocompatíveis e sem indisponibilidade são o padrão documentado e imposto em toda a organização. As estratégias de cache, os TTLs e os caminhos de invalidação são artefatos de design explícitos, e o único sistema de registro de cada conjunto de dados é documentado, com cópias derivadas reconstruíveis a partir dele.
- Nível 4: Gerenciar. O parque de dados é medido e controlado em relação a linhas de base. Você acompanha a duração das migrações e a taxa de reversão, a defasagem de replicação contra as exigências de ler-suas-escritas, a taxa de acerto de cache e os incidentes de obsolescência, o custo operacional por armazenamento e a latência das consultas nos percentis-alvo e então age sobre os números. A retenção e a residência são auditadas contra as exigências estatutárias, a correção dos dados derivados é verificada continuamente e todo motor precisa justificar o custo contra a carga que atende.
- Nível 5: Orquestrar. A arquitetura de dados é continuamente melhorada e integrada ao planejamento de capacidade, de custo e de risco em toda a organização. As escolhas de sharding, de cache e de consistência são reequilibradas por carga de trabalho conforme os padrões de acesso e o custo mudam, e os armazenamentos que já não merecem seu lugar são aposentados por migrações planejadas. A evolução do esquema, o arquivamento e a residência são totalmente automatizados e adaptativos, de modo que o parque se remodela a novos mandatos e cargas sem replataformização de emergência.
Ideias para discussão
- Quais dos seus armazenamentos atuais estão fazendo um trabalho para o qual não foram projetados, e qual seria o motor certo?
- Vocês conseguem fazer hoje uma mudança de esquema na sua maior tabela com indisponibilidade zero? Se não, por quê?
- Onde o seu sistema faz cache de dados cuja obsolescência poderia causar uma falha de conformidade ou de correção?
- Quais serviços compartilham um banco de dados, e o que seria preciso para dar a cada um o seu?
- Onde um único banco de dados gravável é o seu teto de escala, e a replicação de leitura ou o sharding é o próximo passo certo?
- Qual é a sua estratégia de retenção e arquivamento, e quem é responsável por ela?
Principais conclusões
- Os dados sobrevivem ao código. As decisões de armazenamento e de modelagem restringem o negócio durante toda a vida do sistema.
- Ajuste cada carga de trabalho ao paradigma de armazenamento que serve ao seu padrão de acesso. Espere persistência poliglota em escala.
- Dê a cada serviço a propriedade dos seus dados. Nunca acople equipes por um banco de dados compartilhado.
- Trate a evolução do esquema como contínua e use migrações expandir-contrair retrocompatíveis e sem indisponibilidade.
- O cache é um problema de correção: projete deliberadamente os TTLs, a invalidação e a proteção contra debandadas e nunca faça cache de dados críticos para a conformidade sem um caminho de invalidação testado.
- Compre apenas a consistência e as garantias transacionais de que cada carga de trabalho precisa. O sharding e a replicação trocam transações entre partições por escala.
Referências e leitura complementar
- Martin Kleppmann, Designing Data-Intensive Applications
- Pramod Sadalage and Martin Fowler, NoSQL Distilled
- Pramod Sadalage and Scott Ambler, Refactoring Databases: Evolutionary Database Design
- C. J. Date, An Introduction to Database Systems
- Joe Celko, SQL for Smarties
- Vlad Mihalcea, High-Performance Java Persistence (transactions, isolation, concurrency)
- Eric Evans, Domain-Driven Design (bounded contexts and data ownership)
- Werner Vogels, “Eventually Consistent”