veilletech.fr
6 oct. Feed du jour
#06 DUCKDB Article

DuckDB en Java : la table function bat le batch JDBC

Laisser DuckDB tirer les données, plutôt que les lui pousser.

Sur le blog de DuckDB, un utilisateur compare quatre façons de remplir DuckDB depuis MongoDB, sur 485 168 enregistrements : insertions par lot JDBC (3 min 37), Appender (2 min 02), table function Java parallèle (1 min 27), extension communautaire mongo (55 s). L'extension a été écartée faute de garantie de suivi ; c'est la table function Java qui est partie en production.

3 min de lectureintermédiairevidéo 1:23
Partager
Sommaire6 sections
  1. Ce qui se passe
  2. Le banc d'essai
  3. D'où viennent les écarts
  4. Pourquoi pas l'extension mongo
  5. En production
  6. À retenir

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

L'enregistrement de la fonction, puis son usage en SQL (noms adaptés) :

Java
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.