Featured image of post 用 DuckDB 搭建 数据湖即服务 :一条 SQL 查询所有数据源,按查询量赚钱

用 DuckDB 搭建 数据湖即服务 :一条 SQL 查询所有数据源,按查询量赚钱

用 DuckDB 的 ATTACH 能力搭建零 ETL 的统一查询层,让客户用一条 SQL 关联查询 PostgreSQL、S3 Parquet、HTTP API 等所有数据源。按查询量或 SaaS 订阅收费,边际成本趋近于零。

数据湖即服务架构

一、这个生意的逻辑是什么?

大多数中小企业的核心困境是:数据散落在各处,却没法一起用。

订单在 PostgreSQL 里,日志在 S3 的 Parquet 文件里,Excel 报表在共享盘里,API 接口拿到的实时数据在缓存里。传统做法需要一个数据工程师搭 ETL、建数仓、写调度脚本——成本动辄数万,周期数周。

如果你提供一个"数据湖即服务":客户把你给的所有数据源接入同一个 DuckDB 实例,用一条 SQL 就能关联查询所有数据。你按查询量或存储量收费。

核心壁垒:DuckDB 的 ATTACH + httpfs + 直接读 Parquet 的能力,让你不需要任何 ETL 就能实现"数据不动、查询动"。客户不需要迁移数据,你的运维成本几乎为零。

二、技术架构:零 ETL 的统一查询层

客户数据源                          你的服务
┌──────────────┐
│ PostgreSQL   │ ──ATTACH postgres──┐
└──────────────┘                   │
┌──────────────┐                   │
│ S3/OSS       │ ──ATTACH s3───────┼── DuckDB Engine
│ Parquet 文件  │   read_parquet()  │   (Serverless)
└──────────────┘                   │
┌──────────────┐                   │
│ HTTP API     │ ──ATTACH httpfs───┘
│ JSON/CSV     │   read_json_auto()
└──────────────┘

每一层都不需要搬运数据。DuckDB 直接在数据源上执行谓词下推(predicate pushdown),只读取需要的列和行。

三、完整代码实现

Step 1:搭建统一查询引擎

import duckdb

# Serverless 模式:内存数据库,用完即销毁
con = duckdb.connect(":memory:")

# 注册 PostgreSQL 数据源(客户的生产库)
con.execute("""
    ATTACH 'postgresql://user:***@host:5432/orders' AS orders (TYPE POSTGRES);
""")

# 注册 S3/OSS 上的 Parquet 数据湖
con.execute("""
    ATTACH 's3://my-data-lake/' AS datalake (TYPE duckdb_io_s3, REGION us-east-1);
""")

# 注册 HTTP 数据源(外部 API)
con.execute("""
    ATTACH 'http://api.example.com/data/' AS api_data (TYPE httpfs);
""")

Step 2:跨源关联查询——核心卖点

SELECT 
    o.order_id,
    o.customer_id,
    o.amount,
    p.category,
    p.unit_cost,
    o.order_date,
    ROUND(o.amount - p.unit_cost * o.quantity, 2) AS profit,
    weather.temperature,
    weather.rainfall
FROM orders.main.orders o
JOIN datalake.products.parquet p 
    ON o.product_id = p.product_id
LEFT JOIN api_data.weather_data weather
    ON DATE(o.order_date) = weather.date
WHERE o.order_date >= '2026-01-01'
ORDER BY profit DESC
LIMIT 100;

这段 SQL 的执行流程

  1. DuckDB 解析 SQL,生成物理计划
  2. orders 表的 order_date 谓词下推到 PostgreSQL,只拉取 2026 年的数据
  3. datalake/products.parquet 利用 Parquet 的 zone map 做分区裁剪
  4. api_data 发起 HTTP 请求获取天气数据
  5. 内存中执行 JOIN 和计算
  6. 返回结果

整个过程没有一次数据全量搬运

Step 3:封装为可复用的报告模板

def generate_profit_report(start_date, end_date, region=None):
    sql = """
    SELECT 
        p.region,
        p.category,
        COUNT(*) AS order_count,
        SUM(o.amount) AS total_revenue,
        SUM(o.amount - p.unit_cost * o.quantity) AS total_profit,
        AVG(o.amount - p.unit_cost * o.quantity) AS avg_margin,
        PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY o.amount) AS median_order_value
    FROM orders.main.orders o
    JOIN datalake.products.parquet p 
        ON o.product_id = p.product_id
    WHERE o.order_date BETWEEN '{start}' AND '{end}'
    """.format(start=start_date, end=end_date)
    
    if region:
        sql += f" AND p.region = '{region}'"
    
    sql += " GROUP BY ALL ORDER BY total_profit DESC"
    
    result = con.execute(sql).fetchdf()
    return result

Step 4:自动化定时报告

import duckdb
import smtplib
from email.mime.text import MIMEText
from datetime import datetime, timedelta

