假设你被任命为一场千人答谢宴的总负责人。你的任务不是亲自掌勺——那根本做不完——而是把整套宴席拆解成一道道菜,规划好工序:哪些先备料、哪些可以同时开火、哪位帮厨手上还有空,最后在开席那一刻让所有菜肴几乎同步上桌。在这整个过程中,最有力的武器不是刀工,而是一张“工序图”,它清清楚楚记载着每道菜依赖什么、谁先谁后。如果把数据世界也想象成一场宴席——Pandas 表格就是巨大的食材库,分析流程就是一道道菜——那么 Dask 就是那个统筹全局的“后厨调度系统”。 Dask 诞生于一个尴尬局面:Python 数据分析师用 Pandas 用得顺手,但数据一旦超过单机内存,Pandas 就“翻车”了。换 Spark?要学新语法、要部署 JVM 集群,让人望而却步。Dask 的答案是:保持你熟悉的 DataFrame 和 Array 接口,背后把操作自动改造成“任务图”,交给一群 Worker 并行执行。你可以理解为——客人吃到的还是同样的菜,菜单也没变,只是厨房从一个灶台升级成了几十个灶台协同作战。 这篇文章将带你从“总负责人”的视角,认识 Dask 的并行核心思想、任务图调度、DataFrame 与 Array 的分块并行、Worker 间的数据交换、内存溢出处理,以及它与 Spark 的定位差异。读完你会明白:Dask 不是要推翻你的单机套路,而是让你的 Python 数据管线上限从“一条铝锅”升级成“一排不锈钢灶台”。
掌握 Dask 分布式并行,是 AI 架构师面对 TB 级数据分析的关键能力:它让你在不离开 Python 生态的前提下,把单机瓶颈升级为集群算力,也是面试和实战中“数据量上来了怎么办”的标准答案之一。
目录
可视化演示
动态展示Dask任务图调度、集合类型、Worker分布shuffle与内存溢出处理机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Dask 核心思想:千人大宴的“总负责人”
作为千人宴总负责人,你很快会发现两条铁律:第一,食材(数据)多到一间厨房(内存)根本放不下;第二,就算放得下,一个灶台也烧不出那么多菜。你不能因此放弃熟悉的菜谱——宾客吃的还是那几道菜。Dask 的思想正是如此:不改变你写 Pandas / NumPy 的操作习惯,只改变背后“谁来烧、用什么灶、怎么并行”。它把你的 DataFrame 自动切成若干份,分发给集群中的 Worker 并行计算,调度器(Scheduler)负责协调节奏。对你来说,代码几乎没变;对系统来说,已经从一口锅变成了一排灶。
Dask 的并行模型由三部分组成:客户端(你写的代码)、调度器(后厨总指挥)、Worker(帮厨)。每当调用 df.groupby('city').sum() 这类操作,Dask 并不会立刻计算,而是构建一张“任务图”,等待 compute() 真正触发执行。这种“先规划、后执行”的策略,让拆解和调度变得极其灵活。它意味着你可以在小数据集上调试逻辑、预览结果形态,再放心地把完整任务丢给集群。
但团队不是越大越好——帮厨之间的沟通也需要时间。Amdahl 定律给出了加速比的天花板:如果你流程中有 10% 必须串行(比如最后还是主厨一个人决定火候),哪怕请 100 位帮厨,最快也只能提速约 9.2 倍。Dask 能做的,是把可并行部分尽量放大、串行部分尽量压缩——它不创造奇迹,但让并行发挥到极致。
回到宴席:Dask 这位总负责人最重要的本事,是把“可拆解的部分”在菜单设计阶段就尽量放大。你写 pandas 代码时顺手把计算拆成几个独立模块,Dask 就能帮你在集群上把它们同时推进,而不会让帮厨们挤在一口锅前等你分派。
- 可并行比例 P程序中可以被拆开并行执行的时间占比,比如洗菜切菜可以多人同时做。
- 串行比例 1-P必须一个人按顺序完成的步骤,比如最后调味必须由主厨一锤定音。
- 节点数 N参与并行计算的 Worker 数量,类比帮厨人数。
- 加速比 S(N)使用 N 个 Worker 后相对单机的提速倍数;P 越接近 1,加速比越接近 N。
任务图调度:主厨手里的“工序图谱”
主厨在开工前不会盲目指挥,而是先在白板上画出整场宴席的工序:番茄炖牛腩要先焯水,焯水后才能炖;炖着的 40 分钟里,助手可以同时准备青菜。Dask 的任务图(Task Graph)就是这张白板——一张有向无环图(DAG),节点是计算任务,边是依赖关系。你写的每一步 Pandas 链式操作,都会在图上多挂几个节点。
任务图最大的价值是“懒执行”:调用 .groupby()、.merge() 时图只是被搭起来,没有真正消耗算力。只有当你调用 .compute(),调度器才会像主厨喊一声“开火”,把图中所有可并行的任务按依赖顺序派发给空闲 Worker。这个机制让 Dask 能全局优化——比如提前合并重复计算、选择最小 shuffle 路径。更妙的是,同一个 Dask DataFrame 可以被多次 compute(),每一次都复用同一张任务图,就像同一份菜谱可以反复照着做,不必每次重新发明。
在 DAG 上,最重要的概念是“关键路径”——图中最长的那条依赖链,它决定了总耗时。如果某个任务的执行时间变了,只有当它落在关键路径上,整体进度才会被推动。
这条规律在生产中极具指导意义:如果发现整体任务很慢,不要平均优化所有环节,先找出关键路径上最慢的任务。就像宴席上菜慢,你不需要同时催促所有帮厨——只要盯住那道最后上的硬菜。
- 路径 p图中从起始任务到结束任务的一条完整依赖链,类比一道菜从备料到出锅的全流程。
- 任务耗时 t_t单个节点(如“读取分区1”)的执行时间。
- 路径求和链上所有任务耗时相加,就是该路径的总时长。
- 取最大值多条路径并行推进,最慢的一条决定整体上菜时间。
DataFrame 分块并行:把筹备清单切成几页分给帮厨
千人宴的食材清单长得惊人:一千人的前菜、主菜、甜点,每样都要登记采购。总负责人不会把清单完整地交给一位帮厨去备料,而是按宴席流程切成几页:张三备前菜区、李四备主菜区……每一页对应一个分区。Dask DataFrame 所做的正是这件事:把一个大型 DataFrame 按行切分成多个小分区(Partition),每个分区本质上就是一个 Pandas DataFrame,分配给不同的 Worker 并行处理。
常见的操作分两类:按行独立的“逐分区操作”(如 fillna、map)几乎可以完全并行,速度快得惊人;而 groupby、merge 这类需要跨分区汇总的操作,则要经历“局部聚合 → 数据交换 → 全局聚合”三个阶段。理解这三个阶段,你就知道为什么分布式聚合比单机慢、慢在哪里。
这个公式给你一个调优直觉:当 DataFrame 分区数(npartitions)太少,每区太大,局部并行度不足;分区数太多,又会导致预聚合重复和 shuffle 压力增大。经验法则是让每个分区的数据量控制在 100MB 左右,既让帮厨各干各的,又不至于区区间搬运食材浪费时间。
- 局部聚合 T_partial每个 Worker 先对本地分区做一次预聚合,类比各帮厨先清点自己手头的食材。
- 数据交换 T_shuffle把相同 key 的数据跨 Worker 搬移到同一处,这是最耗时的一环。
- 全局聚合 T_final目标 Worker 收到所有局部结果后汇总出最终答案。
Array 并行数组:把大蛋糕切块分烤
如果说 DataFrame 是“按行切”,那么 Dask Array 就是“按维度切块”——像把一整块婚礼大蛋糕先横切再竖切,切成许多小立方体,每个小立方体由不同的烤炉(Worker)负责。这样无论你做矩阵加法、乘法、还是逐元素函数,每块都能独立计算,最后自动拼回完整结果。你写代码时看到的仍然是一个逻辑上的大数组,Dask 在背后替你管好了所有块的边界与组装。
Dask Array 支持绝大部分 NumPy 操作:sum、mean、std、fft、dot 等,接口几乎逐字对齐。只需要在创建时用 chunks 参数告诉 Dask 每个块多大。分块数量直接影响并行度和内存占用,计算方式非常直白:
选块大小是一种权衡:块越小,调度粒度越细、越适合负载均衡,但块的数量膨胀会让任务图元数据开销变大;块越大,计算效率高但并行度下降。经验上单块控制在几十 MB 到几百 MB 之间,就像蛋糕不要切得太碎,也不要一大块塞进烤炉。
- 总元素数 n_elements整个数组包含多少元素,类比蛋糕总体积。
- 每块元素数 n_chunk_sizechunks 参数指定的每个分块的大小。
- 向上取整块的数量必须是整数,哪怕最后一块装不满也要有地方放。
数据 Shuffle:帮厨之间的食材交换
宴席烹饪中最头疼的时刻:炖番茄汤需要把散落在各帮厨手头的番茄全部集中到一个灶台。这就是分布式计算里的 Shuffle——根据某个 key 把数据重新洗牌分配。Dask 中的 groupby、join、distinct 都会触发 shuffle。Shuffle 是网络密集型操作,也是分布式任务中最容易出问题的环节——它占用的是最宝贵的网络带宽,还会因为数据分布不均造成“旱的旱死、涝的涝死”。
Shuffle 的开销与数据量强相关。如果每个分区大小为 $S_i$,n 个分区互相交换数据,总交换量有个很直观的期望值:
这个公式告诉你:只要做 shuffle,交换量就约等于整份数据的规模,分再多的区也不会减少网络传输总量。真正该做的是少做 shuffle——尽量把 join、groupby 拆成先局部再合并,或用 repartition 预先把相同 key 的数据放在同一分区。好比聪明的总负责人会在备菜时就按菜谱类别分筐装好,而不是烹饪中途才到处翻找食材。
- 分区数据量 S_i第 i 个分区的大小。
- 交换比例 (n-1)/n除了特意保留在自己手中的那份,其余都要发给其他区。
- 连加求和把所有分区的交换量累加得到总 shuffle 量。
内存与溢出处理:厨房冰箱与储物室
千人宴厨房里,冰箱(内存)是稀缺资源。备好的半成品一开始堆在操作台上,台面放不下就放进冰箱;冰箱也被占满时,只能把不急着用的备料转移到储物室(磁盘),用的时候再取回来。Dask 的每个 Worker 都有类似的内存管理机制:设定了 memory_limit,当内存占用逼近阈值,Worker 会把“最不常用”的中间数据 spill 到磁盘,避免进程被系统 OOM Kill。但这只是兜底方案——spill 之后的读写都走磁盘 I/O,速度会断崖式下跌。
在工程中,还有一些主动管理手段。persist() 可以把你反复使用的中间结果固定缓存到内存中,就像把热门前菜的半成品提前放进保鲜柜;任务完成后 Worker 会自动释放内存。粗估一个 DataFrame 需要的内存,可以用这个公式:
这个估算帮助你提前决策:1000 万行、20 列的 float64 表约需 1000万 × 20 × 8 × 2 = 3.2GB。如果单 Worker 内存只有 4GB,你就要考虑增加分区数或换更大的机器。记住:最好的内存管理不是溢出后救火,而是在开火前就算清家底。
- 行数 N_rows表有多少行记录,类比菜品数量。
- 特征数 F_features表有多少列。
- 每元素字节数float64 占 8 字节,int64 占 8 字节,object 类型更贵。
- 开销系数 overheadPandas 的索引、对象头等额外内存,通常取 1.5 到 2.5。
Spark 对比定位:西式大厨房与灵活小灶台
很多人会问:已经有 Spark 了,为什么还要 Dask?打个比方,Spark 是一座西式中央宴会厨房:所有食材进入统一中央厨房,由 JVM 体系的主厨团队(Spark SQL 优化器)统一拆解配比,适合大规模、批量、结构化数据流水线;而 Dask 更像你手边灵活的中式小灶台群——亲切、原生 Python、随用随起,和 NumPy/Pandas 生态无缝衔接。二者并没有绝对的优劣,只有适合的场景。
从架构层面看,Spark 以 DataFrame/SQL 为核心,任务粒度较粗,依赖 Catalyst 优化器对执行计划做全局改造;Dask 以 Python 原生的任务图为粒度,可以灵活表达任意计算图(包括 scikit-learn 那样的自定义逻辑)。从内存管理看,Spark 以 JVM 堆内/堆外统一对象池管理,Dask 直接使用 Python 对象,内存估算更直观、调试更亲民。一个实用的对比指标是 shuffle 占比——它反映了任务中有多大比例的时间耗在网络传输上。
选型建议很实在:如果团队以 Python 数据科学为主、数据规模在几十 TB 以内、希望零迁移成本,Dask 通常更顺手;如果数据达到数百 TB、需要严格的企业级调度和 SQL 治理,Spark 无可替代。对 AI 架构师来说,掌握 Dask 不是要取代 Spark,而是多一把趁手的工具,在合适的场景用小灶台快速翻锅。
- shuffle 字节数任务中跨节点网络传输的数据总量。
- 处理总字节数整个任务读入和处理的数据总量。
- 比值区间shuffle 占比在 0 到 1 之间,越接近 1 说明任务越“网络密集”。
生产实践复盘:宴席如何不翻车
再好的菜谱,也需要真正开席才能检验。Dask 生产环境的核心保障是 Dashboard——一个实时监控面板,你能像主厨盯着各灶台一样看到每个任务的状态、每台 Worker 的内存水位。当某个任务显示红色卡住,或某个 Worker 的曲线逼近内存上限,你就能提前干预,而不是等 OOM 报错才手忙脚乱。这个面板还能看到任务图的实际执行路径,帮你发现优化空间。
常见故障几乎都有套路可循:数据倾斜(某个分区特别大,导致单个 Worker 忙死、其他空闲)、shuffle 量过大拖垮网络、persist 误用导致内存累积、分区数设置不匹配集群规模。调优时最重要的经验公式是每个 Worker 的并行分区数——它把“分区总数量”翻译成“每个灶台站几个人干活”:
比如 8 个 Worker、每台 4 核,P 取 8 × 3 × 4 = 96 个分区往往是不错的起点。开席前的演练同样重要:先在部分数据上小规模试跑、观察 Dashboard 水位、再逐步放大,是绝大多数 Dask 生产事故的标准免疫手段。
- 总分区数 P任务的 DataFrame 分区总量。
- Worker 数 W集群中可用 worker 的数量。
- 并发系数 c每个 worker 同时运行几个任务,通常取 2 到 4(取决于 CPU 核心数)。
AI 应用场景
场景1:电商日志的离线用户行为分析
阶段:数据预处理
解决的问题:数据分析师拿到数亿条用户点击日志,Pandas 直接读入会触发 MemoryError。传统做法是抽样或分批处理,既损失信息又费时。Dask 将日志按天分块,用熟悉的 groupby 聚合出用户路径特征,代码迁移几乎为零。
具体案例:某电商平台每天产生约 8 亿条行为日志(约 300GB)。数据团队用 Dask DataFrame 在 6 台 16GB 内存的机器上,完成此前需要 Spark 集群才能跑完的日活统计和漏斗分析,整个任务从“申请大数据集群”到“跑完出结果”的时间缩短了 3 倍。
场景2:金融风控中的批量特征回测
阶段:特征工程
解决的问题:训练风控模型前需要历史 12 个月的窗口特征,传统单机要跑 20 小时以上。Dask Array 把特征矩阵切成 chunk 并行计算滚动窗口,同时在多台 Worker 上执行,且不必改动原有 NumPy 代码逻辑。
具体案例:某金融科技公司使用 Dask Array 对 5000 万样本 × 300 维特征做 Rolling 窗口计算,用 8 台 worker 将耗时从 20 小时压缩到 2.5 小时。模型团队还惊喜地发现,同一份 Dask Array 可以同时支撑多个窗口的并发计算,特征迭代效率大幅提升。
场景3:地图服务的切片渲染管线
阶段:批量计算任务
解决的问题:地图瓦片生成需要对全城路网数据做大量 GeoPandas 操作,单机串行需要一整天。Dask 的 Delayed 接口把每片渲染任务声明为独立计算节点,自动并行调度,无需引入重型分布式框架。
具体案例:一家 LBS 服务商把 200 万条路网数据的缓冲区计算拆成 1200 个延迟任务,在 16 核单机上也做到了 6 倍加速。后来扩展到 4 台机器组成的集群,任务图自动把 1200 个节点分派给全部 Worker,代码一行未改。
Python 演示
import dask.dataframe as ddimport pandas as pdimport time # 构造 100 万行样例数据pdf = pd.DataFrame({ 'city': ['beijing', 'shanghai', 'guangzhou', 'shenzhen'] * 250000, 'amount': list(range(1000000)), 'flag': [i % 2 for i in range(1000000)],}) # 转为 Dask DataFrame,划分 8 个分区ddf = dd.from_pandas(pdf, npartitions=8) # 构建任务图:加一列 + 分组聚合,此刻只是"纸上谈兵"ddf['amount_sq'] = ddf['amount'] * ddf['amount']agg = ddf.groupby('city')['amount'].sum() # 任务图已构建但未计算,打印节点数print('任务图节点数:', len(agg.dask)) # 触发真正的并行计算t0 = time.time()result = agg.compute()print('计算结果:')print(result)print('Dask 耗时: %.2f 秒' % (time.time() - t0)) # 与单机 Pandas 对比t0 = time.time()pdf['amount_sq'] = pdf['amount'] * pdf['amount']pd_result = pdf.groupby('city')['amount'].sum()print('Pandas 耗时: %.2f 秒' % (time.time() - t0))
ddf = dd.from_pandas(pdf, npartitions=8)把单机 DataFrame 切成 8 个分区,相当于把食材分给 8 位帮厨备料。ddf['amount_sq'] = ddf['amount'] * ddf['amount']在任务图上登记新列的计算步骤,但此刻并没有真正执行。agg = ddf.groupby('city')['amount'].sum()构建分组聚合任务图;打印len(agg.dask)可以看到图里挂了 16 个任务节点。result = agg.compute()这是“开火”信号,调度器才真正把任务图派发给 Worker 并行执行。pd_result = pdf.groupby('city')['amount'].sum()对比组的 Pandas 纯单机聚合,用于感受调度开销与并行收益的关系。
常见误区
误区“Pandas 能写的代码,Dask 都能跑,而且结果应该一模一样。” 很多初学者把 Dask DataFrame 当成一个无缝的超集,一旦某段代码不能运行就困惑不已。
正解Dask 实现了 Pandas 的常用子集(尤其适合聚合、分组、join),但不是完整的 Pandas 复刻。例如涉及行序依赖的 iterrows、pct_change 等操作无法直接并行化;索引语义也有差异(Dask 的索引不保证顺序)。更准确的比喻是:Dask 提供的是同一本菜谱上的大多数菜,但需要精确控制火候的少数菜还是只能回小灶台(单机 Pandas)。
为何常见:因为 Dask 刻意把 API 模仿得极像 Pandas,宣传语也强调“几乎无学习成本”,让人忽略了接口子集和语义差异。
误区“懒执行肯定是好处,调一下 compute() 就完事了,Dask 会自动找到最优调度。” 不少初学者只调用 compute() 却完全不考虑分区数、数据分布。
正解compute() 只是触发点,性能好坏取决于任务图和分区设计。如果分区数太少或太多、shuffle 设计不合理、数据倾斜严重,Dask 照样慢甚至崩溃。分区数要匹配 worker 数 × 并发度,而不是拍脑袋决定。就像喊了“开火”,但如果帮厨分工不均,照样有人闲着有人累死。
为何常见:因为懒执行 + 自动调度的宣传让用户误以为调度器是全知全能的,忽略了分布式系统固有的资源规划责任。
误区“groupby 就是做个聚合,为啥这么慢?所有 worker 不是已经在并行了吗?” 初学者经常忽略跨节点网络传输的巨大开销。
正解Shuffle 的成本通常和整个数据集规模相当,甚至更贵(涉及序列化、网络传输、磁盘落盘)。它消耗的是最珍贵的集群资源——网络带宽。优化 shuffle 让相同 key 的数据尽量在同一分区,并用 repartition 提前对齐,能显著提速。类比厨房里“临时翻找食材”永远比“预先分筐装好”更浪费时间。
为何常见:因为单机开发时内存中的数据移动几乎无感,初学者很难建立起“网络传输很贵”的直觉。
误区“Dask 有 spill 机制,数据放不下会自动写磁盘,所以不用担心内存。” 有人以为 Dask 的内存管理是自动化的万能保险,结果 spill 到磁盘后速度断崖式下降。
正解spill 是兜底方案而非优化策略。一旦触发大规模 spill,磁盘 I/O 会成为新瓶颈,速度比单机 Pandas 还慢。正确做法是先估算内存需求、设定合理的分区数、用 persist 复用中间结果,把需要频繁访问的数据留在内存。spill 就像储物室——偶尔放几件应急可以,如果每次做饭都要从储物室搬东西,出菜速度必然惨不忍睹。
为何常见:因为官方文档把 spill 描述为“内存不足时的自动缓解”,用户就把自动缓解误认为自动优化。
面试题
Q1. Dask 的核心设计目标是?
- A.连接所有已有的 Python 数据分析库
- B.让单机库扩展到多核与集群← 正确答案
- C.重写整套数据分析工具链
- D.取代 SQL 成为大数据标准
答案:B。Dask 的核心是让 Pandas、NumPy 等单机库的接口在分布式环境中复用,B 准确描述了它的设计目标。A 只是手段的一部分,C 违背了“适配而非重写”的初衷,D 更不是 Dask 的定位——SQL 不是它要取代的对象。
Q2. Dask DataFrame 的物理存储单位是?
- A.分区 Partition← 正确答案
- B.分片 Shard
- C.分块 Chunk
- D.切片 Slice
答案:A。Dask DataFrame 按行切分为若干个 Partition,每个 Partition 是一个独立的 Pandas DataFrame。Chunk 是 Dask Array 的术语,Shard 多见于搜索引擎或 NoSQL,Slice 是通用切片概念,都不是 Dask DataFrame 的标准物理单位。
Q3. Dask 中触发实际计算的方法是?
- A.execute() 执行
- B.compute() 计算← 正确答案
- C.run() 启动
- D.start() 运行
答案:B。Dask 采用懒执行,构建任务图时不消耗算力,调用 compute() 才触发真正的分布式计算。execute 是数据库术语,run 和 start 不是 Dask 的标准触发接口。
Q4. 关于 Dask 任务图 DAG 的说法,正确的是?
- A.边表示任务间的依赖流向← 正确答案
- B.节点的数量越多越好
- C.构建时立即执行计算
- D.依赖关系可以形成回环
答案:A。DAG 的边代表依赖方向——下游任务必须等待上游完成。节点过多会带来调度开销而非无条件好处,构建时是懒执行、不会真算,DAG 中的 D 代表 Directed(有向),形成环就违背了图的基本定义。
Q5. DataFrame 分区数过多会导致什么后果?
- A.任务图元数据开销增大← 正确答案
- B.单个分区数据量变大
- C.网络传输通道被禁用
- D.内存占用直接降为零
答案:A。分区越多,任务图中的节点越多,调度器需要维护的元数据和通信开销越大。B 描述的是分区数过少的特征;C、D 都是不切实际的极端说法。分区数应匹配 Worker 数量和并发度。
Q6. Dask Worker 内存接近上限时,默认处理方式是?
- A.立即终止整条任务链
- B.将中间数据溢写磁盘← 正确答案
- C.自动扩大内存继续执行
- D.将数据复制给其他节点
答案:B。Dask 的 Worker 会监控本地内存占用,逼近阈值时把不常用的中间数据 spill 到磁盘以保住进程。A 是 OOM 之后的被动结果而非默认策略;C 不可能物理扩容;D 会加剧其他节点的内存压力。
Q7. 任务关键路径的耗时发生变化,总耗时会?
- A.总耗时完全不受关键路径影响
- B.跟随关键路径总耗时同向变化← 正确答案
- C.只与任务总数成正比关系
- D.只取决于机器内存大小
答案:B。总耗时的下界由关键路径决定——最慢的依赖链就是木桶的短板。关键路径变长,整体变慢;变短,整体变快。C 忽略依赖结构,D 混淆资源与时间的关系,A 与定义相悖。
Q8. Dask Array 适合哪些应用场景?(多选)
- A.大规模矩阵乘法与 FFT 计算← 正确答案
- B.行间严格排序与逐行偏移
- C.图像数据集分块预处理← 正确答案
- D.需要严格行序的逐行迭代
答案:AC。Dask Array 擅长元素级、矩阵级和块级并行计算,因此矩阵乘法、FFT、图像分块整形都是它的主战场。B 和 D 都依赖全局行序,违背数组的分块独立性,更适合单机逐行处理。
Q9. 关于 Dask 与 Spark 的差异,下列说法正确的是?(多选)
- A.Spark 基于 JVM,Dask 原生运行 Python← 正确答案
- B.Dask 任务粒度更细且控制更灵活← 正确答案
- C.Spark 完全不具备结构化数据接口
- D.使用 Dask 必须迁移到 Scala 语言
答案:AB。Spark 运行在 JVM 上,核心接口是 Scala/SQL;Dask 是纯 Python 实现,任务图粒度更细、表达任意计算图更灵活。C 错误——Spark SQL 正是结构化数据的核心;D 相反,Dask 的价值恰恰是不离开 Python。
Q10. 某 Dask 任务耗时过长,Dashboard 显示单个 Worker 内存 90% 且任务卡住,可能的根因有?(多选)
- A.数据倾斜使个别分区异常过大← 正确答案
- B.shuffle 量过大造成严重网络拥塞← 正确答案
- C.persist 缓存了过多中间结果← 正确答案
- D.分区数设定为越多调度越高效
答案:ABC。单个 Worker 内存 90% 的典型原因是数据倾斜——某分区远大于其他分区,该 Worker 成为瓶颈;shuffle 过多会让网络拥塞并堆积中间数据;persist 滥用会把本应释放的中间结果钉在内存里。D 是错误命题,分区过多会放大元数据开销。