“小而美” 的分析庫-DuckDB 初探
DuckDB 是近期非?;鸬囊豢?AP 數(shù)據(jù)庫,其獨特的定位很有趣。甚至有數(shù)據(jù)庫產(chǎn)品考慮將其納入進(jìn)來,作為分析能力的擴(kuò)展。本文就針對這一數(shù)據(jù)庫做個小評測。
1. DuckDB 數(shù)據(jù)庫概述
1).DuckDB 產(chǎn)生背景
DuckDB 是一個 In-Process 的 OLAP 數(shù)據(jù)庫,可以理解為 AP 版本的 SQLite,但其底層是列式存儲。2019 年 SIGMOD 有一篇 Demo 論文介紹 DuckDB:an embedded analytical database。隨著單機(jī)內(nèi)存的變大,大部分 OLTP 數(shù)據(jù)庫都能在內(nèi)存中放得下,而很多 OLAP 也有在單機(jī)就能搞定的趨勢。單臺服務(wù)器的內(nèi)存很容易達(dá)到 TB,加上 SSD,搞個幾十甚至上百 TB 很容易。DuckDB 就是為了填補這個空白而生的。
2).DuckDB 開源情況
DuckDB 采用 MIT 協(xié)議開源,是荷蘭 CWI 數(shù)據(jù)庫組的一個項目,學(xué)術(shù)氣息比較濃厚,項目的組織很有教科書的感覺,架構(gòu)很清晰,所以非常適合閱讀學(xué)習(xí)。我從 OSS Insight 拉個一個 Star 數(shù)對比,可以看到 DuckDB 發(fā)展非常迅速。
圖片
3).DuckDB 特點
DuckDB是一個免費的、開源的、嵌入式數(shù)據(jù)庫管理系統(tǒng),專為數(shù)據(jù)分析和在線分析處理而設(shè)計。這意味著以下幾點:
- 它是免費的開源軟件,因此任何人都可以使用和修改代碼。
- 它是嵌入式的,這意味著DBMS(數(shù)據(jù)庫管理系統(tǒng))與使用它的應(yīng)用程序在同一進(jìn)程中運行。這使得它快速且易于使用。
- 它針對數(shù)據(jù)分析和OLAP(在線分析處理)進(jìn)行了優(yōu)化,而不僅僅是像典型數(shù)據(jù)庫那樣只針對事務(wù)數(shù)據(jù)。這意味著數(shù)據(jù)按列而不是行組織以優(yōu)化聚合和分析。
- 它支持標(biāo)準(zhǔn)SQL,因此可以在數(shù)據(jù)上運行查詢、聚合、連接和其他SQL函數(shù)。
- 它在進(jìn)程中運行,即在應(yīng)用程序本身內(nèi)運行,而不是作為單獨的進(jìn)程運行。這消除了進(jìn)程間通信的開銷。
- 與SQLite一樣,它是一個簡單的、基于文件的數(shù)據(jù)庫,因此不需要單獨安裝服務(wù)器。只需將庫包含在應(yīng)用程序中即可。
4).DuckDB 優(yōu)點
- DuckDB 易于安裝、部署和使用。沒有需要配置的服務(wù)器,可在應(yīng)用程序內(nèi)部嵌入運行,這使得它易于集成到不同編程語言環(huán)境中。
- DuckDB 盡管它很簡單,但DuckDB具有豐富的功能集。它支持完整的SQL標(biāo)準(zhǔn)、事務(wù)、二級索引,并且與流行的數(shù)據(jù)分析編程語言如 Python 和 R 集成良好。
- DuckDB 是免費的,任何人都可以使用和修改它,這降低了開發(fā)人員和數(shù)據(jù)分析師采用它的門檻。
- DuckDB 兼容性很好,幾乎無依賴性,甚至可在瀏覽器中運行。
- DuckDB 具有靈活的擴(kuò)展機(jī)制,這對于直接從 CSV、JSON、Parquet、MySQL 或直接從 S3 讀取數(shù)據(jù)特別重要,能夠大大提高開發(fā)人員的體驗。
- DuckDB 可提供數(shù)據(jù)超出內(nèi)存限制但小于磁盤容量規(guī)模下的工作負(fù)載,這樣分析工作可通過 "便宜"的硬件來完成。
2. DuckDB 數(shù)據(jù)庫架構(gòu)
圖片
DuckDB 數(shù)據(jù)庫可分為多個組件:Parser、Logical Planner、Optimizer、Physical Planner、Execution Engine、Transaction and Storage Managers。
1).Parser
DuckDB SQL Parser 源自 Postgres SQL Parser。
2).Logical Planner
包含了兩個過程 binder、plan generator。前者是解析所有引用的 schema 中的對象(如 table 或 view)的表達(dá)式,將其與列名和類型匹配。后者將 binder 生成的 AST 轉(zhuǎn)換為由基本 logical query 查詢運算符組成的樹,就得到了一顆 type-resolved logical query plan。
3).Optimizer
優(yōu)化器部分,會采用多種優(yōu)化手段對 logical query plan 進(jìn)行優(yōu)化,最終生成 physical plan。例如,其內(nèi)置一組 rewrite rules 來簡化 expression tree,例如執(zhí)行公共子表達(dá)式消除和常量折疊。針對表關(guān)聯(lián),會使用動態(tài)規(guī)劃進(jìn)行 join order 的優(yōu)化,針對復(fù)雜的 join graph 會 fallback 到貪心算法會消除所有的 subquery。
4).Execution Engine
DuckDB 最開始采用了基于 Pull-based 的 Vector Volcano 的執(zhí)行引擎,后來切換到了 Push-based 的 pipelines 執(zhí)行方法。DuckDB 采用了向量化計算來來加速計算,具有內(nèi)部實現(xiàn)的多種類型的 vector 以及向量化的 operator。另外出于可移植性原因,沒有采用 JIT,因為 JIT引擎依賴于大型編譯器庫(例如LLVM),具有額外的傳遞依賴。
5).Transactions
DuckDB 通過 MVCC 提供了 ACID 的特性,實現(xiàn)了HyPer專門針對混合OLAP / OLTP系統(tǒng)定制的可串行化MVCC 變種 。該變種立即 in-place 更新數(shù)據(jù),并將先前狀態(tài)存儲在單獨的 undo buffer 中,以供并發(fā)事務(wù)和 abort 使用。
6).Persistent Storage
DuckDB 使用面向讀取優(yōu)化的 DataBlocks 存儲布局(單個文件)。邏輯表被水平分區(qū)為 chunks of columns,并使用輕量級壓縮方法壓縮成 physical block 。每個塊都帶有每列的min/max 索引,以便快速確定它們是否與查詢相關(guān)。此外,每個塊還帶有每列的輕量級索引,可以進(jìn)一步限制掃描的值數(shù)量。
3. DuckDB 初體驗
1).部署安裝
DuckDB 提供了非常簡單的安裝方法,從官網(wǎng) duckdb.org 直接下載安裝解壓即可使用。此外,DuckDB 還可以內(nèi)置在多種開發(fā)語言中使用,下文會以 Python 舉例說明。
圖片
2).啟動數(shù)據(jù)庫
DuckDB 啟動非常簡單,直接將安裝包解壓后執(zhí)行即可。
[root@hfserver1 soft]# ./duckdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
上文提示連接到內(nèi)存庫。默認(rèn)情況下,DuckDB 是運行在內(nèi)存數(shù)據(jù)庫中,這意味著創(chuàng)建的任何表都存儲在內(nèi)存中,而不是持久化到磁盤上。可以通過啟動命令行參數(shù)的方式,將 DuckDB 連接到磁盤上的持久化數(shù)據(jù)庫文件。任何寫入該數(shù)據(jù)庫連接的數(shù)據(jù)都將保存到磁盤文件中,并在重新連接到同一文件時重新加載。
[root@hfserver1 soft]# ls -al *db
-rwxr-xr-x 1 root root 44784232 Mar 18 20:47 duckdb
-rw-r--r-- 1 root root 18886656 Apr 9 16:06 testdb
[root@hfserver1 soft]# ./duckdb testdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
D PRAGMA database_list;
┌───────┬─────────┬─────────┐
│ seq │ name │ file │
│ int64 │ varchar │ varchar │
├───────┼─────────┼─────────┤
│ 1080 │ testdb │ testdb │
└───────┴─────────┴─────────┘
上面示例啟動到一個文件中,并通過 PRAGMA 命令查看下當(dāng)前運行庫。
3).簡單 CRUD
[root@hfserver1 soft]# ./duckdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
-- 創(chuàng)建一張表
D create table t1( a int,b int);
-- 查看表
D .tables
t1
-- 插入數(shù)據(jù)
D insert into t1 values(1,1);
-- 修改輸出格式
D .mode table
-- 查看數(shù)據(jù)
D select * from t1;
+---+---+
| a | b |
+---+---+
| 1 | 1 |
+---+---+
-- 更新數(shù)據(jù)
D update t1 set b=2 where a=1;
-- 查看數(shù)據(jù)
D select * from t1;
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+
-- 查看表結(jié)構(gòu)
D describe t1;
+-------------+-------------+------+-----+---------+-------+
| column_name | column_type | null | key | default | extra |
+-------------+-------------+------+-----+---------+-------+
| a | INTEGER | YES | | | |
| b | INTEGER | YES | | | |
+-------------+-------------+------+-----+---------+-------+
4).數(shù)據(jù)加載
DuckDB 除了支持通常的insert插入數(shù)據(jù)外,也支持從CSV、JSON、Parquet、MySQL 等數(shù)據(jù)源中直接查詢或?qū)霐?shù)據(jù)。
-- 讀取外部數(shù)據(jù)
D select * from read_csv('tmp.csv');
+----+-------+
| id | name |
+----+-------+
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
+----+-------+
-- 加載數(shù)據(jù)到本地
D create table csv_table as select * from read_csv('tmp.csv');
D select count(*) from csv_table;
+--------------+
| count_star() |
+--------------+
| 3 |
+--------------+
-- COPY 復(fù)制數(shù)據(jù)
D COPY csv_table FROM 'tmp.csv';
D select count(*) from csv_table;
+--------------+
| count_star() |
+--------------+
| 6 |
+--------------+
5).應(yīng)用集成
DuckDB 有個很強大的功能,就是可以方便的集成進(jìn)應(yīng)用,其支持常見的C、Java、Python、Go等。下文通過 Python 做個示例。
[root@hfserver1 soft]# pip install duckdb
[root@hfserver1 soft]# cat test.py
import duckdb
con = duckdb.connect("file.db")
con.sql("CREATE TABLE test (i INTEGER)")
con.sql("INSERT INTO test VALUES (42)")
con.table("test").show()
con.close()
[root@hfserver1 soft]# python test.py
┌───────┐
│ i │
│ int32 │
├───────┤
│ 42 │
└───────┘
6).插件擴(kuò)展
DuckDB 通過插件進(jìn)行能力的擴(kuò)展,其支持很多不同的插件,能夠通過 INSTALL 和 LOAD來進(jìn)行開關(guān),可以使用 shared library 的方式進(jìn)行加載。很多核心特性都是通過插件來實現(xiàn)的,例如:time zone, json, sqlite_scanner 等。下圖是 DuckDB 內(nèi)置的一些插件。
圖片
下文通過插件訪問 MySQL 庫做個示例。
[root@hfserver1 soft]# ./duckdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D install mysql;
100% ▕████████████████████████████████████████████████████████████▏
D
-- 加載本地的 MySQL 數(shù)據(jù)庫
D ATTACH 'host=localhost user=root port=3307 database=test' AS mysqldb (TYPE MYSQL);
D use mysqldb;
D show tables;
┌────────────────────┐
│ name │
│ varchar │
├────────────────────┤
│ AA │
│ COMMITTEE │
...
7).性能對比
DuckDB 定位是一款分析型數(shù)據(jù)庫,下文針對 DuckDB 與 MySQL 做個簡單的查詢性能對比。測試環(huán)境在MySQL中構(gòu)建一張大表(百萬級)執(zhí)行聚合查詢,然后通過插件功能導(dǎo)入到 DuckDB 中跑下同樣的示例。從跑出的數(shù)據(jù)來看,有十余倍的提升。
-- MySQL 環(huán)境
mysql> select count(*) from big_emp;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
mysql> show create table big_emp\G;
*************************** 1. row ***************************
Table: big_emp
Create Table: CREATE TABLE `big_emp` (
`empno` int NOT NULL,
`ename` varchar(30) DEFAULT NULL,
`job` varchar(9) DEFAULT NULL,
`mgr` int DEFAULT NULL,
`hiredate` date DEFAULT NULL,
`sal` int DEFAULT NULL,
`comm` int DEFAULT NULL,
`deptno` int DEFAULT NULL,
PRIMARY KEY (`empno`),
KEY `fk_deptno` (`deptno`),
KEY `idx_sal` (`sal`),
CONSTRAINT `fk_deptno1` FOREIGN KEY (`deptno`) REFERENCES `big_dept` (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> select * from big_emp limit 3;
+-------+--------+-------+-------+------------+-------+------+---------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-------+-------+------------+-------+------+--------+
| 1 | user1 | job | 1 | 2000-01-01 | 1 | 1 | 925 |
| 2 | user2 | job | 1 | 2000-01-01 | 1 | 1 | 594 |
| 3 | user3 | job | 1 | 2000-01-01 | 1 | 1 | 307 |
+-------+--------+-------+------+-------------+-------+------+---------+
-- 構(gòu)建 DuckDB 環(huán)境
[root@hfserver1 soft]# ./duckdb testdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
D ATTACH 'host=localhost user=root port=3307 database=test' AS mysqldb (TYPE MYSQL);
D create table big_emp as select * from mysqldb.big_emp;
100%
-- 查詢對比
[root@hfserver1 soft]# time mysql -e "select deptno,count(*) from big_emp group by deptno" test
real 0m0.192s
user 0m0.014s
sys 0m0.000s
[root@hfserver1 soft]# time ./duckdb testdb -c "select deptno,count(*) from big_emp group by deptno"
real 0m0.015s
user 0m0.010s
sys 0m0.009sselect count(*) from big_emp;\n+----------+\n| count(*) |\n+----------+\n| 1000000 |\n+----------+\n\nmysql> show create table big_emp\\G;\n*************************** 1. row ***************************\n Table: big_emp\nCreate Table: CREATE TABLE " big_emp="big_emp" n="n" empno="empno" int="int" not="not" null="null" ename="ename" varchar="varchar" default="default" job="job" mgr="mgr" hiredate="hiredate" date="date" sal="sal" comm="comm" deptno="deptno" primary="primary" key="key" fk_deptno="fk_deptno" idx_sal="idx_sal" cnotallow="constraint" fk_deptno1="fk_deptno1" foreign="foreign" references="references" big_dept="big_dept" engine="InnoDB" charset="latin1\n1" row="row" in="in" set="set" sec="sec" select="select" from="from" limit="limit" user1="user1" user2="user2" user3="user3" duckdb="duckdb" soft="soft" testdb="testdb" a89d97db8="a89d97db8" quot="quot" for="for" usage="usage" hints="hints" attach="attach" host="localhost" user="root" port="3307" database="test'" as="as" mysqldb="mysqldb" type="type" mysql="mysql" create="create" table="table" time="time" e="e" group="group" by="by" test="test" m0="m0" c="c" data-lark-record-format="docx/text" class="" style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
-- MySQL 環(huán)境
mysql> select count(*) from big_emp;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
mysql> show create table big_emp\G;
*************************** 1. row ***************************
Table: big_emp
Create Table: CREATE TABLE `big_emp` (
`empno` int NOT NULL,
`ename` varchar(30) DEFAULT NULL,
`job` varchar(9) DEFAULT NULL,
`mgr` int DEFAULT NULL,
`hiredate` date DEFAULT NULL,
`sal` int DEFAULT NULL,
`comm` int DEFAULT NULL,
`deptno` int DEFAULT NULL,
PRIMARY KEY (`empno`),
KEY `fk_deptno` (`deptno`),
KEY `idx_sal` (`sal`),
CONSTRAINT `fk_deptno1` FOREIGN KEY (`deptno`) REFERENCES `big_dept` (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> select * from big_emp limit 3;
+-------+--------+-------+-------+------------+-------+------+---------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-------+-------+------------+-------+------+--------+
| 1 | user1 | job | 1 | 2000-01-01 | 1 | 1 | 925 |
| 2 | user2 | job | 1 | 2000-01-01 | 1 | 1 | 594 |
| 3 | user3 | job | 1 | 2000-01-01 | 1 | 1 | 307 |
+-------+--------+-------+------+-------------+-------+------+---------+
-- 構(gòu)建 DuckDB 環(huán)境
[root@hfserver1 soft]# ./duckdb testdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
D ATTACH 'host=localhost user=root port=3307 database=test' AS mysqldb (TYPE MYSQL);
D create table big_emp as select * from mysqldb.big_emp;
100%
-- 查詢對比
[root@hfserver1 soft]# time mysql -e "select deptno,count(*) from big_emp group by deptno" test
real 0m0.192s
user 0m0.014s
sys 0m0.000s
[root@hfserver1 soft]# time ./duckdb testdb -c "select deptno,count(*) from big_emp group by deptno"
real 0m0.015s
user 0m0.010s
sys 0m0.009sselect count(*) from big_emp;\n+----------+\n| count(*) |\n+----------+\n| 1000000 |\n+----------+\n\nmysql> show create table big_emp\\G;\n*************************** 1. row ***************************\n Table: big_emp\nCreate Table: CREATE TABLE " big_emp="big_emp" n="n" empno="empno" int="int" not="not" null="null" ename="ename" varchar="varchar" default="default" job="job" mgr="mgr" hiredate="hiredate" date="date" sal="sal" comm="comm" deptno="deptno" primary="primary" key="key" fk_deptno="fk_deptno" idx_sal="idx_sal" cnotallow="constraint" fk_deptno1="fk_deptno1" foreign="foreign" references="references" big_dept="big_dept" engine="InnoDB" charset="latin1\n1" row="row" in="in" set="set" sec="sec" select="select" from="from" limit="limit" user1="user1" user2="user2" user3="user3" duckdb="duckdb" soft="soft" testdb="testdb" a89d97db8="a89d97db8" quot="quot" for="for" usage="usage" hints="hints" attach="attach" host="localhost" user="root" port="3307" database="test'" as="as" mysqldb="mysqldb" type="type" mysql="mysql" create="create" table="table" time="time" e="e" group="group" by="by" test="test" m0="m0" c="c" data-lark-record-format="docx/text" class="" style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">
-- MySQL 環(huán)境
mysql> select count(*) from big_emp;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
mysql> show create table big_emp\G;
*************************** 1. row ***************************
Table: big_emp
Create Table: CREATE TABLE `big_emp` (
`empno` int NOT NULL,
`ename` varchar(30) DEFAULT NULL,
`job` varchar(9) DEFAULT NULL,
`mgr` int DEFAULT NULL,
`hiredate` date DEFAULT NULL,
`sal` int DEFAULT NULL,
`comm` int DEFAULT NULL,
`deptno` int DEFAULT NULL,
PRIMARY KEY (`empno`),
KEY `fk_deptno` (`deptno`),
KEY `idx_sal` (`sal`),
CONSTRAINT `fk_deptno1` FOREIGN KEY (`deptno`) REFERENCES `big_dept` (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> select * from big_emp limit 3;
+-------+--------+-------+-------+------------+-------+------+---------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-------+-------+------------+-------+------+--------+
| 1 | user1 | job | 1 | 2000-01-01 | 1 | 1 | 925 |
| 2 | user2 | job | 1 | 2000-01-01 | 1 | 1 | 594 |
| 3 | user3 | job | 1 | 2000-01-01 | 1 | 1 | 307 |
+-------+--------+-------+------+-------------+-------+------+---------+
-- 構(gòu)建 DuckDB 環(huán)境
[root@hfserver1 soft]# ./duckdb testdb
v0.10.1 4a89d97db8
Enter ".help" for usage hints.
D ATTACH 'host=localhost user=root port=3307 database=test' AS mysqldb (TYPE MYSQL);
D create table big_emp as select * from mysqldb.big_emp;
100%
-- 查詢對比
[root@hfserver1 soft]# time mysql -e "select deptno,count(*) from big_emp group by deptno" test
real 0m0.192s
user 0m0.014s
sys 0m0.000s
[root@hfserver1 soft]# time ./duckdb testdb -c "select deptno,count(*) from big_emp group by deptno"
real 0m0.015s
user 0m0.010s
sys 0m0.009s
8).參數(shù)管理
DuckDB 運維管理是比較簡單的,需要調(diào)整的一般就是參數(shù)部分??赏ㄟ^下面步驟來查看及修改。
-- 查看參數(shù)
D select name,value from duckdb_settings();
+-----------------------------------+-------------------------------------------+
| ame | value |
+-----------------------------------+-------------------------------------------+
| access_mode | automatic |
| allow_persistent_secrets | true |
| checkpoint_threshold | 16.0 MiB |
| debug_checkpoint_abort | none |
| debug_force_external | false |
| debug_force_no_cross_product | false |
...
| Calendar | gregorian |
+-----------------------------------+-------------------------------------------+
-- 修改參數(shù)
D set threads=10;
-- 查看單個參數(shù)
D SELECT current_setting('threads') AS threads;
+---------+
| threads |
+---------+
| 10 |
+---------+
9).數(shù)據(jù)字典
DuckDB 仿照 MySQL 實現(xiàn)一組數(shù)據(jù)字典,此外也提供部分函數(shù)作為補充。
-- information_schema
information_schema.schemata: Database, Catalog and Schema
information_schema.tables: Tables and Views
information_schema.columns: Columns
information_schema.character_sets: Character Sets
information_schema.key_column_usage: Key Column Usage
information_schema.referential_constraints: Referential Constraints
information_schema.table_constraints: Table Constraints
-- catalog function
current_catalog()
Return the name of the currently active catalog. Default is memory.
current_schema()
Return the name of the currently active schema. Default is main.
current_schemas(boolean)
Return list of schemas. Pass a parameter of true to include implicit schemas.
10).Pragma 擴(kuò)展
PRAGMA 語句是DuckDB從SQLite中采用的SQL擴(kuò)展。PRAGMA語句可以以與常規(guī)SQL語句類似的方式發(fā)出。PRAGMA命令可能會改變數(shù)據(jù)庫引擎的內(nèi)部狀態(tài),并可能影響引擎的后續(xù)執(zhí)行或行為。
-- 數(shù)據(jù)庫信息
D PRAGMA database_list;
+------+------+---------------------------------------+
| seq | name | file |
+------+------+---------------------------------------+
| 1080 | file | ...file.db |
+------+------+---------------------------------------+
-- 數(shù)據(jù)庫信息(大?。?D CALL pragma_database_size();
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+
| database_name | database_size | block_size | total_blocks | used_blocks | free_blocks | wal_size | memory_usage | memory_limit |
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+
| file | 512.0 KiB | 262144 | 2 | 2 | 0 | 0 bytes | 256.0 KiB | 25.0 GiB |
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+
-- 所有表信息
D PRAGMA show_tables;
+------+
| name |
+------+
| t1 |
| t2 |
| test |
+------+
-- 表詳細(xì)信息
D PRAGMA show_tables_expanded;
+----------+--------+------+--------------+--------------------+-----------+
| database | schema | name | column_names | column_types | temporary |
+----------+--------+------+--------------+--------------------+-----------+
| file | main | t1 | [a, b] | [INTEGER, INTEGER] | false |
| file | main | t2 | [a, b] | [INTEGER, INTEGER] | false |
| file | main | test | [i] | [INTEGER] | false |
+----------+--------+------+--------------+--------------------+-----------+
-- 函數(shù)信息
D PRAGMA functions;
D PRAGMA functions;
┌────────────┬─────────┬────────────────────────┬─────────┬─────────────┬──────────────┐
│ name │ type │ parameters │ varargs │ return_type │ side_effects │
│ varchar │ varchar │ varchar[] │ varchar │ varchar │ boolean │
├────────────┼─────────┼────────────────────────┼─────────┼─────────────┼──────────────┤
│ !__postfix │ SCALAR │ [INTEGER] │ │ HUGEINT │ false │
│ !~~ │ SCALAR │ [VARCHAR, VARCHAR] │ │ BOOLEAN │ false │
│ !~~* │ SCALAR │ [VARCHAR, VARCHAR] │ │ BOOLEAN │ false │
│ % │ SCALAR │ [SMALLINT, SMALLINT] │ │ SMALLINT │ false │
│ % │ SCALAR │ [UBIGINT, UBIGINT] │ │ UBIGINT │ false │
│ % │ SCALAR │ [UINTEGER, UINTEGER] │ │ UINTEGER │ false │
...
-- 表結(jié)構(gòu)
D PRAGMA table_info('t1');
+-----+------+---------+---------+------------+-------+
| cid | name | type | notnull | dflt_value | pk |
+-----+------+---------+---------+------------+-------+
| 0 | a | INTEGER | false | | false |
| 1 | b | INTEGER | false | | false |
+-----+------+---------+---------+------------+-------+
-- 版本與平臺
D PRAGMA version;
+-----------------+------------+
| library_version | source_id |
+-----------------+------------+
| v0.10.1 | 4a89d97db8 |
+-----------------+------------+
D PRAGMA platform;
+---------------+
| platform |
+---------------+
| windows_amd64 |
+---------------+
-- Profiling
PRAGMA enable_profiling;
SET profiling_mode = 'detailed';
SET enable_profiling = 'query_tree'; //logical query plan:
SET enable_profiling = 'query_tree_optimizer'; //physical query plan:
PRAGMA disable_profiling;
-- Optimizer
PRAGMA disable_optimizer;
PRAGMA enable_optimizer;
-- Storage Info
D PRAGMA storage_info('t1');
+--------------+-------------+-----------+-------------+------------+--------------+-------+-------+--------------+------------------------------------------------------+-------------+------------+----------+--------------+--------------+
| row_group_id | column_name | column_id | column_path | segment_id | segment_type | start | count | compression | stats | has_updates | persistent | block_id | block_offset | segment_info |
+--------------+-------------+-----------+-------------+------------+--------------+-------+-------+--------------+------------------------------------------------------+-------------+------------+----------+--------------+--------------+
| 0 | a | 0 | [0] | 0 | INTEGER | 0 | 3 | Uncompressed | [Min: 1, Max: 3][Has Null: false, Has No Null: true] | false | true | 1 | 0 | |
| 0 | a | 0 | [0, 0] | 0 | VALIDITY | 0 | 3 | Constant | [Has Null: false, Has No Null: true] | false | true | -1 | 0 | |
| 0 | b | 1 | [1] | 0 | INTEGER | 0 | 3 | Uncompressed | [Min: 1, Max: 3][Has Null: false, Has No Null: true] | false | true | 1 | 16 | |
| 0 | b | 1 | [1, 0] | 0 | VALIDITY | 0 | 3 | Constant | [Has Null: false, Has No Null: true] | false | true | -1 | 0 | |
+--------------+-------------+-----------+-------------+------------+--------------+-------+-------+--------------+------------------------------------------------------+-------------+------------+----------+--------------+--------------+
11).性能調(diào)優(yōu)
DuckDB 性能調(diào)優(yōu)主要涉及到參數(shù)、執(zhí)行計劃等。這里簡單說明下使用 Explain 命令查看執(zhí)行計劃。
-- 查看執(zhí)行計劃
D explain select deptno,count(*) from big_emp group by deptno;
┌─────────────────────────────┐
│┌───────────────────────────┐│
││ Physical Plan ││
│└───────────────────────────┘│
└─────────────────────────────┘
┌───────────────────────────┐
│ PROJECTION │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│__internal_decompress_integ │
│ ral_integer(#0, 1) │
│ #1 │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ PERFECT_HASH_GROUP_BY │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ #0 │
│ count_star() │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ PROJECTION │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ deptno │