import sqlite3
import pandas as pd
# Load CSV into SQLite
df = pd.read_csv("sales.csv")
conn = sqlite3.connect("sales.db")
df.to_sql("sales", conn, if_exists="replace", index=False)# Run a GROUP BY query directly on the imported table
cursor = conn.execute("""
SELECT customer_id, SUM(amount) AS total_spent
FROM sales
GROUP BY customer_id
ORDER BY total_spent DESC
LIMIT 5
""")print(cursor.fetchall())技术支持
DuckDB(Python):
用 DuckDB 在 Python 中运行类似查询,步骤更少。无需 DataFrame。
示例代码如下:
import duckdb
con = duckdb.connect()# Run a GROUP BY query directly on the CSV file
result = con.execute(""" SELECT customer_id, SUM(amount) AS total_spent FROM read_csv_auto('sales.csv') GROUP BY customer_id ORDER BY total_spent DESC LIMIT 5 """).fetchdf()print(result)技术支持
import duckdb
con = duckdb.connect()
result = con.execute("""
SELECT customer_id, SUM(amount) AS total_spent
FROM 'sales.parquet'
GROUP BY customer_id
ORDER BY total_spent DESC
LIMIT 5
""").fetchdf()print(result)技术支持
SQLite
SQLite 在运行查询前,需要将外部数据导入为其自身的存储格式。
import sqlite3
import pandas as pd
# Load CSV into pandas first
df = pd.read_csv("sales.csv")# Store into SQLite
conn = sqlite3.connect("sales.db")
df.to_sql("sales", conn, if_exists="replace", index=False)# Query the imported table
cursor = conn.execute("""
SELECT customer_id, SUM(amount) AS total_spent
FROM sales
GROUP BY customer_id
ORDER BY total_spent DESC
LIMIT 5
""")print(cursor.fetchall())技术支持
import duckdb
import pandas as pd
# Example pandas DataFrame
df = pd.DataFrame({"customer_id":[1,2,1,3],"amount":[100,200,150,300]})# Query DataFrame directly with DuckDB
result = duckdb.query("""
SELECT customer_id, AVG(amount) as avg_spent
FROM df
GROUP BY customer_id
""").to_df()print(result)技术支持
SQLite
SQLite 可与多种编程语言配合,但相对而言对数据科学不那么“中心化”。
例如,SQLite 也能与 pandas 交互,但通常需要更多样板代码。
import sqlite3
import pandas as pd
conn = sqlite3.connect(":memory:")
df = pd.DataFrame({"customer_id":[1,2,1,3],"amount":[100,200,150,300]})
df.to_sql("sales", conn, index=False, if_exists="replace")# Query back into pandas
query ="SELECT customer_id, AVG(amount) as avg_spent FROM sales GROUP BY customer_id"
result = pd.read_sql_query(query, conn)print(result)技术支持
3. 语言绑定与 API
SQLite
SQLite 自 2000 年问世以来,其绑定极为成熟。由于其仅是小型 C 库,大多数语言都有官方或社区支持。
import duckdb, pathlib
db = pathlib.Path("warehouse.duckdb")
con = duckdb.connect(str(db))# EXTRACT
con.execute("""
CREATE OR REPLACE VIEW v_orders AS
SELECT * FROM read_csv_auto('data/orders.csv')
""")# TRANSFORM
con.execute("""
CREATE OR REPLACE VIEW v_clean AS
SELECT CAST(order_id AS BIGINT) order_id,
CAST(customer_id AS BIGINT) customer_id,
TRY_CAST(order_ts AS TIMESTAMP) order_ts,
COALESCE(NULLIF(status,''),'unknown') status,
TRY_CAST(amount AS DOUBLE) amount
FROM v_orders
""")# LOAD (table + parquet export)
con.execute("CREATE TABLE IF NOT EXISTS fact_orders AS SELECT * FROM v_clean WHERE 1=0")
con.execute("""
MERGE INTO fact_orders t USING v_clean s
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET customer_id=s.customer_id, order_ts=s.order_ts, status=s.status, amount=s.amount
WHEN NOT MATCHED THEN INSERT VALUES (s.order_id, s.customer_id, s.order_ts, s.status, s.amount)
""")
con.execute("""
COPY (SELECT DATE_TRUNC('day', order_ts) AS order_date, SUM(amount) AS daily_gmv FROM fact_orders GROUP BY 1)
TO 'warehouse/daily_gmv' (FORMAT PARQUET, PARTITION_BY (order_date), OVERWRITE_OR_IGNORE 1)
""")技术支持
3. 混合与互补场景
在 DuckDB 中分析 SQLite 数据:
DuckDB 可直接查询 SQLite 数据库文件中的数据:
import duckdb
# Query a SQLite database via the sqlite_scanner extension
con = duckdb.connect()
con.install_extension("sqlite_scanner")
con.load_extension("sqlite_scanner")
result = con.execute("""
SELECT customer_id, COUNT(*) AS orders
FROM sqlite_scan('app_cache.db', 'orders')
GROUP BY customer_id
ORDER BY orders DESC
""").fetchdf()print(result)技术支持
import sqlite3
conn = sqlite3.connect("perf.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS logs (id INTEGER, message TEXT)")# Bulk insert with a single transaction
data =[(i,f"event {i}")for i inrange(100000)]
cur.execute("BEGIN TRANSACTION;")
cur.executemany("INSERT INTO logs VALUES (?, ?)", data)
conn.commit()技术支持
import duckdb
con = duckdb.connect()
# Bulk load CSV into DuckDB
con.execute(""" CREATE OR REPLACE TABLE logs AS SELECT * FROM read_csv_auto('transactions.csv') """)技术支持
2. 查询优化技巧
SQLite:
为更佳的查询优化,请为查找创建索引。
CREATEINDEX idx_customer_id ON orders(customer_id);技术支持
import duckdb
import math
con = duckdb.connect()# Register a Python function as UDF
con.create_function("sqrt_plus",lambda x: math.sqrt(x)+1)
result = con.execute("SELECT sqrt_plus(16)").fetchall()print(result)# [(5.0,)]技术支持
在现代数据管理中,轻量级嵌入式数据库在支持应用、工作流和分析方面发挥着关键作用。经常被拿来比较的两个系统是DuckDB 和 SQLite。
二者同为嵌入式数据库,但用途不同:SQLite 擅长事务型负载,而 DuckDB 则针对分析型查询进行了优化。
在本文的对比中,我们将看看这两种数据库之间的差异。
一览对比:DuckDB vs SQLite
下表展示了 DuckDB 与 SQLite 的相同点与不同点。完整细节请参见文中的逐项说明。
什么是 SQLite?
SQLite 是全球部署最广泛的数据库之一。自 2000 年发布,SQLite 被设计为轻量级、无服务器 的 SQL 数据库引擎。
其主要特性包括:
由于其轻量级架构,可用于如下场景:
SQLite 的主要强项在于简洁与可靠。开发者可以随应用一同分发基于单文件的数据库,而无需担心外部依赖。
什么是 DuckDB?
DuckDB 常被称为“面向分析的 SQLite”。2019 年推出,DuckDB 诞生于 CWI。其主要目标是提供一种进程内的OLAP 数据库方案。
其主要特性包括:
典型用例包括:
使用 DuckDB 的关键点在于:它以嵌入式形态提供了分析型数据库(如 Snowflake 或 BigQuery)的能力。
DuckDB 与 SQLite:关键差异与相似点
1. 安装与依赖
SQLite
SQLite 以“无依赖”著称。它可编译为单一 C 库,几乎可嵌入到任意应用中。
安装极为简便,可直接从 SQLite 官网下载。
可移植性是其最大优势之一:在 Windows、macOS、Linux、iOS 与 Android 上几乎无需额外工作即可保持一致表现。
DuckDB
DuckDB 同样易于安装,但通常以独立二进制或Python/R 包的形式分发,而非普遍预装。
当启用与 Apache Arrow、Parquet 或 Pandas 的集成时,会有更多依赖。
整体体积仍然小巧,但若需云存储连接器或数据科学工具集成等高级功能,相比 SQLite,DuckDB 的设置会稍多一些。
2. 存储格式与架构
SQLite
以行式格式存储数据,针对OLTP(联机事务处理)任务(如插入、更新、删除)优化。
检索整行速度很快,但当分析型查询仅需扫描几列却跨越数百万行时,因读取了不必要的数据而可能变慢。
DuckDB
DuckDB 采用列式存储引擎,专为 OLAP(联机分析处理)打造。
列式格式意味着对于在大数据集上进行聚合的查询(例如
AVG(sales)或COUNT(*)),仅需将相关列读入内存,因此速度更快。同时还能实现更好的压缩与向量化执行以提升速度。
示例:
SELECT * FROM sales WHERE customer_id=123在 SQLite 上更快,而SELECT AVG(amount) FROM sales GROUP BY region在 DuckDB 上快得多。3. 事务型 vs 分析型负载
两者在负载类型上也有差异:
SQLite
SQLite 针对 OLTP(插入、更新、删除、点查)优化。
例如:
DuckDB
DuckDB 针对 OLAP(聚合、连接、扫描)优化。
例如:
4. SQL 支持与语法
接下来看看它们的 SQL 语法。两者都实现了 SQL 标准的大部分并有所扩展;均未完全符合 ANSI 标准。
SQLite
语法示例如下:
DuckDB
DuckDB 的类似语法示例如下:
SQLite 与 DuckDB:底层架构与设计理念
从底层架构看,SQLite 与 DuckDB 都是嵌入式、进程内数据库,但其设计理念有所不同。
SQLite 注重事务的简洁与可靠,而 DuckDB 则针对分析型负载与现代数据科学集成进行了优化。
1. 存储架构
首先,两者在存储架构上有所区别。
SQLite
SQLite 采用行存,非常适合事务性访问。这意味着整行数据会被一起存放在磁盘上。
该设计有利于事务型负载(OLTP),因为插入、更新或检索单条记录通常只需访问一行。
需要对单条记录(如用户资料或订单详情)进行快速且一致访问的应用,在此模型下会有良好表现。
DuckDB
DuckDB 采用列式格式,旨在快速扫描数十亿行。列式格式意味着同一列的值在磁盘与内存中被归组存储。
列式存储使分析型查询(OLAP)非常高效,因为聚合、过滤和扫描通常只需访问列的子集。
压缩技术在列上效果更佳,从而降低内存与存储占用,并提升扫描性能。
2. 查询处理方法
接着,我们看看它们如何处理查询。
总体而言,SQLite 的模型追求简洁与事务正确性,而 DuckDB 的向量化引擎则为吞吐量与分析性能而生。
SQLite
SQLite 采用传统的迭代器模型,沿查询管线逐行处理。
这种逐行执行方式轻量,契合事务型负载,因为查询往往涉及小数据集并伴随频繁的插入或更新。
但当查询必须扫描大型数据集时,逐行评估会引入开销并导致性能瓶颈。
DuckDB
DuckDB 采用向量化查询执行,一次处理成批(向量)行,而非逐行处理。
这种方式可最小化 CPU 开销,更好地利用现代 CPU 缓存,并允许SIMD(单指令多数据)并行。
向量化执行对分析尤为有利:在大数据量上,聚合、连接与过滤等操作会显著加速。
在 DuckDB 中,下面的查询受益于列式存储与向量化处理,速度显著更快。
3. 进程内数据库架构
最后,SQLite 与 DuckDB 均为嵌入式数据库,即运行在宿主应用的进程内,而非独立的数据库服务器。
这种架构消除了网络延迟,简化了部署,降低了管理外部服务的运维负担。
其优势包括:
SQLite
SQLite 首创了“无服务器”数据库的理念。这意味着应用程序直接链接其库,所有查询均在应用内执行。
DuckDB
DuckDB 采用类似的进程内设计,但将其应用于分析场景。
为避免启动外部分析引擎的开销,DuckDB 通过在数据科学环境(Python、R,甚至 Jupyter 笔记本)中运行查询来发挥作用。
DuckDB vs SQLite 性能分析与基准
性能是 DuckDB 与 SQLite 的重要分水岭。尽管都是嵌入式数据库,但各自的设计优先级决定了其在不同负载下的表现。
总体而言,SQLite 在事务型用例上表现强劲,而 DuckDB 在分析场景中表现出色。
1. 分析型查询性能
DuckDB:
我们通过一个聚合查询来看看如何测试。
假设我们有一个包含 1000 万行的销售数据集(
sales.csv),含有order_id、customer_id、amount与date。SQLite(Python):
要在 Python 中用 SQLite 运行该查询,我们需要先将 CSV 加载到 DataFrame,再写入
SQLite3连接的数据库中,并在其中运行 SQL 查询。示例代码如下:
DuckDB(Python):
用 DuckDB 在 Python 中运行类似查询,步骤更少。无需 DataFrame。
示例代码如下:
2. 事务型负载性能
在事务型负载方面,两者各有其用武之地。
SQLite 针对事务密集、点查询负载进行了优化。其在频繁的 INSERT、UPDATE、DELETE 操作中开销极低,非常适合嵌入式事务系统。
SQLite 对使用事务的批量插入支持高效,并在需要频繁进行小更新时表现良好。
相较之下,DuckDB 更专注于数据摄入后的分析查询,而非高频率的事务更新。
3. 可扩展性与资源利用
SQLite
SQLite 轻量,常用于中小型数据集。单条查询在单线程上执行,并采用单写多读模型;其不会对单个 SELECT 并行化,这会限制分析吞吐量。
DuckDB
DuckDB 支持多核并行与超内存查询。它通过并行执行进行扩展,并在需要时回退到磁盘,从而实现外存分析。
这使其能够分析远超系统内存的数据集,同时维持接近内存内执行的性能。
4. 文件格式与 I/O 性能
SQLite
SQLite 将数据存储在其专有的行式格式中,具备可移植与稳定的优点,但缺乏与现代分析格式的直接互操作性。通常需要先将数据导入 SQLite,才能运行查询,这会增加额外的 I/O 开销。
DuckDB
DuckDB 原生支持包括 Parquet、Arrow、CSV 在内的多种文件格式,可直接查询数据而无需转换(例如 SELECT … FROM 'file.parquet')。
这消除了 ETL 开销,并通过仅从列式格式(如 Parquet)中读取所需数据来加速分析。
DuckDB 与 SQLite 的数据摄入与互操作性
数据库常用于摄入大量数据。接下来,我们看看两者在摄入能力与互操作性方面的表现。
1. 支持的文件格式与摄入
DuckDB
DuckDB 原生支持摄入 Parquet、Arrow 与 CSV。它可以直接在 Parquet 上运行 SQL,而无需先导入至数据库文件。
SQLite
SQLite 在运行查询前,需要将外部数据导入为其自身的存储格式。
2. 与数据科学工具的集成
DuckDB
DuckDB 与 pandas、R、Jupyter 紧密集成,并可直接集成 pandas DataFrame,将其视为 SQL 表:
SQLite
SQLite 可与多种编程语言配合,但相对而言对数据科学不那么“中心化”。
例如,SQLite 也能与 pandas 交互,但通常需要更多样板代码。
3. 语言绑定与 API
SQLite
SQLite 自 2000 年问世以来,其绑定极为成熟。由于其仅是小型 C 库,大多数语言都有官方或社区支持。
例如:C、Python(
sqlite3)、Java、PHP 等。DuckDB
DuckDB 年代更短,但以数据科学与分析工作流为核心进行设计。其绑定种类虽少,但非常友好于数据科学。
例如:Python、R、C++、JavaScript 绑定。
使用场景与应用领域
尽管 SQLite 与 DuckDB 都是轻量级嵌入式数据库,但用途截然不同。
SQLite 在事务型、应用级场景中表现突出,而 DuckDB 则针对数据科学和商业智能中的分析型负载进行了优化。在某些情况下,它们甚至可以相互补充。
下面来看一些具体示例:
1. SQLite 的应用场景
SQLite 十分轻量,特别适合:
2. DuckDB 的分析型应用
DuckDB 与 ETL 流水线等分析型工作流高度契合。以下是其在 Python 中的实现示例。
使用 DuckDB CLI:
然后在终端中运行该文件:
或者,您也可以在 Python 内使用 DuckDB,打造更顺畅的数据分析工作流。
3. 混合与互补场景
在 DuckDB 中分析 SQLite 数据:
DuckDB 可直接查询 SQLite 数据库文件中的数据:
性能优化与最佳实践
SQLite 与 DuckDB 本身都很高效,但它们的性能会因数据的摄入、查询与维护方式而有较大差异。
1. 批量插入与高效数据处理
SQLite
对于大规模数据装载,请务必使用事务。逐行且不使用事务地插入会使 SQLite 每行单独提交,性能会大幅下降。
像上述这样进行批量处理可显著提升性能。
DuckDB
DuckDB 对来自外部文件的大数据集摄入效率很高。使用
COPY命令或直接查询 Parquet/CSV 可加速流水线。2. 查询优化技巧
SQLite:
为更佳的查询优化,请为查找创建索引。
建议使用
EXPLAIN QUERY PLAN分析执行路径并识别缺失索引。总体上,在资源受限环境中尽量保持查询简洁,避免不必要的连接。
DuckDB
对于 DuckDB,可利用列裁剪与谓词下推。
列裁剪示例:
谓词下推(Parquet)示例:
3. 维护与运维考量
VACUUM回收空间。对于嵌入式应用,确保足够的磁盘 I/O,并使用 WAL 模式以提升耐久性与并发访问。开发、集成与生态
数据库需要良好的集成,才能确保数据易于访问。下面我们看看各数据库与其他系统的集成情况。
1. 编程语言支持与 API
2. 数据科学与分析集成
DuckDB:可直接与 pandas DataFrame 协作。
示例:
DuckDB 还支持以 SQL 与 Python 编写用户自定义函数(UDF),以实现高级分析与自定义转换。
示例(DuckDB 中的 Python UDF):
3. 云与基础设施集成
SQLite
SQLite 是以本地为先的数据库,主要用于本地嵌入式存储。
在云原生应用中,它常作为轻量级持久层或缓存,常见于容器内部。尽管缺乏直接的云存储连接器,但 SQLite 数据库具有可移植性,易于在环境之间分发。
DuckDB
DuckDB 因针对现代数据架构进行了优化(如查询 S3 上的 Parquet 文件),正在被越来越多地用于云端。
它还原生支持在 AWS S3、Azure Blob Storage 或 Google Cloud Storage 等云存储上的 Parquet 与 Arrow 文件进行查询。
DuckDB 非常适合无服务器分析场景:数据存放在对象存储中,按需查询,无需重量级数仓。
局限与权衡
尽管二者各有优势,但 DuckDB 与 SQLite 也都存在与其设计理念相关的内在约束。
SQLite 与 DuckDB 总结
DuckDB 与 SQLite 都很有价值,但角色不同:
在许多场景中,两者可相辅相成:SQLite 负责存储与事务,DuckDB 在其之上执行分析查询。选择取决于您的负载更偏向 OLTP(事务)还是 OLAP(分析)。
想进一步了解像 DuckDB 与 SQLite 这样的数据库?欢迎查看我们的 SQLite 新手指南 教程,以及我们的 DuckDB SQL 入门 跟学教程。