DuckDB, in-memory, mais pas totalement !
On plonge dans les arcanes de DuckDB pour comprendre comment il utilise en complément de la mémoire le disque dont il dispose. Et il semble être assez gourmand !
Contexte
DuckDB est souvent présenté comme un moteur SQL “in-memory”, reconnu pour sa légèreté et son besoin réduit en infrastructure. Pour un projet client, nous avons mis en place l’architecture suivante :
• Les données sont stockées sous forme de fichiers Parquet dans un espace de type S3.
• Des traitements sont effectués pour mettre à jour et optimiser ces fichiers pour la consommation.
• Une application Streamlit consomme ensuite ces mêmes fichiers Parquet
Le tout est déployé sur une plateforme “Datatask”, permettant une orchestration des traitements sur des ressources totalement serverless.
Cependant, lors de l’exécution des traitements, un problème de saturation du disque du worker est apparu, causant un échec. Pour pallier cela, nous avons redimensionné le worker afin de nous assurer que l’ensemble du dataset (environ 15 à 20 Go de fichiers Parquet) puisse être entièrement chargé en mémoire, ce qui, en théorie, aurait du résoudre tous les problèmes. Malgré cela, nous avons constaté que la mémoire n’était pas saturée. En revanche, l’espace disque se remplissait rapidement, atteignant plus de 30 Go via des fichiers temporaires qui continuaient de croître tout au long de la session.
Expérimentations
Pour approfondir ce problème, j’ai isolé un seul dataset du processus global. Il s’agit d’un fichier Parquet de 6,3 Go contenant 95 millions de lignes, qui occupe 40 Go une fois converti en CSV. Le traitement consiste à lire les fichiers Parquet, à ajouter un fichier CSV à la table créée, puis à sauvegarder le tout sous forme de nouveaux fichiers Parquet.
Solution
Lors de l’utilisation d’une base de données “in-memory”, environ 31 Go de fichiers temporaires sont créés sur le disque au fur et à mesure du traitement, qui ne dure que 2 minutes. Nous observons également que la mémoire utilisée ne dépasse pas les 6,5 Go, tandis que les 12 cœurs de la machine sont utilisés à 100%.
# Fichiers temporaires :
3.9G duckdb_temp_storage-0.tmp
5.3G duckdb_temp_storage-1.tmp
3.9G duckdb_temp_storage-2.tmp
7.8G duckdb_temp_storage-3.tmp
11G duckdb_temp_storage-4.tmp
Cette création excessive de fichiers temporaires s’explique par le fait que DuckDB sérialise les données dans des fichiers Parquet immuables. Cette approche présente l’avantage de la portabilité et de la standardisation, facilitant ainsi l’interopérabilité. Cependant, si nous utilisons une base de données DuckDB stockée sous forme de fichier pour effectuer les transformations, les données peuvent être stockées de manière mutable.
Le résultat est immédiat : un fichier “base.duckdb” de 9,5 Go, et seulement 3 Go de fichiers temporaires sont créés durant l’opération. Un autre avantage est la possibilité de travailler en micro-batches, en fermant DuckDB entre les étapes pour supprimer les fichiers temporaires, sans ajouter de temps de traitement supplémentaire.
# base de donnée :
9.5G Base.duckdb
0B Base.duckdb.wal
# fichiers temporaires :
1.0G duckdb_temp_storage-0.tmp
1.9G duckdb_temp_storage-1.tmp
A retenir !
DuckDB est un moteur SQL “in-memory”, mais l’utilisation du disque est parfois inévitable, notamment lors de traitements intensifs avec des volumes de données importants. Comme démontré dans nos tests, les fichiers temporaires peuvent rapidement occuper de l’espace disque.
Cependant, en ajustant la stratégie de stockage, en passant des fichiers Parquet immuables à une base DuckDB mutable, nous avons pu réduire significativement l’utilisation du disque tout en maintenant de bonnes performances. Cette approche permet également de travailler par micro-batches, libérant les fichiers temporaires entre les étapes sans allonger le temps de traitement.