Carregando 1 trilhão de dados meteorológicos no TimescaleDB
(aliramadhan.me)- Para analisar rapidamente mudanças históricas no clima global, é preciso colocar os dados de reanálise ERA5 no PostgreSQL/TimescaleDB, o que chega a cerca de 754 bilhões de linhas dependendo das variáveis e da grade escolhidas
- O ERA5 fornece resolução horária desde 1940 e uma grade de latitude/longitude de 0,25 grau, mas na estrutura NetCDF até a extração de séries temporais longas de um único ponto pode levar de 20 a 30 minutos
insertde linha única alcança cerca de 3 mil linhas por segundo, o que exigiria cerca de 8 anos para carregar tudo; mesmoinsertmultirlinha, no psycopg3, fica em 25 mil a 30 mil linhas por segundo, algo em torno de 10 meses- O
copydo PostgreSQL e ocursor.copy()do psycopg3 diferem conforme a disponibilidade prévia de CSV, mas incluindo overhead ficam em torno de 100 mil linhas por segundo, com espaço para aumentar via paralelismo e ajuste de configuração - Ao inserir direto de um dataframe, a melhor opção é
copydireto para hypertable com psycopg3; se o CSV já existir,timescaledb-parallel-copyé mais adequado, e neste sistema 12 a 16 workers parecem ser o ponto ideal de paralelização
Construindo um data warehouse meteorológico global
- O objetivo é criar um data warehouse capaz de consultar dados meteorológicos históricos do mundo inteiro para analisar sinais de mudança climática que já aconteceram
- As análises de exemplo incluem verificar se Jacarta realmente ficou mais quente ou passou a ter tempestades com mais frequência, se o Chile como um todo ficou mais quente ou mais nublado, e que forma essas mudanças assumem por região
- Para fazer análises globais rápidas, as consultas do data warehouse precisam ser rápidas, e o primeiro passo é carregar um grande volume de dados no banco
- A base é PostgreSQL; para acelerar consultas temporais, TimescaleDB é usado, e para futura aceleração de consultas geoespaciais, PostGIS é uma opção promissora
Dados de reanálise ERA5 e a escala de 754 bilhões de linhas
- Em vez de observações meteorológicas reais, são usados dados de reanálise climática ERA5
- Observações históricas podem ser escassas em certas regiões e períodos
- O ERA5 é a saída de um modelo climático restringido para se ajustar às observações, sendo amplamente usado em pesquisa meteorológica e climática
- O ERA5 cobre o planeta inteiro com resolução de 0,25 grau e está disponível com resolução horária desde 1940
- Há 727.080 snapshots temporais por variável
- Existem 1.038.240 pontos de grade, compostos por 1.440 longitudes e 721 latitudes, incluindo os polos
- Ao indexar por tempo e localização, isso resulta em 753.836.544.000 linhas por variável, cerca de 754 bilhões de linhas
- As variáveis carregadas são temperatura, velocidade do vento zonal e meridional a 10 m, cobertura total de nuvens, precipitação e neve acumulada
- A tabela inclui
time,location_id,latitude,longitudee colunas para cada variável meteorológicalocation_ide latitude/longitude são mantidos juntos para futuros benchmarks de consulta e índice
Onde a estrutura dos arquivos NetCDF fica lenta
- O ERA5 é distribuído em arquivos NetCDF, normalmente organizados por dia, mês ou ano
- A estrutura em chunks por tempo é rápida e simples para consultar dados de um instante específico
- Para observar padrões temporais, como uma série histórica longa de um ponto, é preciso ler muitos arquivos, o que torna tudo lento
- No exemplo, extrair a série de temperatura de um local leva de 20 a 30 minutos
- Consultas geoespaciais complexas, especialmente as que incluem o eixo temporal, são lentas e difíceis de executar
- Ferramentas como xarray, dask e Pangeo podem acelerar esse processo, mas ele ainda continua lento
insert: de linha única a múltiplas linhas
- A forma mais simples é inserir uma linha por vez com
insert insertde linha única envolve vários custos- O PostgreSQL precisa analisar a instrução, validar nomes de tabela e coluna e montar um plano de execução
- Pode ser necessário bloquear recursos para garantir integridade dos dados
- Os dados são gravados em buffer por causa do WAL (write-ahead logging)
- Os dados são então inseridos na área em disco da tabela
- No commit da transação, a alteração é tornada permanente
- Em Python, foram comparadas três formas de inserção de linha única
- uso de
chunksize=1empandas df.to_sql() - consulta parametrizada com psycopg3
- consulta parametrizada com SQLAlchemy
- uso de
- Nos resultados, psycopg3 ficou ligeiramente à frente e SQLAlchemy foi o mais lento
- A hypertable do TimescaleDB foi um pouco mais lenta que uma tabela PostgreSQL comum
- O melhor desempenho ficou em cerca de 3 mil linhas por segundo, o que significa quase 8 anos para carregar o conjunto inteiro
- O
insertmultirlinha coloca várias linhas numa única instrução, reduzindo ida e volta de rede, parsing e custo de planejamento- O psycopg3 foi o mais rápido, com 25 mil a 30 mil linhas por segundo
- O pandas pode ser mais lento por usar dicionários em vez de tuplas na inserção
- O SQLAlchemy pode ter overhead extra com gerenciamento de sessão e abstração de expressões SQL
- Mesmo assim, o carregamento completo ainda exigiria cerca de 0,8 ano, quase 10 meses
copy: o caminho de carga em massa do PostgreSQL
- O
copydo PostgreSQL é um recurso de carga em massa que lê linhas de arquivos CSV ou binários - Como é otimizado para carga em massa, ele ajusta parsing, planejamento e uso de WAL para ser mais rápido que
insertmultirlinha - Foram comparados dois caminhos
- salvar os dados NetCDF em CSV e depois carregá-los com
copy - fazer streaming direto para o PostgreSQL com
psycopg3 cursor.copy(), sem criar arquivo CSV
- salvar os dados NetCDF em CSV e depois carregá-los com
- Quando o CSV já está pronto, o
copyalcança velocidade próxima de 400 mil linhas por segundo - Incluindo o overhead de gravar o CSV ou gerar tuplas, tanto
copyquanto psycopg3 ficam em torno de 100 mil linhas por segundo, com psycopg3 ligeiramente à frente - Mesmo nessa velocidade, o carregamento de tudo ainda levaria cerca de 3 meses
Taxa sustentada de carga e copy paralelo
- Ao inserir muitas linhas, surgem gargalos como escrita em disco, disputa de I/O entre WAL e inserção na tabela, autovacuum e checkpoint
- Em um teste que carregou cerca de 772 milhões de linhas em 744 lotes, não houve grande queda de velocidade com um único worker
copy csvmostrou quedas frequentes e maior sensibilidade a variações- O psycopg3 foi em geral mais rápido
- A diferença entre tabela comum e hypertable não foi grande
- Foram executados em paralelo vários trabalhos de
copyou múltiplos cursores psycopg3 com joblib - Inserir em uma única tabela não é uma carga que escala tão bem em paralelismo, e o desempenho em geral estagna depois de 16 workers
pg_bulkload e timescaledb-parallel-copy
- Além do
copydo PostgreSQL, também foram benchmarkados pg_bulkload e timescaledb-parallel-copy - O pg_bulkload parece mais rápido na configuração padrão, mas por padrão ele contorna shared buffers e pula o WAL logging, o que pode impedir recuperação de dados após falhas
- Em condições equivalentes com
fsyncdesativado, o timescaledb-parallel-copy com vários workers supera o pg_bulkload - O timescaledb-parallel-copy permite definir o número de workers para inserção paralela
- O desempenho inicial é bom, mas neste sistema o gargalo aparece antes de 100 milhões de linhas, a taxa de inserção despenca e depois se recupera em ondas
- A taxa sustentada fica em cerca de 600 mil a 700 mil linhas por segundo em tabela comum, e cerca de 300 mil linhas por segundo em hypertable
- O pg_bulkload não permite definir número de workers, mas usa múltiplas threads para leitura, parsing e escrita com a opção
writer=parallel
Ajustes de configuração do PostgreSQL e compromissos de durabilidade
- Dá para ganhar desempenho extra ajustando configurações não duráveis do PostgreSQL
- As principais são desativar
fsyncpara evitar flush em disco e desativarfull_page_writespara evitar proteção contra escrita parcial de página - Essas configurações podem colocar em risco a integridade do banco em caso de falha
- Tabelas unlogged não geram WAL e, por isso, escrevem mais rápido, mas podem ser truncadas na recuperação após crash
- Depois é preciso convertê-las para tabelas logged, e esse processo pode ser lento e de thread única
- Hypertables não podem ser unlogged, então se a hypertable for necessária será preciso uma conversão ou migração adicional
Escolha final: inserir diretamente na hypertable
- Se o objetivo final é uma hypertable, inserir primeiro numa tabela comum e depois converter é mais lento do que inserir direto na hypertable
- Em um teste simples com cerca de 772 milhões de linhas usando
copydo psycopg3 e 16 workers, a inserção direta na hypertable levou menos tempo do que inserir na tabela comum e depois converter- Nesse caso, a inserção direta na hypertable levou aproximadamente 80% do tempo da abordagem com conversão posterior
- O processo de conversão ou migração não é rápido e parece funcionar em thread única
- A recomendação final é a seguinte
- Se for carregar direto de um dataframe, use
copydo psycopg3 diretamente na hypertable - Se o arquivo CSV já existir, use
timescaledb-parallel-copy - Neste sistema, 12 a 16 workers parecem ser o ponto ideal de paralelização
- Se for carregar direto de um dataframe, use
Conclusões gerais dos benchmarks e tempo necessário
- Com as proteções ativadas e um único worker, o limite superior da taxa sustentada, incluindo overhead, parece ser de cerca de 140 mil linhas por segundo neste hardware
- Com vários workers, usando
copyvia cursor do psycopg3 e mantendo as proteções, a taxa sustentada pode chegar a cerca de 250 mil linhas por segundo - O processo de inserção não escala tão bem com paralelismo, e a faixa adequada parece estar entre 4 e 16 workers
- Assumindo o risco e desativando
fsync, é possível sustentar cerca de 462 mil linhas por segundo com psycopg3 - O pg_bulkload desativa
fsyncpor padrão, então é preciso cuidado ao usá-lo - Mantendo cerca de 462 mil linhas por segundo, carregar cerca de 754 bilhões de linhas levaria aproximadamente 20 dias
Código e ambiente de benchmark
- O código para download do ERA5, criação de tabelas, inserção e
copy, benchmarks e geração de gráficos está em timescaledb-insert-benchmarks - Para cada benchmark, um novo contêiner Docker é iniciado para manter um ambiente consistente
- O armazenamento não é preservado entre contêineres Docker
- Os arquivos NetCDF e CSV são lidos de HDD
- O banco de dados é armazenado em SSD NVMe
- Configuração de hardware
- CPU: 2x Intel Xeon Silver 4214 de 12 núcleos
- RAM: 16x 16 GiB Samsung ECC DDR4 2666 MT/s
- SSD: Intel NVMe de 2 TB
- HDD: Seagate Exos X16 14TB 7200 RPM
- Configuração de software
- Ubuntu 20.04, Linux kernel 5.15
- PostgreSQL 15.5
- TimescaleDB 2.13.0
- pg_bulkload 3.1.20
- A configuração do PostgreSQL usa os valores recomendados pelo
timescaledb-tunepara 250,57 GB de memória e 48 CPUs, com ajuste separado do tamanho do WAL durante os benchmarks
2 comentários
A opinião do GN+ está em linguagem formal..?
Opiniões do Hacker News
Já trabalhei bastante com análise geoespacial profissionalmente, e dados geoespaciais são muito mais sutis do que parecem, porque é preciso entender corretamente o sistema de referência de coordenadas (CRS) e a projeção de visualização
Sem uma infraestrutura que carregue os metadados corretamente junto com os dados, o CRS vira uma fonte constante de preocupação
Já usei os recursos relacionados da AWS, Postgres/PostGIS, Spark/Databricks, Snowflake, Trino e ArcGIS, mas, para trabalhos geoespaciais em grande escala, acho que o Google BigQuery é disparado o melhor
Uma consulta que levava horas em PostGIS num EC2 m6a enorme e custava caro levou menos de 5 segundos no nível gratuito do BigQuery
Usei dados públicos da FEMA, e o Snowflake e os serviços da AWS travaram porque a coluna geometry excedia o tamanho máximo em bytes; o Spark nem tem tipo de dados geoespaciais, e as extensões open source também deixavam a desejar
Em um ambiente on-premises, a situação pode ser diferente, mas, para 20 TB, o custo de armazenamento do BigQuery provavelmente fica abaixo de US$ 100 por mês, então eu não teria muita vontade de operar isso por conta própria
Isso foi possível porque usei índices H3 no início e todos os datasets intermediários cabiam em memória
A configuração é um Mac Studio com 128 GB + Asahi Linux + arquivos Parquet via mmap + DuckDB; também rodo Airflow e uso Nix para acelerar builds de desenvolvedores e executar jobs do Airflow do time de dados
O GCP é ótimo quando é gratuito ou barato, mas pode te surpreender em algum momento com uma fatura maior, mesmo que o uso não aumente
Queria saber se você comparou com o Apache Sedona e, especificamente, o que estava faltando
Trabalho na Wherobots, fundada pelos criadores do Apache Sedona, então gostaria de ouvir esse feedback
https://sedona.apache.org/latest/
https://wherobots.com/
Concordo que colunas muito largas quebram muitas ferramentas, mas acho que outros derivados de Postgres orientados a colunas conseguiriam dar suporte sem problemas
Acho curioso que você aparentemente não tenha usado o Redshift, que compete diretamente com o BigQuery, e a coluna
superdo Redshift pode ser configurada para ser ainda maior que o limite máximo do BigQueryVejo com frequência pessoas descobrindo, do jeito difícil, que é difícil superar o PostGIS
O fato de Trino/Presto e Spark terem estagnado nessa área também é especialmente revelador
No Postgres, fazíamos cache de parte das tabelas grandes, enquanto no BigQuery reconstruíamos tudo do zero, e ainda assim foi esse o resultado
Entre as ferramentas que dá para operar por conta própria, gosto mais do Postgres, mas é difícil contestar uma melhora de desempenho de múltiplos de um dígito
Texto realmente excelente
Trabalho com DevRel na Timescale, e é ótimo ver a comunidade produzindo um texto tão bem feito assim
Um dos motivos pelos quais a hypertable ficou mais lenta é quase certamente o fato de ela criar, por padrão, um índice na coluna de timestamp
A tabela comum não tinha índice, então é possível que por isso tenha sido mais rápida
Em
create_hypertable, dá para usarcreate_default_indexes=>falsepara pular a criação do índice, e também dá para excluir o índice antes de inserir os dadosNo fim, esse índice será necessário, mas, para cargas em lote como essa, é melhor criá-lo de uma vez depois da carga
Também fico curioso sobre até que ponto o HDD que lê os dados aguenta em uma configuração com alto paralelismo
create_default_indexes=>falseexistia nem que a hypertable cria por padrão um índice de tempo, e vou adicionar uma observação explicando essa parteTambém quero fazer um benchmark inserindo sem índice de tempo e depois criando o índice manualmente
Mesmo com 32 workers, o HDD pareceu ficar bem
Ao olhar o uso de disco no btop, o SSD onde o Postgres estava instalado parecia mais próximo de ser o gargalo do que o HDD, então concluí que trocar o SSD do Postgres por um mais rápido seria um investimento melhor do que mover os dados do HDD para o SSD
Não entendo por que fazer isso desse jeito
A maioria dos datasets de tempo e clima, incluindo o ERA5, é altamente estruturada sobre uma grade regular de latitude-longitude
Mesmo que se extraia apenas a série temporal de um local específico, a força desses datasets está na estrutura e no contexto espaço-temporais incorporados; se o objetivo não for apenas extrair séries temporais pontuais, não faz muito sentido destruir completamente essa estrutura
Mesmo no caso de extrair só séries temporais pontuais, por exemplo, uma série temporal de temperatura de superfície no meio do oceano quase nunca teria uso, então provavelmente você iria querer reduzir os dados de forma bastante agressiva
Para a maior parte dos usos de pesquisa e operação de datasets como o ERA5, parece mais adequado usar uma réplica otimizada para nuvem que preserva a estrutura original, como o ARCO-ERA5 do Google Public Datasets
Essas versões mantêm a estrutura original e, ao mesmo tempo, dividem os chunks para acesso paralelo em larga escala a partir do armazenamento em nuvem
Em quase todos os casos que vi na minha carreira, um arquivo baseado em Zarr com chunking comum já era rápido o bastante para a maioria dos usos de interesse
https://cloud.google.com/storage/docs/public-datasets/era5
Dito isso, como rabernat comentou, extrair séries temporais longas das réplicas em nuvem também é lento
No fim, também quero fazer consultas espaço-temporais complexas, como calcular o percentil 99 das temperaturas de verão no Chile entre 1940 e 1980
Não duvido que a réplica em nuvem possa ser mais rápida, mas isso entra em conflito com um orçamento de US$ 0
Eles são otimizados para consultas espaciais, não para consultas de séries temporais
Acabei de rodar um benchmark e levou 20 minutos para buscar a série temporal de uma única variável em um único ponto
Isso mostra bem que, se o padrão de uso esperado é série temporal, é preciso um chunking otimizado para séries temporais
Alguns laboratórios já têm pipelines baseados em RDBMS que combinam algoritmos e dados publicados, ninguém quer reimplementar, e não há orçamento para isso
A melhor melhoria que conseguimos foi mais ou menos migrar de um MySQL antigo para Postgres + PostGIS
Timescale também teria ajudado, acho
Também havia motivos para rodar localmente, como privacidade, acesso a cluster e orçamento
Bom artigo
O que acho que falta aqui é uma análise do que se ganha ao mover dados meteorológicos para um banco de dados relacional
A motivação é aumentar a velocidade das consultas, mas é preciso ter uma linha de base
Como mantenedor do Xarray e do Zarr e fundador da https://earthmover.io/, conheço bem esse cenário tecnológico; com os dados devidamente particionados em chunks no Zarr, é possível processar consultas de séries temporais em dados meteorológicos com latência abaixo de 1 segundo usando apenas uma solução serverless + armazenamento de objetos
Muito mais rápido do que os 30 minutos mencionados no artigo
Considerando a dificuldade de carga de dados mostrada no texto, vale avaliar seriamente esse tipo de solução antes de seguir pelo caminho do RDBMS
Também dá para colocar uma API na frente e fazer parecer uma consulta
Também é verdade que eu não justifiquei suficientemente a abordagem com RDBMS
O principal motivo de eu ter escolhido Postgres + Timescale foi querer aprendê-los, e brincar com dados ERA5 parecia o mais divertido
Acho que também havia o atrativo de os dados meteorológicos serem grandes o bastante para representar um desafio
Sem base concreta, fico curioso se um TimescaleDB + PostGIS bem ajustado e indexado ajudaria em consultas espaço-temporais complexas, como o percentil 99 das temperaturas de verão no Chile entre 1940 e 1980
Nesse caso, talvez fosse necessário ler vários chunks Zarr
Também gosto da ideia de ter uma tabela separada para armazenar esse tipo de estatística em cache, embora isso também não seja tão difícil com Zarr
Na próxima etapa pretendo fazer benchmark de consultas e índices, então devo aprender mais
Parece haver uma tensão fundamental: chunks grandes são bons para visualização de áreas amplas e consultas grandes, enquanto chunks pequenos são bons para consultas pontuais ou de séries temporais
Manter versões separadas desses grandes datasets geoespaciais com chunkings diferentes é possível, mas não é muito eficiente em termos de custo
Ouvi dizer que o
kerchunké usado para tentar obter o melhor dos dois mundos, mas acho que com isso se perdem opções de compressão dos dados e a complexidade aumenta bastanteTenho curiosidade sobre qual é a melhor forma de equilibrar esses diferentes casos de uso
Contribuí para a documentação de carregamento em massa do Postgres referenciada aqui, e é um texto que passa bem por várias técnicas
Fiz bastante esse tipo de trabalho para acelerar o carregamento do banco de dados do OpenStreetMap, e a última atualização pública está em https://www.youtube.com/watch?v=BCMnu7xay2Y
Desde então, graças à evolução do hardware, às melhorias de GIS no PG15 e à adoção da técnica middle-way-node-index-id-shift pelo osm2pgsql, o tempo de carregamento do conjunto planet caiu para menos de 4 horas
Como sugestão ao autor, alguns experimentos estão eliminando indiretamente as escritas de WAL via pg_bulkload e COPY
Quando Craig Ringer escreveu o post linkado no SO, isso não era documentado, mas dá para simplesmente desligar o WAL na configuração
Claro que, se houver uma falha, você perde a tabela em andamento, e se o trabalho levar semanas isso pode acontecer
Mas, com dados de séries temporais, não é difícil estruturar o carregamento de modo que só o último chunk seja perdido
Para carregamento em massa, os dados de WAL praticamente não são necessários
Se houver uma falha, basta limpar a extremidade direita dos dados carregados e recomeçar
As configurações de
postgresql.confque uso para desligar o WAL e outras sobrecargas são estas:wal_level = minimalmax_wal_senders = 0synchronous_commit = offfsync = offfull_page_writes = offautovacuum = offcheckpoint_timeout = 60minPor fim, para reduzir o trabalho de vacuum ao carregar chunks grandes, normalmente desligo o autovacuum como acima e executo periodicamente
VACUUM FREEZEatrás da partição de data que está sendo carregadaIsso é para PG comum, e permite pular parte do trabalho em que o banco se preocupa com estados intermediários nos quais novas transações foram gravadas, mas ainda não estão visíveis para todos
Especialmente no carregamento por chunks, é bom ouvir de um especialista que os dados de WAL não são estritamente necessários para carga em massa
Ainda não tenho UPS, mas espero conseguir passar os cerca de 20 dias de carregamento de dados sem queda de energia; no pior caso, acho que é só continuar de onde parou
Se o OP for o autor, talvez um experimento parecido que fiz uns 4 anos atrás também seja interessante
Era o mesmo conjunto de dados, o mesmo alvo e objetivos parecidos
https://rdrn.me/optimising-sql/
É uma linha de investigação parecida, mas usei Postgres comum em vez de Timescale e, na minha configuração, assumindo que os dados já estavam em memória, a cópia direta de dados binários ficou cerca de 3 vezes mais rápida que COPY
Deixei uma nota de rodapé explicando por que não tentei COPY binário: basicamente porque outra pessoa disse que o desempenho era decepcionante
Ainda assim, acho que preciso testar eu mesmo
https://aliramadhan.me/2024/03/31/trillion-rows.html#fn:copy-binary-note
Também gostei do formato com notas nas margens
Foi útil você fornecer uma função que escreve arrays estruturados do numpy como binário do Postgres; antes eu não tinha conseguido descobrir isso
“Será que um banco de dados relacional é mesmo adequado para dados meteorológicos em grade? Não sei, mas vou descobrir testando.”
Gosto dessa atitude
É o oposto do estilo “na verdade, é o seguinte” de muitos textos técnicos mainstream, e isso me agrada
Também gosto de como ele mantém o leitor preso até o fim acompanhando a jornada
Pesquisei bastante, mas não encontrei uma resposta definitiva para o meu caso de uso, então decidi rodar meus próprios benchmarks
Texto interessante
A parte “a uma taxa sustentada de cerca de 462 mil inserções por segundo, levaria uns 20 dias para cerca de 754 bilhões de linhas, o que não parece ruim. É menos tempo do que levou para escrever este post.” é engraçada
Também estou pendendo a escrever posts de blog mais longos e aprofundados, então me identifico com o fato de que dá muito mais trabalho do que parece
Se você quiser gerar gráficos de séries temporais ou vários gráficos diretamente a partir de consultas SQL, o qStudio é uma IDE SQL gratuita e funciona com vários bancos, incluindo TimescaleDB
https://www.timestored.com/qstudio/database/timescale
Aviso: fui eu que criei a ferramenta
Pensei que talvez desse para adicionar suporte ao Timeplus
O Timeplus é um banco de dados streaming-first baseado no ClickHouse, e o Timeplus Proton, seu mecanismo principal de banco de dados, é open source
Como o qStudio também é open source e escrito em Java, parece que um driver JDBC seria necessário para dar suporte a um novo RDBMS
Se for isso mesmo, o Timeplus Proton tem um driver JDBC open source baseado no driver do ClickHouse, com modificações para uso em streaming
https://www.timeplus.com/
https://github.com/timeplus-io/proton
https://github.com/timeseries/qstudio
https://github.com/timeplus-io/proton-java-driver
Por enquanto só fiz inserção de dados, mas em breve pretendo testar consultas e plotagem
Eu também uso dados de reanálise ERA5 e preciso de séries temporais rápidas
Como os dados vêm em uma grade
[lat, lon]acumulada por período selecionado, por exemplo no formato[dados horários de um mês, lat, lon], se você quiser mais de 20 anos isso vira um enorme problema de transposição de matrizO meu método é baixar cada arquivo netCDF, transpor e então colocá-lo em um enorme arquivo HDF 3D estruturado como
[lat, lon, hour]Na minha workstation, leva cerca de 30 minutos para gerar um ano de uma única variável, mas depois buscar uma única posição
(lat, lon)leva milissegundosÉ uma abordagem em que você sofre no começo e colhe os benefícios no longo prazo
É simples, mas eu sou apenas climatologista, não especialista em bancos de dados
Pelo que parece, rabernat e open-meteo, que comentaram aqui, também usam uma abordagem parecida e a consideram rápida