Um dos desafios ao lidar com um novo design de banco de dados é que você não sabe coisas como o tamanho das tabelas até que elas sejam realmente preenchidas com uma quantidade razoável de dados. Mas se o design tiver que levar em consideração eventuais preocupações de escalabilidade, você não poderá implantá-lo para obter esses dados até que a estimativa seja feita. Uma maneira de contornar isso é prototipar agressivamente as coisas. Use hardware de teste para esse propósito, para que novos aplicativos possam viver temporariamente enquanto classificam detalhes como esse. Você pode apenas considerar que precisará mover o aplicativo e, possivelmente, redesenhá-lo após alguns meses, quando tiver uma ideia melhor de quais dados serão exibidos nele.
A outra maneira de contornar esse problema da galinha/ovo é escrever um gerador de dados. Construa manualmente dados de amostra suficientes para ver como é, quão denso é e como seus valores são distribuídos. Em seguida, escreva algo que pegue essas estatísticas e produza um conjunto de dados maior semelhante a ele. Você nunca vai conseguir que isso seja perfeito, mas não precisa ser. Gerar conjuntos de dados gigantes, mesmo com algumas falhas, ainda é a melhor maneira disponível para fazer estimativas de tamanho de banco de dados. Existem tantas fontes de sobrecarga que é difícil explicar que qualquer tabela medida e tamanhos de índice, com base em algo como seus dados, serão muito mais precisos do que qualquer outra abordagem. Há uma razão pela qual acabo respondendo muitas perguntas sobre questões relacionadas ao desempenho usando o pgbench para criar primeiro um banco de dados do tamanho apropriado.
A geração de dados não é fácil. Gerar timestamps realistas em particular é sempre irritante. E não importa o quão eficiente você acha que os escreveu, eles geralmente levam mais tempo do que você espera para serem executados – e ainda mais para obter os dados resultantes em um banco de dados e indexados corretamente.
E esse continua sendo o caso, não importa quantas vezes você tenha feito isso, porque mesmo que você faça tudo certo, a Lei de Murphy se intrometerá para tornar o trabalho doloroso de qualquer maneira. Todos os meus computadores em casa são construídos com hardware de PC relativamente barato. Não é o material mais barato disponível – eu tenho padrões – mas certamente não usando a mesma qualidade que eu recomendaria que as pessoas procurassem em um servidor. O problema de geração de dados desta semana lembrou por que um hardware melhor vale quanto custa para o trabalho crítico de negócios.
Depois de gerar alguns bilhões de linhas de dados e observar essa importação por 10 horas, não fiquei satisfeito em ter todo o trabalho abortado assim:
psql:load.sql:10: ERROR: invalid input syntax for type timestamp: "201^Q-04-14 12:17:55"
CONTEXT: COPY testdata, line 181782001, column some_timestamp: "201^Q-04-14 12:17:55"
Acontece que, em algum lugar no meio da gravação dos 250 GB de dados de teste que gerei, uma das linhas de saída estava corrompida. Dois bits foram invertidos e os dados escritos estavam errados. Eu não sei onde isso aconteceu com certeza.
A memória é o suspeito mais provável. Servidores reais usam RAM ECC e por boas razões. Se você monitorar um sistema com muita RAM, o número de erros corrigidos silenciosamente pelo ECC pode ser chocante. A RAM que uso em casa é boa, mas as taxas de erro em qualquer memória sem recursos de detecção/correção de erros serão mais altas do que você gostaria – e nunca detectadas, a menos que aconteçam no código que leva a uma falha do sistema. O trabalho de geração de dados é bom para expor esses problemas, porque normalmente coloca pelo menos uma CPU em seu servidor sob carga pesada por potencialmente dias seguidos. Se houver alguma instabilidade em seu sistema, aquecer o sistema e deixá-lo funcionar por um longo tempo irá agravá-lo.
Uma segunda camada de proteção contra esse tipo de corrupção é colocar somas de verificação nos dados que estão sendo gravados no disco, para proteger contra erros de gravação e, em seguida, ler os dados novamente. A soma de verificação de bloco feita pelo sistema de arquivos ZFS é uma das melhores implementações disso. No meu caso, usar ou não o ZFS pode não ter feito diferença. Se os bits fossem invertidos antes que a soma de verificação do bloco acontecesse, eu teria escrito dados inúteis - com uma soma de verificação indesejada para corresponder - independentemente.
Meu próximo passo foi usar o split utilitário para pegar meu arquivo gigante e quebrá-lo em pedaços menores – mais algumas horas para esperar que isso termine. Então eu poderia começar a carregar os arquivos bons enquanto corrigia o ruim.
Dado que os arquivos resultantes tinham 13 GB cada e meu único servidor tem 16 GB de RAM, embora eu pudesse corrigir esse erro de digitação de um caractere usando o vi. Teoricamente, esse deveria ser o caso, mas estou começando a ter minhas dúvidas, devido a quanto tempo estou esperando o arquivo ser escrito novamente depois:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
21495 gsmith 20 0 8542m 8.3g 1544 R 98 53.0 419:56.70 vi datafile-ag
São 7 horas sólidas que estou esperando apenas para que esse erro de digitação de um caractere seja corrigido, para que eu possa terminar de carregar os dados de teste. Como eu estava dizendo, a geração de dados séria nunca é fácil.