2024-02-13
Ankündigung: DuckDB 0.10.0
Mark Raasveldt, Hannes Mühleisen

Um die neue Version zu installieren, besuchen Sie bitte den Installationsleitfaden. Die vollständigen Release Notes finden Sie auf GitHub.
Was ist neu in 0.10.0
Es gab zu viele Änderungen, um jede im Detail zu besprechen, aber wir möchten mehrere besonders spannende Features hervorheben!
Unten eine Zusammenfassung dieser neuen Features mit Beispielen, beginnend mit einer Änderung in unserem SQL-Dialekt, die standardmäßig intuitivere Ergebnisse liefern soll.
Breaking SQL Changes
Impliziter Cast nach VARCHAR. Bisher hat DuckDB automatisch jeden Typ beim Function Binding implizit nach VARCHAR gecastet. Dadurch war es möglich, z. B. den Substring einer Ganzzahl ohne expliziten Cast zu berechnen. Ab diesem Release brauchen Sie hier einen expliziten Cast.
SELECT substring(42, 1, 1) AS substr;No function matches the given name and argument types 'substring(...)'.You might need to add explicit type casts.Für einen expliziten Cast führen Sie aus:
SELECT substring(42::VARCHAR, 1, 1) AS substr;┌─────────┐│ substr ││ varchar │├─────────┤│ 4 │└─────────┘Alternativ kann die Einstellung old_implicit_casting dieses Verhalten zurücksetzen, z. B.:
SET old_implicit_casting = true;SELECT substring(42, 1, 1) AS substr;┌─────────┐│ substr ││ varchar │├─────────┤│ 4 │└─────────┘Literal Typing. Bisher verhielten sich Integer- und String-Literale identisch zu den Typen INTEGER und VARCHAR. Ab diesem Release sind INTEGER_LITERAL und STRING_LITERAL eigene Typen mit eigenen Binding-Regeln.
INTEGER_LITERAL-Typen können implizit in jeden Integer-Typ konvertiert werden, in den der Wert passtSTRING_LITERAL-Typen können implizit in jeden anderen Typ konvertiert werden
Das richtet DuckDB an Postgres aus und macht Operationen auf Literalen intuitiver. Zum Beispiel können wir String-Literale mit Daten vergleichen – aber wir können VARCHAR-Werte nicht mit Daten vergleichen.
SELECT d > '1992-01-01' AS resultFROM (VALUES (DATE '1992-01-01')) t(d);┌─────────┐│ result ││ boolean │├─────────┤│ false │└─────────┘SELECT d > '1992-01-01'::VARCHAR AS resultFROM (VALUES (DATE '1992-01-01')) t(d);Binder Error:Cannot compare values of type DATE and type VARCHAR – an explicit cast is requiredAbwärtskompatibilität
Abwärtskompatibilität meint die Fähigkeit einer neueren DuckDB-Version, Speicherdateien zu lesen, die von einer älteren DuckDB-Version erzeugt wurden. Dieses Release ist das erste DuckDB-Release, das Abwärtskompatibilität im Speicherformat unterstützt. DuckDB v0.10 kann Dateien der vorherigen DuckDB-Version – DuckDB v0.9 – lesen und darauf arbeiten. Das ermöglicht die Implementierung eines neuen Serialisierungs-Frameworks.
Schreiben mit v0.9:
duckdb_092 v092.dbCREATE TABLE lineitem ASFROM lineitem.parquet;Lesen mit v0.10:
duckdb_0100 v092.dbSELECT l_orderkey, l_partkey, l_commentFROM lineitemLIMIT 1;┌────────────┬───────────┬─────────────────────────┐│ l_orderkey │ l_partkey │ l_comment ││ int32 │ int32 │ varchar │├────────────┼───────────┼─────────────────────────┤│ 1 │ 155190 │ to beans x-ray carefull │└────────────┴───────────┴─────────────────────────┘Für zukünftige DuckDB-Versionen ist unser Ziel, dass jede DuckDB-Version, die danach erscheint, Dateien vorheriger Versionen lesen kann, beginnend mit diesem Release. Wir wollen das Dateiformat vollständig abwärtskompatibel halten. So können Sie Daten in DuckDB-Dateien behalten und sind sicher, dass Sie die Dateien lesen können, ohne sich um die Schreibversion sorgen oder Dateien zwischen Versionen konvertieren zu müssen.
Vorwärtskompatibilität
Vorwärtskompatibilität meint die Fähigkeit einer älteren DuckDB-Version, Speicherdateien einer neueren DuckDB-Version zu lesen. DuckDB v0.9 ist teilweise vorwärtskompatibel mit DuckDB v0.10. Bestimmte von DuckDB v0.10 erzeugte Dateien können von DuckDB v0.9 gelesen werden.
Schreiben mit v0.10:
duckdb_0100 v010.dbCREATE TABLE lineitem ASFROM lineitem.parquet;Lesen mit v0.9:
duckdb_092 v010.dbSELECT l_orderkey, l_partkey, l_commentFROM lineitemLIMIT 1;┌────────────┬───────────┬─────────────────────────┐│ l_orderkey │ l_partkey │ l_comment ││ int32 │ int32 │ varchar │├────────────┼───────────┼─────────────────────────┤│ 1 │ 155190 │ to beans x-ray carefull │└────────────┴───────────┴─────────────────────────┘Vorwärtskompatibilität wird auf Best-Effort-Basis geboten. Die Stabilität des Speicherformats ist wichtig – es gibt aber noch viele Verbesserungen und Innovationen, die wir am Speicherformat vornehmen wollen. Deshalb kann Vorwärtskompatibilität gelegentlich (teilweise) gebrochen werden.
Für dieses Release kann DuckDB v0.9 von DuckDB v0.10 erzeugte Dateien lesen, sofern:
- Die Datenbankdatei keine Views enthält
- Die Datenbankdatei keine neuen Typen enthält (
ARRAY,UHUGEINT) - Die Datenbankdatei keine Indizes enthält (
PRIMARY KEY,FOREIGN KEY,UNIQUE, explizite Indizes) - Die Datenbankdatei keine neuen Kompressionsmethoden enthält (
ALP). Da ALP automatisch zum Komprimieren vonFLOAT- undDOUBLE-Spalten genutzt wird, funktioniert Vorwärtskompatibilität in der Praxis oft nicht fürFLOAT- undDOUBLE-Spalten, sofernALPnicht explizit per Konfiguration deaktiviert wird.
Wir erwarten, dass das mit Stabilisierung und Reife des Formats seltener vorkommt – und hoffen, bessere Garantien zu bieten, damit DuckDB Dateien zukünftiger DuckDB-Versionen lesen kann.
CSV-Reader-Überarbeitung
CSV-Reader-Überarbeitung. Der CSV-Reader hat in diesem Release eine große Überarbeitung bekommen. Der neue CSV-Reader nutzt effiziente Zustandsübergänge, um schnell durch CSV-Dateien zu gehen. Das hat die Performance des CSV-Readers stark beschleunigt, besonders in multithreaded Szenarien. Außerdem sollten Fehlermeldungen bei fehlerhaften CSV-Dateien klarer sein.
Unten ein Benchmark, der die Ladezeit von 11 Millionen Zeilen des NYC-Taxi-Datensatzes aus einer CSV-Datei auf einem M1 Max mit 10 Kernen vergleicht:
| Version | Ladezeit |
|---|---|
| v0.9.2 | 2.6 s |
| v0.10.0 | 1.2 s |
Außerdem wurden viele Optimierungen vorgenommen, die Abfragen direkt über CSV-Dateien deutlich schneller machen. Unten ein Benchmark, der die Ausführungszeit einer Abfrage SELECT count(*) direkt über die NYC-Taxi-CSV-Datei vergleicht.
| Version | Abfragezeit |
|---|---|
| v0.9.2 | 1.8 s |
| v0.10.0 | 0.3 s |
Arrays fester Länge
Arrays fester Länge. Dieses Release führt den Array-Typ fester Länge ein. Arrays fester Länge ähneln Listen, jedes Wert muss aber dieselbe feste Anzahl Elemente haben.
CREATE TABLE vectors (v DOUBLE[3]);INSERT INTO vectors VALUES ([1, 2, 3]);Arrays fester Länge lassen sich schneller verarbeiten als Listen variabler Länge, weil die Größe jedes Listenelements im Voraus bekannt ist. Dieses Release führt auch spezialisierte Funktionen über diese Arrays ein, etwa array_cross_product, array_cosine_similarity und array_inner_product.
SELECT array_cross_product(v, [1, 1, 1]) AS resultFROM vectors;┌───────────────────┐│ result ││ double[3] │├───────────────────┤│ [-1.0, 2.0, -1.0] │└───────────────────┘Mehr Informationen auf der Seite Array-Typ in der Dokumentation.
Multi-Datenbank-Unterstützung
DuckDB kann jetzt MySQL-, Postgres- und SQLite-Datenbanken anhängen, zusätzlich zu Datenbanken im eigenen Format. So lassen sich Daten bequem in DuckDB einlesen und zwischen diesen Systemen bewegen: Angehängte Datenbanken sind voll funktionsfähig, erscheinen wie reguläre Tabellen und können transaktional sicher aktualisiert werden. Mehr zur Multi-Datenbank-Unterstützung finden Sie in unserem aktuellen Blogpost.
ATTACH 'sqlite:sakila.db' AS sqlite;ATTACH 'postgres:dbname=postgresscanner' AS postgres;ATTACH 'mysql:user=root database=mysqlscanner' AS mysql;Secret Manager
DuckDB integriert sich mit mehreren Cloud-Speichersystemen wie S3, die Zugangsdaten brauchen. In der aktuellen DuckDB-Version werden Authentifizierungsinformationen über DuckDB-Einstellungen konfiguriert, z. B. SET s3_access_key_id = '...';. Das funktionierte, hatte aber mehrere Schwächen. Zum Beispiel war es nicht möglich, verschiedene Credentials für verschiedene S3-Buckets zu setzen, ohne die Einstellungen zwischen Abfragen zu ändern. Weil Einstellungen nicht als geheim gelten, war es auch möglich, sie mit duckdb_settings() abzufragen.
Mit diesem Release fügt DuckDB einen neuen „Secrets Manager“ hinzu, um Secrets besser zu verwalten. Wir haben jetzt eine einheitliche Benutzeroberfläche für Secrets über alle Backends, die sie nutzen. Secrets können scoped sein, sodass verschiedene Speicherpräfixe verschiedene Secrets haben können und so zum Beispiel in einer einzigen Abfrage organisationsübergreifend gejoint werden kann. Secrets können auch persistiert werden, sodass sie nicht bei jedem DuckDB-Start angegeben werden müssen.
Secrets sind typisiert; ihr Typ kennzeichnet, für welchen Dienst sie gelten. Dieses Release kann zum Beispiel Secrets für S3, Google Cloud Storage, Cloudflare R2 und Azure Blob Storage verwalten. Für jeden Typ gibt es einen oder mehrere „Secret Provider“, die festlegen, wie das Secret erzeugt wird. Secrets können auch einen optionalen Scope haben, ein Dateipfad-Präfix, auf das das Secret zutrifft. Beim Holen eines Secrets für einen Pfad werden die Secret-Scopes mit dem Pfad verglichen und das passende Secret zurückgegeben. Bei mehreren passenden Secrets wird das längste Präfix gewählt.
Schließlich können Secrets temporär oder persistent sein. Temporäre Secrets sind Standard – und werden im Speicher für die Lebensdauer der DuckDB-Instanz gehalten, ähnlich wie Einstellungen bisher. Persistente Secrets werden unverschlüsselt im Binärformat im Verzeichnis ~/.duckdb/stored_secrets gespeichert. Beim Start von DuckDB werden persistente Secrets aus diesem Verzeichnis gelesen und automatisch geladen.
Um zum Beispiel ein temporäres, unscoped Secret für den Zugriff auf S3 zu erzeugen, können wir jetzt folgende Syntax nutzen:
CREATE SECRET ( TYPE s3, KEY_ID '⟨AKIAIOSFODNN7EXAMPLE⟩', SECRET '⟨wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY⟩', REGION '⟨us-east-1⟩');Gibt es zwei Secrets für einen Diensttyp, kann der Scope entscheiden, welches genutzt wird. Zum Beispiel:
CREATE SECRET secret1 ( TYPE s3, KEY_ID 'my_key1', SECRET 'my_secret1', SCOPE 's3://⟨my-bucket⟩');
CREATE SECRET secret2 ( TYPE s3, KEY_ID 'my_key2', SECRET 'my_secret2', SCOPE 's3://⟨my-other-bucket⟩');Fragt der Nutzer etwas von s3://⟨my-other-bucket⟩/something{:.language-sql .highlight} ab, wird secret2 für diese Anfrage automatisch gewählt.
Secrets können mit der eingebauten tabellenerzeugenden Funktion aufgelistet werden, z. B. mit FROM duckdb_secrets();{:.language-sql .highlight}. Sensible Informationen werden redigiert.
Um Secrets zwischen DuckDB-Datenbankinstanzen zu persistieren, können wir jetzt den Befehl CREATE PERSISTENT SECRET nutzen, z. B.:
CREATE PERSISTENT SECRET my_persistent_secret ( TYPE s3, KEY_ID 'my_key', SECRET 'my_secret');Wie erwähnt schreibt das das Secret (unverschlüsselt, Vorsicht) ins Verzeichnis ~/.duckdb/stored_secrets.
Mehr Informationen auf der Seite Create Secret in der Dokumentation.
Temporary Memory Manager
DuckDB unterstützt Larger-than-Memory-Operationen: Speicherhungrige Operatoren wie Aggregationen und Joins können einen Teil ihrer Zwischenergebnisse in temporäre Dateien auf der Festplatte auslagern, wenn nicht genug Speicher verfügbar ist.
Bisher begannen diese Operatoren mit dem Auslagern, wenn ihre Speichernutzung etwa 60 % des verfügbaren Speichers (gemäß Speicherlimit) erreichte. Das funktioniert gut, wenn genau eine dieser Operationen gleichzeitig läuft. Laufen mehrere speicherintensive Operationen gleichzeitig, kann ihr kombinierter Speicherverbrauch das Speicherlimit überschreiten und DuckDB einen Fehler werfen.
Dieses Release führt den sogenannten „Temporary Memory Manager“ ein, der den temporären Speicher gleichzeitiger Operationen verwaltet. Das funktioniert so: Speicherintensive Operationen registrieren sich beim Temporary Manager. Jede Registrierung ist je nach Thread-Anzahl und aktuellem Speicherlimit mit einer Mindestmenge Speicher garantiert. Dann kommunizieren die speicherintensiven Operationen, wie viel Speicher sie aktuell nutzen möchten. Der Manager kann das genehmigen oder mit einer reduzierten Allokation antworten. Bei reduzierter Allokation muss der Operator seine Speicheranforderungen dynamisch senken, zum Beispiel durch Wechsel des Algorithmus.
Ein Hash Join könnte zum Beispiel seine Operation anpassen und einen partitionierten Hash Join statt eines vollständigen In-Memory-Joins ausführen, wenn nicht genug Speicher verfügbar ist.
Hier ein Beispiel:
PRAGMA memory_limit = '5GB';SET temp_directory = '/tmp/duckdb_temporary_memory_manager';
CREATE TABLE tbl ASSELECT range AS i, range AS jFROM range(100_000_000);
SELECT max(i), max(t1.j), max(t2.j), max(t3.j),FROM tbl AS t1JOIN tbl AS t2 USING (i)JOIN tbl AS t3 USING (i);Beachten Sie, dass hier ein temporäres Verzeichnis gesetzt werden muss, weil die Operatoren tatsächlich Daten auf die Festplatte auslagern müssen, um diese Abfrage bei diesem Speicherlimit zu beenden.
Mit der neuen Version 0.10.0 läuft diese Abfrage auf einem MacBook in ca. 5 s durch, während sie in der vorherigen Version mit Error: Out of Memory Error: failed to pin block of size ... fehlgeschlagen wäre.
Adaptive Lossless Floating-Point Compression (ALP)
Gleitkommazahlen sind berüchtigt schwer effizient zu komprimieren, sowohl beim Kompressionsverhältnis als auch bei Kompressions- und Dekompressionsgeschwindigkeit. Früher hatte DuckDB Unterstützung für die damaligen State-of-the-Art-Methoden „Chimp“ und „Patas“. Es stellte sich heraus, dass das nicht das letzte Wort bei der Gleitkommakompression war. Die Forschenden Azim Afroozeh, Leonard Kuffo und (der eine und einzige) Peter Boncz haben kürzlich das Paper „ALP: Adaptive Lossless floating-Point Compression“ auf der SIGMOD veröffentlicht, einer erstklassigen akademischen Konferenz für Datenmanagementforschung. In einem ungewöhnlichen und sehr anerkennenswerten Schritt haben sie auch einen Pull Request an DuckDB geschickt. Das neue Kompressionsschema ersetzt Chimp und Patas. In DuckDB ist ALP bei der Dekompression 2- bis 4-mal schneller als Patas und erreicht doppelt so hohe Kompressionsverhältnisse (manchmal sogar deutlich mehr).
| Kompression | Laden | Abfrage | Größe |
|---|---|---|---|
| ALP | 0.434 s | 0.020 s | 184 MB |
| Patas | 0.603 s | 0.080 s | 275 MB |
| Unkomprimiert | 0.316 s | 0.012 s | 489 MB |
Als Nutzer müssen Sie nichts tun, um die neue ALP-Kompressionsmethode zu nutzen: DuckDB entscheidet beim Checkpointing automatisch, ob ALP für den konkreten Datensatz vorteilhaft ist.
CLI-Verbesserungen
Der Kommandozeilenclient hat in diesem Release viel Arbeit gesehen. Insbesondere ist Multi-Line-Editing der Standardmodus geworden und wurde stark verbessert. Die Query-History ist jetzt ebenfalls mehrzeilig. Syntax Highlighting wurde verbessert – fehlende Klammern und ungeschlossene Anführungszeichen werden als Fehler hervorgehoben, und passende Klammern werden hervorgehoben, wenn der Cursor darüber fährt. Die Kompatibilität mit Readline wurde ebenfalls stark erweitert.

Siehe die erweiterte CLI-Dokumentation.
Schlussgedanken
Das waren ein paar Highlights – aber es gibt viele weitere Features und Verbesserungen in diesem Release. Unten noch ein paar Highlights. Die vollständigen Release Notes finden Sie auf GitHub.
Neue Features
COMMENT ONCOPY FROM DATABASE- Typ
UHUGEINT - Unterstützung für Window
EXCLUDEund WindowDISTINCT - Parquet-Verschlüsselungsunterstützung
- Indizes für Lambda-Parameter
EXCEPT ALL/INTERSECT ALLDESCRIBE/SHOW/SUMMARIZEals Subquery- Unterstützung rekursiver CTEs in korrelierten Subqueries
Neue Funktionen
- Funktionen
parquet_kv_metadataundparquet_file_metadata - Tabellenfunktionen
read_text/read_blob list_reduce,list_where,list_zip,list_select,list_grade_up
Speicherverbesserungen
Optimierungen
Danksagungen
Wir danken allen Beitragenden für ihre harte Arbeit an der Verbesserung von DuckDB.