def daily_report():
    con = duckdb.connect(":memory:")
    con.execute("ATTACH 'postgresql://user:***@host:5432/orders' AS orders (TYPE POSTGRES)")
    con.execute("ATTACH 's3://my-data-lake/' AS datalake (TYPE duckdb_io_s3, REGION us-east-1)")
    
    yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
    
    sql = f"""
    SELECT 
        DATE(order_date) AS sale_date,
        COUNT(*) AS orders,
        SUM(amount) AS revenue,
        SUM(amount) - SUM(unit_cost * quantity) AS profit
    FROM orders.main.orders o
    JOIN datalake.products.parquet p ON o.product_id = p.product_id
    WHERE DATE(order_date) = '{yesterday}'
    GROUP BY ALL
    """
    
    result = con.execute(sql).fetchdf()
    
    html = f"""
    <h2>📊 昨日经营日报 | {yesterday}</h2>
    <p>订单数:<b>{result['orders'].sum()}</b></p>
    <p>营收:<b>¥{result['revenue'].sum():,.2f}</b></p>
    <p>利润:<b>¥{result['profit'].sum():,.2f}</b></p>
    <p>利润率:<b>{result['profit'].sum()/result['revenue'].sum()*100:.1f}%</b></p>
    """
    
    msg = MIMEText(html, 'html')
    msg['Subject'] = f'经营日报 {yesterday}'
    msg['From'] = '[email protected]'
    msg['To'] = '[email protected]'
    
    with smtplib.SMTP('smtp.company.com', 587) as server:
        server.starttls()
        server.login('[email protected]', 'password')
        server.send_message(msg)

四、与传统方案的对比

方案数据迁移开发周期运维成本扩展性
传统 ETL + 数仓需要全量搬运2-4 周高(调度、监控)
Flink/Kafka 实时管道需要改造1-3 月极高
DuckDB ATTACH 零 ETL不需要1-2 天极低

DuckDB 方案的优势在于:客户现有的数据基础设施完全不用动。你只是在上面加了一层 SQL 查询接口。对客户来说风险极低——没有任何沉没成本损失。

五、变现路径详解

路径一:按查询次数收费(轻量级)

适合初期验证市场:

  • 为 10 家中小企业提供统一数据查询服务
  • 每家每月 500 次查询 × ¥0.5/次 = ¥250/家
  • 月收入 ¥2,500,边际成本几乎为零(DuckDB 是 serverless 的)

获客策略:在知识星球/社群分享"一条 SQL 查所有数据源"的技巧,吸引第一批用户。

路径二:SaaS 化(规模化)

将上述代码封装为 Web 应用:

  1. FastAPI 提供 SQL 查询 API
  2. Streamlit 提供可视化前端
  3. 客户上传数据源配置 → 你托管 DuckDB 实例 → 客户通过 Web 界面写 SQL
  4. 定价:¥999/月/企业,目标 50 家 = ¥50,000/月

技术栈

# FastAPI 示例
from fastapi import FastAPI, HTTPException
import duckdb

app = FastAPI()

@app.post("/query")
def run_query(config: dict, sql: str):
    con = duckdb.connect(":memory:")
    for source in config.get("sources", []):
        con.execute(f"ATTACH ...")
    try:
        result = con.execute(sql).fetchdf()
        return {"columns": result.columns.tolist(), "data": result.values.tolist()}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

路径三:数据产品(高附加值)

将查询结果打包为行业报告:

  • 一份深度行业利润分析报告定价 ¥299
  • 月销 100 份 = ¥29,900
  • 边际成本为零,一次开发反复售卖

关键:选择利润空间大、数据透明度低的行业(如跨境电商、本地生活服务)。

六、关键注意事项

  1. 安全性:ATTACH 的凭证必须加密存储,不要硬编码。推荐使用环境变量或密钥管理服务。
  2. 性能:对于超大数据集,先用 EXPLAIN ANALYZE 查看执行计划,确认谓词下推生效。
  3. 成本:DuckDB 是 CPU 密集型,选择合适规格的实例(8核以上性价比最高)。
  4. 并发:DuckDB 原生支持多线程,但 ATTACH 远程数据源时注意并发连接数限制。建议在 API 层做连接池管理。
  5. 超时控制:HTTP API 数据源响应慢,设置合理的 timeout 参数避免阻塞。

七、为什么现在入场正是时候?

DuckDB 在 2026 年已经具备了生产级数据湖查询能力:

  • PostgreSQL 扩展duckdb_attach_postgres() 直接连接生产库
  • S3/OSS 原生支持duckdb_io_s3 类型无需额外配置
  • httpfs 扩展:直接查询远程 JSON/CSV/Parquet
  • Parquet 谓词下推:自动裁剪不需要的列和数据块
  • GROUP BY ALL:简化聚合查询,减少 80% 代码量

这些能力的组合,让"数据湖即服务"从一个概念变成了今天就能上线的产品

八、变现建议总结

阶段动作预期收入时间投入
第 1 周搭建 MVP,测试 3 个数据源¥0(验证期)1-2 天
第 2-4 周找 3-5 家种子客户免费试用¥0(换反馈)每周 5 小时
第 2 个月开始收费,目标 10 家客户¥2,500-5,000/月每周 10 小时
第 3-6 个月SaaS 化,扩展至 50 家¥10,000-50,000/月每周 15 小时
第 6 个月后推出行业报告产品¥30,000-80,000/月边际成本趋零

用 DuckDB 做数据湖查询层,最大的优势就是不需要迁移数据。客户的现有数据基础设施完全不用动,你只是在上面加了一层 SQL 查询接口。这对客户来说风险极低——没有任何沉没成本损失,对你来说是极低的获客阻力。

📖 本文涉及的全部代码模板和部署脚本已整理成完整教程,包含 S3 认证配置、PostgreSQL 安全连接、Streamlit 前端封装等细节,详见 duckdblab.org

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

⚠️ 本站为独立社区项目,与 DuckDB 基金会及 DuckDB 官方项目无任何从属、背书或赞助关系。

"DuckDB" 是 DuckDB 基金会的注册商标,本站仅以事实描述方式使用该名称。

本站内容仅供教育与社区推广用途,不构成任何商业服务。