2022-11-14
Ankündigung: DuckDB 0.6.0
Mark Raasveldt

Das DuckDB-Team freut sich, die neueste DuckDB-Version (0.6.0) anzukündigen. Dieses Release von DuckDB heißt „Oxyura“ nach der Weißkopf-Ruderente (Oxyura leucocephala), einer gefährdeten Art, die in Eurasien beheimatet ist.
Zur Installation der neuen Version besuchen Sie bitte den Installationsleitfaden. Hinweis: Das Release wird noch ausgerollt, daher sind möglicherweise noch nicht alle Artefakte veröffentlicht. Die vollständigen Release Notes finden Sie auf GitHub.
Was steckt in 0.6.0
Das neue Release enthält viele Verbesserungen am Speichersystem, allgemeine Leistungsverbesserungen, Verbesserungen am Speichermanagement und neue Features. Unten eine Zusammenfassung der wirkungsvollsten Änderungen zusammen mit den verlinkten PRs, die die Features umsetzen.
Speicherverbesserungen
Während wir auf die Stabilisierung des Speicherformats und Version 1.0 hinarbeiten, haben wir aktiv an der Verbesserung unseres Speicherformats gearbeitet, einschließlich vieler Kompressionsverbesserungen.
Optimistisches Schreiben auf die Platte. In früheren DuckDB-Versionen wurden die Daten einer einzelnen Transaktion zuerst in den Speicher geladen und erst beim Commit auf die Platte geschrieben. Das funktioniert gut, wenn Daten in Batches geladen werden, die in den Speicher passen, aber schlecht, wenn in einer einzelnen Transaktion viele Daten geladen werden, etwa beim Einlesen einer sehr großen Datei.
Diese Version führt optimistisches Schreiben auf die Platte ein. Beim Laden großer Datensätze in einer einzelnen Transaktion werden Daten komprimiert und in die Datenbankdatei gestreamt, noch bevor der COMMIT erfolgt ist. Wenn die Transaktion committet wird, sind die Daten bereits auf der Platte, und es muss nichts weiter geschrieben werden. Bei einem Rollback holt das System alle optimistisch geschriebenen Daten zurück.
Paralleles Datenladen. Zusätzlich zum optimistischen Schreiben auf die Platte enthält dieses Release Unterstützung für paralleles Datenladen in einzelne Tabellen. Das verbessert die Leistung des Datenladens auf Maschinen mit mehreren Kernen (also allen modernen Maschinen) erheblich.
Unten ein Benchmark zum Vergleich der Ladezeit von 150 Millionen Zeilen des Taxi-Datensatzes aus einer Parquet-Datei auf einem M1 Max mit 10 Kernen:
| Version | Load time |
|---|---|
| v0.5.1 | 91.4 s |
| v0.6.0 | 17.2 s |
DuckDB unterstützt zwei Modi – das order-preserving und das non-order-preserving parallele Datenladen.
Das order-preserving Laden bewahrt die Einfügereihenfolge, sodass z. B. die erste Zeile in Ihrer CSV-Datei die erste Zeile in der DuckDB-Tabelle ist. Das non-order-preserving Laden bietet keine solchen Garantien – und kann die Daten beim Laden umordnen. Standardmäßig wird das order-preserving Laden genutzt, das etwas Extra-Bookkeeping mit sich bringt. Das Bewahren der Einfügereihenfolge kann mit dem Statement SET preserve_insertion_order = false deaktiviert werden.
Kompressionsverbesserungen
FSST. Der Kompressionsalgorithmus Fast Static Symbol Table wird in dieser Version eingeführt. Dieser moderne Kompressionsalgorithmus komprimiert Daten innerhalb von Strings über ein Dictionary und behält dabei Unterstützung für effiziente Scans und Random Lookups. Das steigert das Kompressionsverhältnis von Strings mit vielen eindeutigen Werten, aber gemeinsamen Elementen, etwa E-Mail-Adressen oder URLs, erheblich.
Die Verbesserungen des Kompressionsverhältnisses des TPC-H-SF1-Datensatzes sind unten gezeigt:
| Compression | Size |
|---|---|
| Uncompressed | 761 MB |
| Dictionary | 510 MB |
| FSST + Dictionary | 251 MB |
Chimp. Der Chimp-Kompressionsalgorithmus ist enthalten, der Stand der Technik bei leichtgewichtiger Gleitkommakompression. Chimp ist eine verbesserte Version von Gorillas, die sowohl ein besseres Kompressionsverhältnis als auch schnellere Dekompression erreicht.
Patas. Patas ist eine neuartige Gleitkommakompressionsmethode, die den Chimp-Algorithmus weiterführt, indem sie auf einen einzelnen Fall im Chimp-Algorithmus optimiert. Patas hat im Allgemeinen ein etwas niedrigeres Kompressionsverhältnis als Chimp, dafür deutlich schnellere Dekompression, die fast an unkomprimierte Daten in der Lesegeschwindigkeit heranreicht.
Das Kompressionsverhältnis eines Datensatzes mit Temperaturen von Städten, gespeichert als Double (8-Byte-Gleitkommazahlen), ist unten gezeigt:
| Compression | Size |
|---|---|
| Uncompressed | 25.4 MB |
| Chimp | 9.7 MB |
| Patas | 10.2 MB |
Leistungsverbesserungen
DuckDB strebt sehr hohe Leistung für eine große Vielfalt von Workloads an. Deshalb arbeiten wir ständig daran, die Leistung für verschiedene Workloads zu verbessern. Dieses Release ist da keine Ausnahme.
Paralleles CSV-Laden (experimentell). In diesem Release starten wir einen neuen experimentellen parallelen CSV-Reader. Das verbessert die Einlesegeschwindigkeit großer CSV-Dateien ins System erheblich. Wir haben unser Bestes getan, um den parallelen CSV-Reader robust zu machen – CSV-Parsing ist ein Minenfeld, weil es draußen eine so große Vielfalt verschiedener Dateien gibt – daher haben wir den Reader vorerst als experimentell markiert.
Der parallele CSV-Reader kann aktiviert werden, indem das Flag experimental_parallel_csv auf true gesetzt wird. Wir wollen den parallelen CSV-Reader in zukünftigen DuckDB-Versionen zum Standard-Reader machen.
SET experimental_parallel_csv = true;Unten die Ladezeit einer 720-MB-CSV-Datei mit der Tabelle lineitem aus dem TPC-H-Benchmark,
| Variant | Load time |
|---|---|
| Single-threaded | 3.5 s |
| Parallel | 0.6 s |
Paralleles CREATE INDEX und Verbesserungen am Index-Speichermanagement. Auch das Anlegen von Indizes ist in diesem Release deutlich beschleunigt, weil das Statement CREATE INDEX jetzt vollständig parallel ausgeführt werden kann. Außerdem wird die Zahl der Speicherallokationen durch den ART durch Inlining kleiner Strukturen stark reduziert, was sowohl die Speichergröße senkt als auch die Leistung weiter verbessert.
Die Zeiten zum Anlegen eines Index auf einer einzelnen Spalte mit 16 Millionen Werten sind unten gezeigt.
| Version | Create index time |
|---|---|
| v0.5.1 | 5.92 s |
| v0.6.0 | 1.38 s |
Paralleles count(DISTINCT). Aggregationen mit DISTINCT-Aggregationen, am häufigsten für die genaue Distinct-Count-Berechnung genutzt (z. B. count(DISTINCT col)), mussten zuvor im Einthread-Modus ausgeführt werden. Ab v0.6.0 kann DuckDB diese Abfragen parallel ausführen, was zu großen Beschleunigungen führt.
Verbesserungen der SQL-Syntax
SQL ist der primäre Weg, mit DuckDB zu interagieren – und DuckDB versucht, einen leicht nutzbaren SQL-Dialekt zu haben. Dieses Release enthält weitere Verbesserungen am SQL-Dialekt.
UNION-Typ. Dieses Release führt den UNION-Typ ein, mit dem Summentypen in DuckDB gespeichert und abgefragt werden können. Zum Beispiel:
CREATE TABLE messages (u UNION(num INTEGER, error VARCHAR));INSERT INTO messages VALUES (42);INSERT INTO messages VALUES ('oh my globs');SELECT * FROM messages;┌─────────────┐│ u │├─────────────┤│ 42 ││ oh my globs │└─────────────┘Summentypen sind stark typisiert – sie erlauben aber, dass ein einzelner Wert in einer Tabelle als einer von verschiedenen Typen dargestellt wird. Die Union-Seite in der Dokumentation enthält mehr Informationen zur Nutzung dieses neuen zusammengesetzten Typs.
FROM-first. Ab diesem Release unterstützt DuckDB, Abfragen mit der FROM-Klausel statt der SELECT-Klausel zu beginnen. Tatsächlich ist die SELECT-Klausel jetzt vollständig optional und defaultet auf SELECT *. Das bedeutet, die folgenden Abfragen sind jetzt in DuckDB gültig:
-- SELECT clause is optional, SELECT * is implied (if not included)FROM tbl;
-- first 5 rows of the tableFROM tbl LIMIT 5;
-- SELECT can be used after the FROMFROM tbl SELECT l_orderkey;
-- insert all data from tbl1 into tbl2INSERT INTO tbl2 FROM tbl1;COLUMNS-Ausdruck. Dieses Release ergänzt Unterstützung für den COLUMNS-Ausdruck, inspiriert von der ClickHouse-Syntax. Der COLUMNS-Ausdruck erlaubt es, Ausdrücke oder Funktionen auf mehreren Spalten auszuführen, ohne den vollen Ausdruck zu duplizieren.
CREATE TABLE obs (id INTEGER, val1 INTEGER, val2 INTEGER);INSERT INTO obs VALUES (1, 10, 100), (2, 20, NULL), (3, NULL, 300);SELECT min(COLUMNS(*)), count(*) FROM obs;┌─────────────┬───────────────┬───────────────┬──────────────┐│ min(obs.id) │ min(obs.val1) │ min(obs.val2) │ count_star() │├─────────────┼───────────────┼───────────────┼──────────────┤│ 1 │ 10 │ 100 │ 3 │└─────────────┴───────────────┴───────────────┴──────────────┘Der COLUMNS-Ausdruck unterstützt alle Star-Ausdrücke, einschließlich der Syntax EXCLUDE und REPLACE. Außerdem kann der COLUMNS-Ausdruck einen regulären Ausdruck als Parameter entgegennehmen:
SELECT COLUMNS('val[0-9]+') FROM obs;┌──────┬──────┐│ val1 │ val2 │├──────┼──────┤│ 10 │ 100 ││ 20 │ NULL ││ NULL │ 300 │└──────┴──────┘Unterstützung für List Comprehension. List Comprehension ist ein eleganter und mächtiger Weg, Operationen auf Listen zu definieren. DuckDB unterstützt jetzt auch List Comprehension als Teil seines SQL-Dialekts. Zum Beispiel funktioniert die Abfrage unten jetzt:
SELECT [x + 1 for x in [1, 2, 3]] AS l;┌───────────┐│ l │├───────────┤│ [2, 3, 4] │└───────────┘Verschachtelte Typen und Strukturen sind in DuckDB sehr effizient umgesetzt und jetzt auch eleganter zu nutzen.
Verbesserungen am Speichermanagement
Bei der Arbeit mit großen Datensätzen ist Speichermanagement immer ein möglicher Schmerzpunkt. Durch eine streaming Ausführungsengine und einen Buffer Manager unterstützt DuckDB viele Operationen auf Datensätzen, die größer als der Speicher sind. DuckDB will auch Abfragen unterstützen, deren Zwischenergebnisse nicht in den Speicher passen, durch Disk-Spilling-Techniken, und hat Unterstützung für eine effiziente Out-of-Core-Sortierung, Out-of-Core-Window-Funktionen und einen Out-of-Core-Hash-Join.
Dieses Release verbessert das weiter, indem der Out-of-Core-Hash-Join stark optimiert wird, was zu einem deutlich eleganteren Leistungsabfall führt, wenn die Daten das Speicherlimit überschreiten.
| Memory limit (GB) | Old time (s) | New time (s) |
|---|---|---|
| 10 | 1.97 | 1.96 |
| 9 | 1.97 | 1.97 |
| 8 | 2.23 | 2.22 |
| 7 | 2.23 | 2.44 |
| 6 | 2.27 | 2.39 |
| 5 | 2.27 | 2.32 |
| 4 | 2.81 | 2.45 |
| 3 | 5.60 | 3.20 |
| 2 | 7.69 | 3.28 |
| 1 | 17.73 | 4.35 |
jemalloc. Außerdem bündelt dieses Release den jemalloc-Allocator standardmäßig mit der Linux-Version von DuckDB, was ein offenes Problem behebt, bei dem der Standard-GLIBC-Allocator Blöcke nicht an das Betriebssystem zurückgab und so unnötig zu Out-of-Memory-Fehlern auf der Linux-Version führte. Hinweis: Dieses Problem tritt unter macOS oder Windows nicht auf, daher nutzen wir dort weiterhin die Standard-Allocatoren (zumindest vorerst).
Shell-Verbesserungen
DuckDB hat eine Kommandozeilenschnittstelle (CLI), die von der SQLite-CLI übernommen ist und daher eine ähnliche Schnittstelle wie SQLite unterstützt. Alle Tabellen in diesem Blogbeitrag wurden mit .mode markdown in der CLI erzeugt.
Die DuckDB-Shell bietet außerdem mehrere Verbesserungen gegenüber der SQLite-Shell, etwa Syntax-Highlighting, und dieses Release enthält ein paar neue Goodies.
DuckBox-Rendering. Dieses Release enthält ein neues Rendering .mode duckbox, das standardmäßig genutzt wird. Dieses Box-Rendering passt sich an die Größe der Shell an und lässt Spalten und Zeilen weg, um einen besseren Überblick über ein Ergebnis zu geben. Es rendert sehr schnell große Ergebnismengen, indem Zeilen in der Mitte weggelassen werden. So sprengt SELECT * FROM tbl in der Shell sie nicht mehr. Tatsächlich kann das jetzt genutzt werden, um schnell ein gutes Gefühl für einen Datensatz zu bekommen.
Die Zahl der gerenderten Zeilen kann mit der Einstellung .maxrows X geändert werden, und Sie können mit dem Befehl .mode box zum alten Rendering zurückwechseln.
SELECT * FROM '~/Data/nyctaxi/nyc-taxi/2014/04/data.parquet';┌───────────┬─────────────────────┬─────────────────────┬───┬────────────┬──────────────┬──────────────┐│ vendor_id │ pickup_at │ dropoff_at │ … │ tip_amount │ tolls_amount │ total_amount ││ varchar │ timestamp │ timestamp │ │ float │ float │ float │├───────────┼─────────────────────┼─────────────────────┼───┼────────────┼──────────────┼──────────────┤│ CMT │ 2014-04-08 08:59:39 │ 2014-04-08 09:28:57 │ … │ 3.7 │ 0.0 │ 22.2 ││ CMT │ 2014-04-08 14:59:22 │ 2014-04-08 15:04:52 │ … │ 1.3 │ 0.0 │ 7.8 ││ CMT │ 2014-04-08 08:45:28 │ 2014-04-08 08:50:41 │ … │ 1.2 │ 0.0 │ 7.2 ││ CMT │ 2014-04-08 08:00:20 │ 2014-04-08 08:11:31 │ … │ 1.7 │ 0.0 │ 10.2 ││ CMT │ 2014-04-08 08:38:36 │ 2014-04-08 08:44:37 │ … │ 1.2 │ 0.0 │ 7.2 ││ CMT │ 2014-04-08 07:52:53 │ 2014-04-08 07:59:12 │ … │ 1.3 │ 0.0 │ 7.8 ││ CMT │ 2014-04-08 16:08:16 │ 2014-04-08 16:12:38 │ … │ 1.4 │ 0.0 │ 8.4 ││ CMT │ 2014-04-08 12:04:09 │ 2014-04-08 12:14:30 │ … │ 1.7 │ 0.0 │ 10.2 ││ CMT │ 2014-04-08 16:18:38 │ 2014-04-08 16:37:04 │ … │ 2.5 │ 0.0 │ 17.5 ││ CMT │ 2014-04-08 15:28:00 │ 2014-04-08 15:34:44 │ … │ 1.4 │ 0.0 │ 8.4 ││ · │ · │ · │ · │ · │ · │ · ││ · │ · │ · │ · │ · │ · │ · ││ · │ · │ · │ · │ · │ · │ · ││ CMT │ 2014-04-25 00:09:34 │ 2014-04-25 00:14:52 │ … │ 2.5 │ 0.0 │ 10.0 ││ CMT │ 2014-04-25 01:59:39 │ 2014-04-25 02:16:07 │ … │ 3.5 │ 0.0 │ 21.0 ││ CMT │ 2014-04-24 23:02:08 │ 2014-04-24 23:47:10 │ … │ 8.8 │ 0.0 │ 52.8 ││ CMT │ 2014-04-25 01:27:11 │ 2014-04-25 01:56:53 │ … │ 4.6 │ 0.0 │ 27.6 ││ CMT │ 2014-04-25 00:15:46 │ 2014-04-25 00:25:37 │ … │ 1.0 │ 0.0 │ 11.5 ││ CMT │ 2014-04-25 00:17:53 │ 2014-04-25 00:22:52 │ … │ 1.3 │ 0.0 │ 7.8 ││ CMT │ 2014-04-25 03:13:19 │ 2014-04-25 03:21:50 │ … │ 2.1 │ 0.0 │ 12.6 ││ CMT │ 2014-04-24 23:53:03 │ 2014-04-25 00:16:01 │ … │ 2.85 │ 0.0 │ 31.35 ││ CMT │ 2014-04-25 00:26:08 │ 2014-04-25 00:31:25 │ … │ 1.4 │ 0.0 │ 8.4 ││ CMT │ 2014-04-24 23:21:39 │ 2014-04-24 23:33:57 │ … │ 1.0 │ 0.0 │ 11.5 │├───────────┴─────────────────────┴─────────────────────┴───┴────────────┴──────────────┴──────────────┤│ 14618759 rows (20 shown) 18 columns (6 shown) │└──────────────────────────────────────────────────────────────────────────────────────────────────────┘Kontextbewusstes Auto-Complete. Die Shell kommt jetzt auch mit kontextbewusstem Auto-Complete. Auto-Complete wird durch Drücken der Tabulatortaste ausgelöst. Die Shell vervollständigt vier verschiedene Gruppen: (1) Schlüsselwörter, (2) Tabellennamen + Tabellenfunktionen, (3) Spaltennamen + Skalarfunktionen und (4) Dateinamen. Die Shell schaut auf die Position im SQL-Statement, um zu bestimmen, welche dieser Vervollständigungen ausgelöst werden. Zum Beispiel:
S -> SELECT
SELECT s -> student_id
SELECT student_id F -> FROM
SELECT student_id FROM g -> grades
SELECT student_id FROM 'd -> data/
SELECT student_id FROM 'data/ -> data/grades.csvFortschrittsbalken. DuckDB hat Fortschrittsbalken in Abfragen schon eine Weile unterstützt, sie waren aber immer opt-in. In diesem Release haben wir den Fortschrittsbalken schöner gemacht und ihn in der Shell standardmäßig aktiviert. Der Fortschrittsbalken erscheint, wenn eine Abfrage länger als 2 Sekunden dauert, und zeigt eine geschätzte Restzeit für die Abfrage.
COPY lineitem TO 'lineitem-big.parquet'; 32% ▕███████████████████▏ ▏In Zukunft wollen wir den Fortschrittsbalken in anderen Clients standardmäßig aktivieren. Vorerst kann das manuell durch folgende SQL-Abfragen geschehen:
PRAGMA enable_progress_bar;PRAGMA enable_print_progress_bar;