Databricks 隐藏功能揭秘:你可能不知道的用法

data-science进阶8 分钟阅读2026/10/7

Databricks 隐藏功能揭秘:你可能不知道的用法

从一个加班夜晚说起

事情是这样的:去年我接手了一个团队的数据管道,前任同事留下的 notebook 有一百多个,密密麻麻堆在一个 workspace 里,连个像样的命名规范都没有。每天凌晨的定时任务失败了,我得翻半天日志才能定位到是哪个 notebook 的哪个 cell 出了问题。就是在那个反复救火的阶段,我开始深挖 Databricks 一些官方文档里写得很简略、甚至完全没提的功能。下面这些是我实际用下来觉得最值的几个,每一个都帮我省过真金白银的时间。

1. %run 之外的魔法:dbutils.notebook 的链式调用与传参

大多数人知道 %run ./shared/utils 可以复用代码,但很少人善用 dbutils.notebook.run()。它能把另一个 notebook 当成函数调用,还支持传参和拿返回值:

result = dbutils.notebook.run(
    "./etl/clean_orders",
    timeout_seconds=600,
    arguments={"date": "2024-06-01", "env": "prod"}
)
print(result)  # 子 notebook 里 dbutils.notebook.exit(json.dumps(metrics)) 的内容

关键在于子 notebook 末尾用 dbutils.notebook.exit() 退出,返回值就是那个字符串。我一般传 JSON,父 notebook 里 json.loads 一下就能拿到清洗后的行数、异常计数这些指标,用于熔断判断。

踩过的坑:%run 是把代码“粘贴”进来,变量共享;dbutils.notebook.run 是独立任务,起独立 cluster 上下文。我一开始以为两者等价,结果子 notebook 里改的变量在父 notebook 里“神秘失效”,排查了半小时才反应过来。

2. 魔术命令 %sql 里直接引用 Python 变量

这个小功能文档里就一句话,但实用性极高:

threshold = 1000
SELECT * FROM orders WHERE amount > ${threshold}

同一个 notebook 里 Python 定义的变量,切到 SQL cell 直接用 ${var} 引用。更进阶的是用 {} 做条件拼接:

SELECT * FROM orders
WHERE 1=1
{ if (start_date) } AND dt >= '${start_date}' { end }

这个语法在把 notebook 变成交互式分析工具时特别好用——配合 Databricks 的参数文本框,业务同事自己就能改参数跑查询。

3. spark.conf.set 临时关闭几个“吞钱”的行为

有次我跑一个 800GB 的 join,job 反复 shuffle 失败。后来发现这几行救命配置:

# 关闭自适应查询执行中过于激进的分区合并,手动控制
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "false")

# 允许 Delta 写入时动态调整 shuffle 分区
spark.conf.set("spark.sql.shuffle.partitions", "1600")

# Databricks 特有:跳过无用的 schema 校验开销
spark.conf.set("spark.databricks.delta.optimizeWrite.enabled", "true")
spark.conf.set("spark.databricks.delta.autoCompact.enabled", "true")

后两行是 Databricks 特有的,optimizeWrite 能在写入时自动做小文件合并。我们的 Delta 表曾经膨胀到几万个小文件,查询慢得离谱,开了这个之后写入即优化,配合定期跑 OPTIMIZE table ZORDER BY (dt),查询时间从 40 分钟降到 6 分钟。

4. %pip 和 init script 的正确姿势

需要装第三方库时,别去 cluster 设置里改(那要重启集群),notebook 里直接:

%pip install snowflake-connector-python==3.7.0

但要记住:%pip 装的库在作业集群(job cluster)里每次都会丢。正确做法是写成 cluster 的 init script 存在 DBFS 里:

# dbfs:/scripts/init.sh
#!/bin/bash
/databricks/python/bin/pip install snowflake-connector-python==3.7.0

我犯过的错:生产 job 里用 %pip install(没锁版本),某天上游包发了个不兼容的大版本,凌晨任务全线崩溃。从那以后所有依赖必须锁版本。

5. Workspace 的 Git 集成 + Repo 功能

Databricks 的 notebook 可以直接关联 Git 仓库(Repo / Git folders)。设置路径:Workspace → Add → Repo,填 Git URL 和凭据。这样 notebook 版本管理走 Git,再也不用靠 notebook 自带的 Revision history 猜“到底哪个版本在生产跑的”。

个人建议的目录结构:

/repo
  /etl        # 生产 notebook
  /lib        # 共享工具函数
  /tests      # 简单的 pytest(Databricks 支持 %pip install pytest 后跑单测)

6. 一个几乎没人提的:EXPLAIN 与 Query Profile

慢查询别只盯着 Spark UI 的 DAG。SQL cell 跑完后点底部的 Query Profile 视图,能看到物理计划的每一行扫描了多少数据、有没有 broadcast 掉了。我有一次发现一个 join 没走 broadcast,两张表加起来 2TB 在 shuffle——加上一行 spark.conf.set("spark.sql.autoBroadcastJoinThreshold", 200*1024*1024)(调大到 200MB),任务从 50 分钟掉到 11 分钟。

实用建议汇总

  1. 定时任务全部用 Jobs + job cluster,别挂在 all-purpose cluster 上,成本差 2 倍以上。
  2. 所有依赖锁版本,用 init script 或 cluster policy 固定。
  3. 子 notebook 通信用 dbutils.notebook.run + JSON 返回值,别靠全局变量。
  4. Delta 表开启 optimizeWrite,定期 OPTIMIZE + ZORDER。
  5. 慢查询先看 Query Profile,再看 Spark UI。

诚实的局限

这些功能也有不爽的地方:dbutils.notebook.run 不能并行(并行要用 subprocess + 多线程,复杂不少);${var} 语法在复杂 SQL 里容易和特殊字符冲突,要转义;%pip 装大依赖每次都要下载,慢。另外 Databricks 迭代太快,UI 一更新我写的截图教程就得重做——比如 Repo 现在改叫 Git folders 了。总体来说,这个平台的深度功能值得你花一个下午专门探索,回报率很高。

相关 Agent

H

拥抱未来

一个用于共享、训练和部署机器学习模型和数据集的平台。

了解更多 →