一次热更让玩家多下了几百个根本没变的 Bundle,我是怎么把它修好的

封面图

先说人话:我们遇到了什么鬼

事情是这样的。我们游戏用的是 Unity + Addressables,热更就是往 CDN 上丢新的 bundle 文件,玩家打开游戏时比对一下,只下载变了的那些。

按理说,你这次更新就改了一张图,那玩家应该只多下那一个 bundle 才对。结果我们打出来的热更包里,躺着几百个 bundle,仔细一看,内容一个字节都没变,就是文件名变了。

文件名一变,客户端就认为它是"新文件",于是玩家吭哧吭哧把这几百个 bundle 重新下载了一遍。那次 iOS 热更,光是"内容没变、纯改名"的 bundle 就占了 272 MB,整个 patch 的一大半体积是白下的。

你说气不气人。


问题到底出在哪

研究了一圈,根子在一个很隐蔽的地方:bundle 的文件名,是根据哈希算出来的。

大概长这样:

文件名 = Hash( 我自己的内容 + 我依赖的那些 bundle 的名字列表 )

注意最后那半句——"我依赖的那些 bundle 的名字列表",它还不是直接依赖,是一路递归展开出来的"传递闭包"。啥意思呢?就是"A 依赖 B,B 依赖 C",那 A 的哈希里不仅记了 B,还把 C 也记进去了。

问题就来了:

只要你新增了一个 bundle,而这个 bundle 又被某个"被大家广泛依赖"的大 hub(比如 OtherData、通用 UI、要塞这种)依赖了,那这个新名字就会一路钻进所有依赖这个 hub 的 bundle 的哈希里。结果就是——几百个 bundle 的哈希全变了,文件名全改了,但内容一个字节都没动。

说白了:你只是往池子里扔了一颗小石子,结果所有连着池子的水管都得换标签。

我们在本地做了个最小复现,就新增一个小 bundle,结果 397 个 bundle 字节没变、文件名全变了。比生产那次(296 个)还狠。


怎么修

思路很简单:哈希里别算传递闭包了,只算直接依赖。

一个 bundle 的文件名,应该只由「它自己的内容」+「它直接依赖的那些 bundle」决定。C 变了那是 C 自己的事,不该连累 A。

具体做法是内嵌(fork)一下 Unity 的 Scriptable Build Pipeline 包,改一个文件:

  1. 把 com.unity.scriptablebuildpipeline 从包缓存里拷到 Packages/ 目录下,变成可以改源码的内嵌包。
  2. 在 ArchiveAndCompressBundles.cs 里,新增一个「只算直接依赖、不递归」的方法。
  3. 让算文件名的哈希用这个新方法,而 manifest 里的依赖信息还是保持原来的传递闭包不变(这样不影响运行时加载)。

改动拢共二十来行,核心就是上面那个思路。细节都写在《Bundle热更重命名级联机制修复.md》里了,这里不展开。


光改完不算数,得验过才敢上

这种动构建链路的事,改完直接发生产那是作死。我们老老实实跑了三步测试,每一步都过了才敢说"可用"。

第一步:先在本机把问题复现出来

改代码之前,先证明"病真的存在"。

  1. 打一次 bundle 包,存好产物。
  2. 随便改一个小资源(比如新建一张图,塞进 PackSeparately 组,再让 OtherData 这种高扇入的 hub 引用它)。
  3. 再打一次包。
  4. 对比两次产物:数一下有多少个 bundle「内容一样、文件名变了」。

结果:新增 1 个 bundle,397 个 bundle 字节没变但改名。问题复现成功,基线拿到了。

第二步:加入修复代码,再做同样的事

  1. 内嵌 SBP,改哈希口径。
  2. 重复第一步的动作:打包 → 改文件 → 再打包。
  3. 对比两次产物。

结果:还是新增 1 个 bundle,但「字节没变、文件名变」的数量——0。

只剩两个变化:那个真正被改了内容的 bundle(它内容变了,改名是应该的),和那个新加的 bundle。其余几百个 bundle 纹丝不动。

到这里,本机层面就说明修复有效了。

第三步:上生产环境,在真实节点之间验证

本机复现通过还不够,得用之前真实出过问题的 apk 和热更节点来验证,这样才有说服力。

  1. 从当时的 apk 节点切个分支出来,把修复代码合进去,打一个 apk(作为基线)。
  2. 再合并最新的生产节点,打一个热更包(里面就是那次会触发大规模级联的更新内容)。
  3. 对比这份 apk 和这份热更的 manifest.json。

结果:

项 数量
完全没动的 bundle 537
内容没变但改名的 bundle 0
内容真变了的 bundle 1
纯新增的 bundle 1

换句话说,那次之前会炸出几百个"假更新"的热更,现在打出来,真正有变化的就俩 bundle,一个是内容真改了,一个是真新增的。剩下的 537 个一个字节没动、文件名也没动。

三步全过,这才能拍胸脯说修好了。


最后提醒一句

改完哈希口径后,第一次打包,所有 bundle 的文件名都会变一次(因为算法变了)。这是预期内的一次性代价,别慌,也别把它单独发成一次热更——要跟一个大的版本更新一起发,让玩家跟着版本整体更新一次就完了。

之后再做热更,就再也不会出现"改一张图、重下几百个 bundle"的鬼故事了。

posted @ 2026-09-29 18:11  鑫鑫哥Adam  阅读(11)  评论(0)    收藏  举报