dnf update与upgrade的区别:从YUM到DNF的软件包更新全解析 写这篇文章的念头源于我几年前在某台生产服务器上的一次操作失误。当时我执行了dnf update以为只是打个补丁修修漏洞结果它顺手把内核和一堆核心库全给升级了导致一个服务因为动态链接库版本不匹配直接跑不起来。事后复盘我发现很多人包括当时的我根本没搞清楚dnf update和dnf upgrade到底有什么区别以为它们只是同一个功能的两种写法。今天就把这个老生常谈但又容易被忽视的问题彻底说清楚。1. 先搞清楚基本盘这两个命令到底是不是一回事很多用 Fedora、RHEL、CentOS 等发行版的朋友应该都看到过网上各种教程里混用这两个命令。有人说“update 是升级软件包upgrade 是升级系统”也有人说“update 只是刷新索引upgrade 才是真正干活”这些说法其实都不准确。在 DNF 的官方文档和实际行为里dnf update和dnf upgrade在绝大多数情况下做的事情完全一样——都是把系统里已安装的软件包更新到软件源里可用的最新版本。那为什么还要区分这两个命令这要从 DNF 的前身 YUM 说起。YUM 当年有一个历史遗留的设计yum update会更新所有软件包同时也承担了更新软件源索引的任务而yum upgrade在早期版本里还会额外处理一些过时的、被废弃的软件包替换逻辑。到了 DNF 时代开发者重新梳理了命令体系把upgrade定位成“更新并妥善处理替代关系”的正式指令而update则被保留为指向upgrade的别名alias。1.1 从 YUM 到 DNF一条容易踩坑的历史沿革我在早期用 CentOS 6 的时候系统管理员之间流传着一个不成文的规矩生产环境不要随便跑yum update要么锁定内核版本要么用好yum --excludekernel* update。原因就是 YUM 时代对依赖关系的处理不够细腻一个不小心的全量更新可能把生产环境搞挂。DNF 从 YUM 手里接棒之后重写了依赖解析器引入了更完善的libsolv库对事务处理、受保护软件包、弱依赖weak dependencies的处理都做了改进。但有意思的是DNF 在命名上给update留了“历史地位”。在 DNF 4 及更早版本里dnf update和dnf upgrade指向同一个内部函数命令行帮助里明确写了“upgrade is an alias of update”。到了 DNF 5在 Fedora 41 前后开始全面铺开官方文档和--help输出里已经把upgrade作为首选术语update仍然保留但定位是兼容旧脚本。所以第一个可以落地的结论是如果你在 DNF 4 或 DNF 5 环境里日常更新系统dnf update和dnf upgrade在行为上没有任何区别更不存在“update 是部分更新、upgrade 是全部更新”这种说法。这个误解在国内外论坛上流传极广希望看到这篇文章的朋友不要再被误导。1.2 命令别名与使用习惯为什么两个名字都能见到既然行为一致为什么文档里会出现两个命令除了历史沿革还有一个原因是不同发行版对文档的编写习惯不同。Fedora 的官方文档、问答社区如 Ask Fedora倾向于推荐dnf upgrade因为这个词更直白地表达“把旧版本替换成新版本”的语义而很多从 RHEL 8 时代走过来的运维教程则习惯沿用dnf update的写法。我觉得比较稳妥的做法是新脚本、新文档、个人日常操作统一使用dnf upgrade因为它语义清晰、没有歧义且在未来的 DNF 5 生态里是官方主推。但如果你接手了一台旧机器发现 crontab 里或其他自动化脚本里写的是dnf update不要急着改完全可以正常工作。等你有机会重构脚本时再顺手统一。2. 实际操作中的行为差异缓存、事务与依赖解析的细枝末节虽然命令功能相同但在实际操作中你给它搭配的参数不同行为结果会有明显差异。很多人踩坑不是因为选错了 update/upgrade而是因为没有搭配--refresh、--security等参数或者没有理解 DNF 的缓存机制。2.1 软件源元数据缓存如果不刷新你可能在升级一个过期的“价目表”DNF 默认会把软件源的元数据即软件包列表、版本号、依赖关系等缓存在本地这个缓存有一个有效期默认一般是 90 分钟或更久具体取决于配置。在缓存有效期内你执行dnf upgrade不会重新从远程源下载完整的元数据而是用本地缓存来判断“有哪些可用的更新”。这个机制的初衷是减少网络请求、加快响应速度但也会带来一个实际问题如果你产线服务器已经很久没更新或者软件源侧在短时间内发布了新版本而你的本地缓存还没过期那么即使你执行了dnf upgrade看到的也不会是最新的软件包列表。我之前在一家公司遇到过这样的情况开发环境里明明刚推送了新版本的内核和驱动包但运维在服务器上执行dnf upgrade始终看不到这些包排查了半天最后发现是元数据缓存没刷新。所以如果你想确保拉到最新版本建议使用sudo dnf upgrade --refresh--refresh会强制重新同步软件源的元数据然后再进行升级判断。在更新内核、升级大型组件比如 Python 运行时、数据库客户端库之前我都会习惯性先刷新一下宁可多等几秒也不要用过期数据做决策。除了--refresh你还可以手动清理缓存sudo dnf clean all sudo dnf makecacheclean all会把所有缓存清空makecache会根据当前配置重新生成元数据缓存。这套组合拳通常用在你怀疑本地缓存文件损坏、或者软件源地址发生变更之后。2.2 同队列加载机制为什么同时装多个包是可靠高效的DNF 的另一个核心特性是“同队列加载”same-queue loading也就是在一次事务里同时处理多个软件包的更新。当你执行dnf upgrade时DNF 会收集所有需要更新的软件包把它们视作一个整体事务来解析依赖关系。打个比方这就像你搬家时找了一家搬家公司把所有家具一次性拉走而不是自己一件一件扛下楼。DNF 的同队列机制能避免“A 包依赖 B 包的新版本但 B 包还没更新”这类中间状态问题。在 DNF 的依赖解析逻辑里一个事务的所有包会被放在同一个“解算空间”里libsolv会尝试为所有包找到一组同时满足依赖关系的版本组合。所以当你在执行dnf upgrade时如果它告诉你需要同时更新 300 个包不要慌这是正常现象——它正在为整个系统的软件包做一次“一致性对齐”。有些新手看到更新列表里出现了无关的软件包就怀疑是不是命令用错了其实这是因为这些包之间存在共享依赖关系比如都依赖同一个库的不同子版本系统必须让它们保持在同一批状态。2.3 受保护软件包与事务保护哪些东西不会被轻易动DNF 提供了一套受保护机制主要用来防止内核、系统关键库等被意外删除或替换。体现在命令行上你经常会看到类似这样的提示The downloaded packages were saved in cache till next successful transaction.或者在某些高危操作时Error: The operation would result in removing the following protected packages: kernel-coreRHEL 和 Fedora 系的发行版默认配置了一个受保护包列表位置一般在/etc/dnf/protected.d/目录下。比如kernel-core、glibc这类极其关键的包DNF 在解析事务时发现某个操作会导致它们被移除或替换就会直接拒绝执行或要求你追加--allow-erasing之类的强制参数。这个保护机制对生产环境来说是好事但对一些“非要换掉某个系统组件不可”的场景比如切换内核分支、降级 glibc就成了障碍。如果你确实需要做这种操作建议先看清楚受保护列表内容cat /etc/dnf/protected.d/*再根据实际需要决定是否动用强制参数。我个人建议除非在测试虚拟机里做破坏性实验否则不要轻易绕过受保护机制。3. 更新策略与使用场景到底该用哪个、什么时候用前面已经说了 update/upgrade 本身是同义的但在不同场景下你搭配的参数和命令组合会决定一次更新是“安全可控”还是“随时翻车”。这里我根据自己的运维和开发经验把常见场景分成三类分别给出推荐做法。3.1 生产环境安全更新用 dnf upgrade 加持 security 和 bugfix 过滤器生产环境的原则是“稳定压倒一切”每次更新都要尽量减少变动范围。Fedora 系发行版尤其在 RHEL 和 CentOS 生态里的软件源会为更新包打上类型标签比如安全更新security、缺陷修复bugfix、增强enhancement三类。如果你想在今天只打安全补丁不引入其他无关更新可以这样用sudo dnf upgrade --security只安装有安全公告如 CVE 修复的更新。如果还想限制更新的具体范围可以用sudo dnf upgrade --security --bugfix有些团队还会配合--exclude参数锁定某些特定包的版本比如sudo dnf upgrade --security --excludekernel*这条命令的思路是只做安全补丁更新但跳过所有内核相关包。为什么跳过内核因为内核更新后需要重启才能生效而且某些私有驱动比如定制的 GPU 驱动、安全模块可能与新内核不兼容。锁定内核然后在维护窗口里单独验证内核更新是运维团队很常见的做法。如果你用的是 RHEL 或者基于它的商业发行版还可以配置 OVALOpen Vulnerability and Language源用dnf updateinfo来查看系统当前有哪些已知漏洞尚未修复。不过 OVAL 相关配置在纯 Fedora 上不是默认可用需要自己安装插件并指定源。3.2 日常开发环境升级直接 upgrade 是最简单高效的路径如果你用的是 Fedora Workstation 或类似的桌面开发环境不需要搞那么复杂的安全过滤器直接全量升级反而更省心sudo dnf upgrade --refresh开发环境没有生产环境那么强的稳定性约束系统组件新一点反而可以早点发现上游行为变化对项目的影响。比如某个依赖库升级后改变了 API 行为你在开发环境第一时间发现提前调整代码比等生产环境出问题再排查要舒服得多。不过开发环境升级也有一个需要提醒的点定期重启。很多人升级完系统后不重启觉得“更新完了就完事了”。实际上如果内核、systemd、图形栈这些核心组件更新了不重启的话系统里跑着的进程仍然挂在旧内核甚至旧库上升级并没有真正生效。你可以在升级后检查一下是否有“待重启”状态sudo dnf needs-restarting这个命令会列出由于系统更新而需要重启才能加载新版本的进程服务。如果输出为空说明当前跑着的进程都没有受到更新影响如果列出了不少服务至少要把它们逐个重启最好直接择机重启整机。3.3 大版本之间的升级别用 update/upgrade老老实实用 system-upgrade很多人会问“我用dnf upgrade能不能从 Fedora 39 升到 Fedora 41”我直接说结论不行至少不建议。跨大版本升级比如从 Fedora 39 升 40、从 RHEL 8 升 9要换软件源仓库光靠upgrade命令是做不到的因为你的软件源默认是指向当前版本的发布仓库没有指向新版本。Fedora 官方推荐的大版本升级路径是使用dnf system-upgrade插件sudo dnf install system-upgrade sudo dnf system-upgrade download --releasever41 sudo dnf system-upgrade reboot这套流程会先下载新发布版本的所有所需包、更新系统固件/引导器然后自动重启到升级流程。整个过程比直接upgrade更复杂但可控性高官方支持力度也大。我有一篇专门写 Fedora 跨版本升级的文章里面详细讲了这个插件的使用和踩坑感兴趣的朋友可以去看那篇。简单提醒一句跨大版本升级前一定要先备份重要数据并确保磁盘空间充足。升级过程出现意外断网、断电轻则升级中断重则系统无法引导。3.4 distro-sync什么情况下该用它来对齐源除了update/upgradeDNF 还有一个很容易被忽略但很有用的命令dnf distro-sync。它会把系统里已安装的软件包版本与当前启用的软件源中的版本进行“同步对齐”允许把包降级以匹配源里的设定状态。举例说明如果你之前从某个第三方源安装了比官方源更新的版本或者在某次测试中手动降级了某个包系统当前的状态和官方源不一致这时dnf distro-sync就会让所有包回到官方源定义的版本集合。这个命令在生产环境的用途通常是当你要把系统从一个定制源切换到官方源或者要清理测试阶段留下的实验性软件包版本时。比如sudo dnf distro-sync执行前建议先加--assumeno参数看下预演结果其实是模拟参数DNF 里默认操作会询问确认建议先看它会变更哪些包再决定是否真跑。我在一次切换第三方镜像源时就是用distro-sync把测试环境的包版本拉回官方一致状态避免了重装系统。4. 升级过程中的常见翻车现场与排查思路不管用哪个命令系统更新过程都不可能百分百顺滑。我根据自己的实操经历整理几个高频翻车问题大家遇上了可以少走弯路。4.1 事务检查失败的经典重灾区依赖冲突与 GPG 签名升级时如果你的软件源里出现了同一个包的两个不同版本或者不同仓库之间产生了依赖冲突比如 A 仓库的 libfoo 需要 libbar 2.0而 B 仓库的 libbar 还停留在 1.8DNF 会在事务检查阶段直接报错Error: The operation would result in resolving a dependency conflict这种问题通常不是 DNF 本身的问题而是第三方软件源没有和发行版官方源做好协同。排查思路我一般按下面这几步走先看错误提示里具体提到了哪个包、哪个依赖冲突。执行dnf repolist查看当前启用了哪些软件源。逐个禁用可疑的第三方源尤其是那些已经很久不维护的。比如我们可以临时禁用某个有问题的源再试sudo dnf upgrade --disablereposome-third-party-repo如果禁用后问题消失说明冲突根源在第三方源。这时可以等第三方源更新或者干脆移除它。另一个高频毛病是 GPG 签名校验失败Error: GPG check FAILED这通常是软件源维护者更换了签名密钥或你本地缓存的公钥过期。处理方式很直接——重新导入该源的官方 GPG 公钥或者去源的官网下载最新公钥并执行rpm --import key-file。正规发行版Fedora、RHEL、EPEL 等在官方文档里都有公钥地址。不要图省事直接加--nogpgcheck参数跳过校验除非你明确知道自己正在做什么比如在隔离测试环境里用本地源做实验。4.2 不要随便加 --force 或 --allowerasing两个危险参数的自白DNF 有两个让新手容易误用的参数--force在 DNF 里一般体现为--setopt*相关和--allowerasing。准确说DNF 里没有--force这个通用参数rpm 层面才有rpm -Uvh --force但 DNF 命令里有一些替代强制的选项。这里要重点批评的是--allowerasing。--allowerasing允许 DNF 在解决依赖关系时自动移除那些“看起来不再被需要”的包以完成升级事务。危险之处在于某些包虽然在依赖解析器里不构成依赖但它们是你系统运行中实际需要的。举一个真实案例某次我在一台跑着旧版本监控代理的服务器上执行升级日志系统里某个采集器包因为依赖变化导致 DNF 认为它和新的 libpcap 版本冲突。我没有细看就直接加了--allowerasing跑完升级监控代理被悄悄删掉了直到过了一个多星期才发现监控数据断档。所以当你遇到依赖冲突时正确操作顺序是先看错误信息判断是哪几个包在打架。用dnf repoquery --whatrequires 包名查一下冲突包的被依赖关系。考虑是通过禁用某个源解决还是单独升级/降级某个包。只有完全确定某个包确实被废弃、不再需要才能用--allowerasing且之后要重新检查系统状态。4.3 内核更新后无法开机或模块不匹配内核更新是升级过程中最能制造“心跳骤停”的环节之一。更新完重启系统卡在引导界面或者某个驱动模块加载失败是很多人升级后最怕遇到的情况。Fedora 系发行版默认会保留最近几个内核版本这让回滚变得简单。如果你升级到新内核后无法正常启动可以在 GRUB 引导菜单里选旧版本内核进入系统。进入后就地解决问题的方法有sudo dnf remove new-kernel-version或者用grubby调整默认内核版本sudo grubby --set-defaultold-kernel-version我对生产服务器处理内核更新有一个比较朴素的原则不要追新内核。新内核往往意味着新的硬件支持、性能优化和安全修复但在你没有可用的备用环境验证之前不要直接推到生产。正确的流程是先在一台测试机上跑一周确认应用无异常再在生产环境单独升级内核并安排维护窗口重启。4.4 磁盘空间不足与下载中断升级过程中系统会先下载所有需要的 RPM 包到缓存目录默认是/var/cache/dnf再进行事务性安装。如果你的系统盘空间不足下载阶段可能不会报错但在解压和安装事务执行时会因为临时空间不足而产生莫名其妙的失败。我建议每次做大型升级前都先检查下磁盘使用率df -h /通常/根分区至少要有 2GB 以上的剩余空间跨大版本升级时建议留出 5GB 以上。空间不够时优先清理旧的缓存包sudo dnf clean packages这个命令只清理已下载的缓存包不会影响已安装系统是回收空间的性价比之选。还可以清理旧的 journal 日志journalctl --vacuum-time3d或者用dnf autoremove卸载不再被依赖的孤立包。如果下载中断DNF 本身有断点续传能力但如果你使用了代理或镜像源偶尔会出现缓存文件损坏。这时清掉缓存重试即可sudo dnf clean all sudo dnf upgrade --refresh5. 好习惯比命令选择更重要我给新手的几点实操建议既然update和upgrade本身没有本质区别那比选命令更重要的是围绕升级建立一套可靠的操作习惯。下面列几个我自己长期坚持的做法。5.1 升级前先看 updateinfo 和依赖关系在执行任何重要升级前我会先用只读命令查看更新列表而不是直接升级dnf updateinfo dnf list updatesdnf list updates会把所有可更新的软件包列出来包括版本号、仓库来源dnf updateinfo则显示这些更新里哪些属于安全公告、哪些是普通修复。看完这两项输出你对自己即将面临的“动荡”会有个基本预期是一次几十个包的大规模变动还是仅仅几个补丁。另外如果某个包要升级大版本比如从 3.x 升到 4.x我会额外查一下它的更新日志dnf changelog 软件包名这个命令会列出最近几个版本的变化说明。如果更新日志里出现了不兼容变更比如配置格式变化、默认行为调整我就知道要先看官方迁移文档或者调整自己的配置文件。5.2 合理使用事务历史与事务回滚DNF 的事务历史功能是一个宝藏可惜很少被人提起。每次执行成功的升级、安装、移除操作DNF 都会把它记录成一条事务你可以随时查看dnf history如果某次升级后系统异常需要让某些包回到升级前版本可以用sudo dnf history rollback 事务ID不过 rollback 并不是万能的它有很严格的先决条件——DNF 必须能拿到原来的软件包版本并计算出可行的回滚事务。如果升级过程中涉及大量依赖变更或者旧版本包已经在下游源中被删除rollback 可能会失败此时只能手工去源里找对应的 RPM 包或者用dnf downgrade指定旧版本号逐包处理。即使 rollback 有各种限制我仍然建议你在做高危升级前先运行dnf history记录当前事务列表至少心里有数最近的变更基础是什么。5.3 使用 exclude 锁定关键包版本对生产系统和重要的开发环境我一般会用exclude锁定一些关键包的自动更新。锁定的对象通常是内核相关包kernel*特定的驱动模块nvidia*或自定义驱动关键运行时如自编译的 OpenSSL、libc 相关组件配置方式有临时和持久两种。临时的方式是命令行直接加sudo dnf upgrade --excludekernel*持久的方式更推荐在 DNF 配置里设置[main] excludepkgskernel* nvidia*位置在/etc/dnf/dnf.conf。这样配置后dnf upgrade默认就会跳过这些包当你确实要升级它们时再临时用--disableexcludesall参数解除锁定。注意别把 exclude 写得过于宽泛我曾经见过有人把glibc*也锁了结果系统上的其他包都因为无法更新动态链接库而逐渐变得陈旧最后不得不手动解除锁定再集中更新反而增加了风险。5.4 升级完不是终点验证与监控系统升级只是一个开始真正重要的是升级后的验证。我会在升级完成后做三件事检查系统版本和关键包版本uname -r cat /etc/os-release dnf list installed | grep glibc查看是否存在“需要重启的服务”sudo dnf needs-restarting确保系统服务运行正常。如果你在用 systemd可以快速检查异常单元systemctl --failed --no-pager这三个检查做完我才认为一次升级流程画上了句号。很多人升级完不检查就直接跑结果部分服务挂了一个星期才发现这时候排查起来可没那么轻松。5.5 定期升级比一次憋个大更新更安全运维老手常说“小步快跑”系统升级也是同样的道理。如果你三个月不升级一次憋个一千个软件包的大事务风险远高于每周或每两周做一次小规模升级。升级本质上是在“功能演进”和“稳定风险”之间做平衡频率太低会让每次事务的变动集太大一旦出问题排查面也会大很多。我的建议是个人桌面机每周执行一次dnf upgrade --refresh配合重启。开发测试机可以根据迭代节奏每两周做一次安全更新 全量更新。生产服务器每月至少一次dnf upgrade --security每季度或半年结合维护窗口做一次全量更新。当然具体节奏还要看你所在团队对变更管理的偏好但原则无非是“定期、可控、可回滚”。6. 最后分享一个我自己的小习惯写了这么多其实核心就是一句话与其纠结update和upgrade哪个更好不如关心你升级时带了什么参数、有没有刷新索引、有没有锁定关键包。从 DNF 4 到 DNF 5update都只是upgrade的别名两个命令在行为上没有本质区别把力气花在参数组合和升级策略上才是真正能让你少踩坑的地方。我个人倾向于在所有脚本和文档里统一用dnf upgrade因为它在语义上更清晰未来兼容性也更好。但如果你在惯用的脚本里看到dnf update完全不用紧张它和dnf upgrade干的是同样的活。升级系统前多看几眼dnf history、dnf updateinfo更新完跑一下dnf needs-restarting这几分钟的成本换来的是半夜不用被监控告警电话叫醒我觉得非常划算。