Ray 2.59.0 让 shuffle 落到磁盘、本地集群默认开鉴权;DuckDB 把「长字符串换成维表键」写进性能指南;Vortex 0.87 把压缩方案从 VTable 搬进会话;Databricks 给出每秒千事件的零售实时推荐参考架构
本期聚焦 10 月 2 日的四件事:Ray 2.59.0 给 shuffle_v2 补上外部落盘模式、把本地集群的 token 鉴权设为默认、Ray Data 与 Serve 的 LLM API 转 GA;DuckDB 用一篇长文把星型模型的维表做法讲成可测量的执行层优化;Vortex 0.87 把 BitPacked、FoR 等压缩方案的序列化从 VTable 搬进会话级插件注册表,并引入每 1024 元素一个参考值的分块 FoR;Databricks 发布零售实时推荐参考架构,Zerobus 直写 Delta、Lakebase 承担在线查找、AI Search 负责向量召回。
10 月 2 日这一天,数据基建的动静分散在四个相距很远的层上:分布式计算框架、单机分析引擎、列式文件格式、云上参考架构。动作却可以概括成同一句——把一件此前靠使用者自己补的事,收进系统里,并顺手划清它的边界。Ray 2.59.0 给 shuffle 补上外部落盘模式,把本地集群的 token 鉴权从「建议」改成默认,顺带把大集群上的放置组重试风暴、Hudi 反序列化 RCE、dashboard 上的凭据泄露三处一并堵上。DuckDB 发了一篇 16 分钟的长文,主题不是新特性,而是把「长字符串 GROUP BY 慢」这件每个写 SQL 的人都撞过的事,还原成哈希聚合里逐行哈希、逐行比较、逐行拷贝的具体成本,再给一套能自己量出来的改法。Vortex 0.87.0 把 BitPacked 与 FoR 的序列化逻辑从 VTable 搬进 Plugin,压缩方案改为在会话里注册、按允许的序列化 ID 过滤,兼容性从此变成一个可配置的集合而不是编译期常量。Databricks 的零售推荐参考架构则划清了另一条边界:离线特征走湖仓,请求时的会话信号直接进 Model Serving 的 payload,不经过存储。四件事里,有三件都附了一句「别指望立刻见效」的提醒。
Ray 2.59.0:shuffle 有了落盘模式,本地集群默认开始校验 token
事件概述
Ray 2.59.0 于 10 月 2 日发布(PyPI 上传时间 07:01 UTC,GitHub release 07:04 UTC),距 8 月 23 日的 2.58.0 约六周。发布说明把变化归到六条主线:Ray Data 与 Ray Serve 的 LLM API 转 GA、shuffle_v2 新增外部落盘模式、本地集群默认启用 token 鉴权、安全加固、Ray Core 的大集群问题修复、以及一批新数据源。
值得注意的是这个版本没有新版本号层面的架构宣言,绝大多数条目是工程化收尾:改名、加固、修 flaky、补类型标注。真正会改变使用者行为的只有两处——shuffle 的配置名,和本地集群的鉴权默认值。
技术解析
- shuffle_v2 拿到外部落盘后端:新增外部 shuffle 的运行时库、task 与 operator 集合、planner 集成,并支持在其上跑 Join 与 Aggregation。策略名从
hash_shuffle_v2改为shuffle_v2,压缩设置从hash_shuffle_compression改为shuffle_compression。改名的代价是已有配置与脚本需要跟着改。 - 本地集群默认开 token 鉴权:
ray.init()在不带 address 时会启用鉴权,并在没有 token 时于~/.ray/auth_token生成一个;ray start --head在RAY_AUTH_TOKEN、RAY_AUTH_TOKEN_PATH或~/.ray/auth_token已有 token 时启用鉴权,否则启动一个未鉴权集群并给出警告。远程与多节点集群本版行为不变,RAY_AUTH_MODE=disabled可退出。 - 两处安全修复:dashboard 在浏览器可见的端点上对
runtime_env的值做脱敏(runtime_env经常携带凭据);read_hudi增加反序列化守卫,堵掉一条远程代码执行路径。 - Ray Core 的规模问题:移除了 GCS 资源视图重置——它曾在大规模集群上引发放置组重试风暴;worker-lease 协议收紧为每个 pending lease 至多一个
CancelWorkerLease,并引入 lease-id 墓碑;本地对象释放改为批处理。 - Serve 侧的三处可观测性/可用性改进:
BackpressureConfig让超限请求返回429而不是503,并可附Retry-After,把「主动限流」与「部署坏了」在负载均衡与重试策略上区分开;TracingConfig(enabled、exporter_import_path、sampling_ratio)把链路追踪从纯环境变量配置改为可声明;gang-scheduled 部署终于支持min_replicas=0,自动扩缩不再把最后的1 → 0回合上调成一整个 gang。 - 性能与工程化:一元 gRPC 在直连入口改走专用快路径,不再经 async generator,发布说明给的基准是平均 RPS 从 4601 回到 4684;Serve 的核心模块(controller、deployment_state、replica、application_state、proxy、scheduler、router 等)强制跑 mypy 与 pyrefly,顺带修掉若干
ReplicaID与str的键混淆。 - LLM API 转 GA:Ray Data LLM 与 Ray Serve LLM 正式可用,vLLM 升到 0.27.0,
ray.data.llm的重型引擎导入改为延迟加载。 - 新数据源:原生 ORC 读取、
read_lance支持多路径、read_videos()增加 fps 与 resize 参数;Iceberg 侧做了读任务状态去重、解码大小估算改进与读任务内存上限,并修掉Dataset.count()返回错误行数的问题。
行业影响
shuffle 落盘这个动作本身不新鲜,Spark 与 Flink 早有各自的外部 shuffle 服务。Ray 补上这一块的意义在于它把 Ray Data 的适用边界从「内存装得下」往「装不下也能跑完」推了一格,代价是引入了新的运行时组件与新的故障域。鉴权默认开启则是另一类信号:一个长期以「本地跑起来最方便」著称的框架,开始把安全默认值放在便利性前面,且明确区分了本地集群与远程集群——后者改动留到后续版本,说明迁移成本被仔细权衡过。这种「默认值收紧 + 明确退出开关 + 分批推进」的三段式,正在成为基础设施项目处理 breaking change 的标准姿势。
相关链接
DuckDB:把「长字符串 GROUP BY」拆成逐行哈希、比较与拷贝三笔账
事件概述
DuckDB 团队在 10 月 2 日发了一篇 16 分钟的长文《Faster String Aggregations with Dimension Tables》。文章没有宣布新功能,而是把一个老办法——星型模型里的维表——重新推导了一遍,并把它写进了官方性能指南的 schema 章节。
起因是一份关于高基数分组时内存占用偏高的用户报告。那份报告后来查明与字符串无关,但 Richard Wesley 提到自己在字符串密集的聚合上,靠建维表、最后再把字符串 join 回来,拿到了明显收益。DuckDB 于是把这个模式固化进文档。
技术解析
- 成本发生在哪里:DuckDB 的
GROUP BY走哈希聚合,每个组一条哈希表项。整数键的哈希与比较是几条 CPU 指令;字符串则随长度增长。DuckDB 里字符串是 16 字节结构,不超过 12 字节的存内联,更长的存 4 字节前缀加一个指针。超过 12 字节的值,哈希要读遍每个字节,前缀不足以确认相等就必须跟着指针做全串比较,新组还要把字符串拷进哈希表自己的内存。 - 连带影响:更宽的哈希表更不容易装进 CPU cache;在超内存的聚合里,它更早触碰内存上限、更早开始溢写。
- 磁盘上的字典编码救不了这件事:DuckDB 对字符串列在磁盘上做字典压缩,但那是存储层优化;一旦列被读进聚合,每个组键又变回完整字符串。
- 三步改法:先测基数决定键宽(
UTINYINT到 255、USMALLINT到 65,535、UINTEGER约 42 亿);再用row_number() OVER (ORDER BY station_name)建维表并把键转成最窄的整型;最后用LEFT JOIN加EXCLUDE把事实表里的字符串列换成键。 - 键要按字符串顺序分配:这样
ORDER BY station_id与ORDER BY station_name结果一致,排序可以落在便宜的整数上;重建维表时键也保持不变。 - 键窄且密时的额外红利:统计信息显示键域足够小时,优化器会改用 perfect hash aggregate(由
perfect_ht_threshold控制),直接拿键值当下标,连哈希都省掉。 - 查询写法要跟着改:先聚合成小结果再 join 回字符串;top-N 先
LIMIT再 join,只回查十条;按名字过滤改成先在维表里查键。 - 与 ENUM 的取舍:ENUM 是类型系统内的字典编码,宽度由 DuckDB 选,值集合在创建时固定、插入未知值会失败。维表更合适的三种情形是新值持续到来、需要带额外属性(城市、所属线路)、以及数据要离开 DuckDB 导出成 Parquet 或 CSV。
- 多列与运维:每列各建一张维表,各按自己的基数选宽度;两列总是一起出现时可以合成一张组合维表,事实表只存一个键。新数据到来时用
max(station_id) + row_number()续键。 - 官方没有夸大收益:文中明确说,在演示用的 38 万行荷兰列车数据(380,959 行、537 个不同站名,Amsterdam Centraal 这 18 字节的值出现 7,591 次)上两种写法差别很小,行数与字符串长度增长后差距才拉开,并建议用
.timer on、EXPLAIN ANALYZE和调低memory_limit自己量。
行业影响
这篇长文的价值不在技巧本身——星型模型是三十年前的东西——而在于它把一个建模选择翻译成了执行引擎层面的三笔账:哈希多少字节、比较多少次、拷贝多少份。这种翻译方式正在成为分析引擎文档的主流写法:不再只说「推荐这么做」,而是给出可以自己复现的测量路径和适用边界。对从业者来说,更实用的部分是它与 ENUM 的对照表,以及那一句「别在 38 万行上期待奇迹」——它把优化手段的生效条件写清楚了,省掉一轮无效改造。
相关链接
- Faster String Aggregations with Dimension Tables(DuckDB,2026-10-02)
- DuckDB Performance Guide: Schema
- Parallel Grouped Aggregation in DuckDB(2022)
- Lightweight Compression in DuckDB(2022)
- The Lord of the Enums(2021)
Databricks:每秒千事件的零售推荐,实时信号绕开湖仓直送推理
事件概述
Databricks 在 10 月 2 日发布一篇零售实时推荐参考架构长文,基于一个亚洲时尚电商平台的真实落地:超过 100 万月活用户、10 万以上 SKU、每秒约 1,000 条点击事件。整套链路从摄取到服务全部跑在 Databricks 上,由 Unity Catalog 统一治理。
文章真正的看点不是推荐算法,而是它在架构上做的两次切分:摄取路径与推理路径分离,以及预计算路径与实时路径分离。
技术解析
- 摄取:Lakeflow Connect 的 Zerobus Ingest 直接把事件写进 Unity Catalog 的 Delta 表,不需要自管消息 broker。它接受任何标准 Kafka producer 客户端(Java、Python、Go),做法是把 bootstrap server 指向 Zerobus 端点。已经在跑 Kafka 的团队可以改用 Structured Streaming with Declarative Pipelines,下游架构不变。
- 关键取舍:点击流经 Zerobus 进湖仓是为了离线特征计算与模型训练;到了实时推理路径,用户在当前会话里的信号(正在浏览什么)直接作为 API 请求的 payload 发给 Model Serving 端点,完全绕开湖仓存储,避免摄取延迟进入响应时间。
- 三层 medallion:Bronze 存原始 append-only 事件与参考数据;Silver 做会话化、清洗与聚合(7 日浏览数、趋势分、热度指数);Gold 出模型可用的特征表与训练数据集。刷新节奏不同——行为聚合每日跑,全量商品目录每周同步,用户与商品的 embedding 每日重算。
- 训练与服务一致性:Databricks Feature Store 一手管离线特征(训练),一手管在线特征(服务),在线侧落在 Lakebase online tables,同一套特征定义自动可用于推理。
- Path A(预计算):夜间批处理对每个活跃用户跑完三级漏斗——用最新用户 embedding 在 AI Search 的 item index 上做批量 ANN 取候选、LightGBM 打分、再叠业务规则(库存、履约距离、多样性、促销加权),结果写进 Lakebase online tables,键是 user_id + surface。服务时只做一次 KV 查找,没有模型推理、没有向量检索、没有特征拼接。代价是数据新鲜度停留在前一天。
- Path B(会话感知):用于「相似商品」「完整穿搭」、随浏览行为动态重排的搜索结果这类只在请求时才知道上下文的场景,会话信号随请求直接进入打分。
- 延迟口径要留意:页面摘要写的是 sub-50ms,正文把两条路径的小节标题都标成「低于两位数毫秒」,文中没有给出统一的单一基准数字,两者不是同一口径,引用时需要注明来源。
行业影响
这套架构最值得抄的不是组件选型,而是它把「哪些信号必须最新」和「哪些信号可以隔夜」拆成了两条独立链路,并让两条链路共享同一份特征定义。多数实时推荐系统的复杂度都来自想让一条链路一次满足两种新鲜度要求,最后既慢又贵。Databricks 的解法是用 KV 查找承接可预计算的部分,把模型推理留给真正不可预知的部分。另一处趋势信号是 Zerobus:把「Kafka producer 改一行配置就写进 Delta 表」做成标准入口,等于把消息中间件从数据平台的必选项降级为可选项,这与 AWS、Confluent 近年来把摄取层往表格式上靠的方向一致。
相关链接
Vortex 0.87.0:压缩方案从 VTable 搬进会话,FoR 改成每 1024 元素一个参考值
事件概述
列式文件格式 Vortex 在 10 月 2 日发布 0.87.0。这是一个以重构为主的版本:变更列表里「Breaks」分组排在「Features」之前,且长度相当。核心动作是把压缩方案的序列化逻辑从 VTable 搬到 Plugin,并把方案的注册与启用收进一个会话级对象。
技术解析
- 方案注册的归属变了:
BitPacked的 serde 逻辑从 VTable 移到 Plugin,FoR同样处理;压缩方案改为在CompressionSession中注册,BtrBlocksCompressorBuilder从 Session 构造。 - 可用性变成过滤规则:构建时按会话允许的 serialized ID 过滤压缩方案,而不是按 array ID 过滤;Delta、Zstd、Pco 三个方案按 builder mode 决定是否注册。读文件时能不能解开某个编码,取决于当前会话允许哪些序列化 ID。
- 分块 FoR:经典 Frame-of-Reference 用整个数组的最小值作参考,一个离群值就能把位宽撑大。这一版引入 Blocked FoR,每 1024 个元素各用一个参考值,配套新增编解码内核与迭代执行路径;per-chunk 参考值仅在
fastlanes.for.v2被允许时启用。 - CUDA 文件扫描:支持列投影与字典解码,本地文件读取经可缓存的 pinned buffer 流水化。
- 其他:新增 C/C++ API 的共享库构建目标;
vortex.is_nan表达式带统计剪枝;list_contains补上 SQL 的 NULL 语义;BoolArray::trim_bits接入Canonical::compact,回收底层 buffer 里未使用的字节。 - 兼容性被显式门控:compat fixture 的写入按 edition 门控,说明格式层面已经引入 edition 概念来隔离兼容矩阵。
行业影响
对列式格式来说,「能读哪些历史文件」从来不是一个可以事后修补的属性。Vortex 把压缩方案从编译期 VTable 挪到会话级注册表,等于把兼容性从「这个二进制支持什么」改成「这次打开文件时允许什么」——灵活性上去了,代价是同一份文件在不同配置下可读性不同,出问题时排查维度也多了一层。edition 门控的出现说明团队清楚这一点。分块 FoR 则是另一类典型动作:牺牲一点元数据与分支开销,换掉离群值放大位宽这个老大难。这类编码层改动不会出现在任何营销材料里,但决定了扫描型负载在真实数据分布上的表现。
相关链接
编者评
四件事看似无关,读下来能拼出一条清晰的主线:数据基建正在把一批「靠使用者经验和约定兜住的事」逐个收进系统边界,收一处就写一处边界。Ray 收 shuffle 的落盘能力和本地集群的鉴权默认值;DuckDB 收一个此前靠老 DBA 口口相传的建模技巧,并给出适用条件;Vortex 收压缩方案的注册与启用规则,把兼容性从编译期常量变成会话级配置;Databricks 收实时推荐的架构决策,把离线与在线、可预计算与不可预计算切成两条链路。四条路都遵循同一个节奏——先给出机制,再给出退出开关或边界条件,最后交代代价:Ray 改名会打断现有配置,DuckDB 明说 38 万行上差别很小,Vortex 让同一份文件的可读性依赖会话配置,Databricks 的 Path A 数据停留在前一天。这比单纯宣布「我们做成了 X」更有信息量,也更接近基础设施该有的诚实度。本期中文来源(阿里云、火山引擎、腾讯云)连续第三次无可用更新,阿里云开发者社区大数据板块最新内容仍停留在 9 月 25 日的云栖,火山引擎开发者社区首页已整体转向 Agent 开发方向,需要在后续寻找新的中文入口。