Ce qui se passe
Billet invité d'un utilisateur sur le blog de DuckDB, avec Geertjan Wielenga et Alex Kasko. Son équipe stocke des données de vulnérabilités dans MongoDB ; l'écran d'analyse prenait des dizaines de secondes, l'export Excel de 50 000 lignes deux à trois minutes. Objectif : toute requête en moins d'une seconde, l'export en moins de dix. Solution retenue : copier un an de données dans DuckDB, sur le même serveur.
Les requêtes GROUP BY et COUNT sont tombées sous 100 ms, l'export sous
5 s. Le vrai travail a été l'import.
Le banc d'essai
Jeu anonymisé et publié : 485 168 enregistrements, 24 champs, dont deux textes longs (médianes de 5 000 et 2 500 caractères, jusqu'à 32 000). Import découpé par journée, huit travailleurs en parallèle. Code : staticlibs/duckdb_java_data_import.
| Méthode | Durée |
|---|---|
extension communautaire mongo |
55 s |
JDBC executeBatch |
3 min 37 s |
| Appender Java | 2 min 02 s |
| table function Java | 1 min 27 s |
Les trois méthodes Java passent par une table intermédiaire, puis une copie
triée (ORDER BY updated_date DESC) d'environ 30 s : trier réduit nettement la
taille du fichier.
D'où viennent les écarts
executeBatch: le lot n'existe que côté Java. Dans DuckDB, chaque ligne reste unINSERTséparé, avec son coût fixe.- Appender (
Connection#createAppender) : un vrai lot, des blocs de 2 048 lignes envoyés en une opération. - Table function : le sens s'inverse. DuckDB appelle
apply(), qui remplit jusqu'àoutput.capacity()lignes depuis un curseur MongoDB, et renvoie 0 quand il n'y a plus de journée à lire.
L'enregistrement de la fonction, puis son usage en SQL (noms adaptés) :
DuckDBFunctions.tableFunction()
.withName("import_mongo")
.withParameters(LocalDate.class, LocalDate.class)
.withFunction(new ImportMongo())
.register(conn);
stmt.execute("SET preserve_insertion_order = FALSE");
stmt.execute("CREATE TABLE vulns AS FROM import_mongo('2026-01-01'::DATE, '2026-06-30'::DATE)");Deux réglages rendent l'appel parallèle : info.setMaxThreads(8) à
l'initialisation globale, et preserve_insertion_order = FALSE — sans lui,
DuckDB garde un seul fil pour respecter l'ordre de lecture.
Pourquoi pas l'extension mongo
Trois lignes de SQL (INSTALL mongo FROM community, puis ATTACH avec
TYPE MONGO) et le meilleur temps. Mais une extension communautaire n'a pas
de garantie de suivi : si elle n'est pas reconstruite pour une nouvelle version
de DuckDB, la mise à jour est bloquée. Maintenir un fork C++ n'était pas
envisageable ; l'équipe préfère du Java, réutilisable pour d'autres sources.
En production
Faute de disque pour une copie intermédiaire, la version déployée est non
parallèle, avec le ORDER BY dans le même CREATE TABLE : l'import y rejoint
le temps de l'extension. SET storage_compatibility_version = 'latest' a aussi
réduit le fichier. Point faible signalé : projeter les longues colonnes texte
reste lent (duckdb#24935).
Source : Importing Data using Java Table Functions, blog DuckDB, 5 octobre 2026.