1. 报错现场与问题还原1.1 报错长什么样在CentOS 7或同类RHEL系服务器上安装PostgreSQL时很多人会栽在一个看起来莫名其妙的依赖错误上。最常见的场景是你执行了yum install postgresql12-server或者yum install postgresql13-server结果终端没有像预期那样开始下载一堆rpm包而是直接弹出一段红色报错Error: Package: postgresql12-server-12.x-1PGDG.rhel7.x86_64 Requires: llvm-toolset-7-clang 4.0.1有一些变体还会附带类似“Nothing provides llvm-toolset-7-clang”的信息。初次遇到的人会非常懵我装PostgreSQL跟clang有什么关系clang不是编译C/C用的编译器吗数据库安装为什么非要它不可这也正是我写这篇东西的原因——把这个报错一次性讲透并且给出几种可以落地的解决办法。这个报错出现的频率相当高尤其是在CentOS 7、RedHat 7这类还停留在yum体系的系统上。CentOS 8/Stream和Rocky Linux 8/9用户也会遇到类似的依赖提示只是包名变成了llvm-toolset-11-clang或者llvm-toolset-12-clang背后的逻辑完全相同。所以别看标题写的是llvm-toolset-7-clang本质上你只要把这一套原理和修复思路掌握了几乎可以覆盖所有RHEL系的PostgreSQL安装问题。1.2 这个报错信息到底在说什么我们先把报错拆开看。关键部分是两个一个是Requires:一个是llvm-toolset-7-clang 4.0.1。Requires是RPM包管理器的依赖声明字段。它的意思是这个rpm包在被安装之前系统里必须已经存在满足条件的另一个软件包。这不是PostgreSQL自己搞的特殊机制而是所有rpm包的通用规则。比如很多软件会声明Requires: openssl-libs 1.0.2说明它运行时依赖某个版本的openssl库如果系统里没有或者版本太低yum就会拒绝安装。而llvm-toolset-7-clang 4.0.1翻译过来就是需要一套版本不低于4.0.1、由SCL软件集提供的clang工具链。这个llvm-toolset-7是RedHat Software CollectionsSCL中的一个软件集名称里面打包了LLVM 7、Clang 7以及配套的工具。PostgreSQL的rpm包在编译时开启了JITJust-In-Time Compilation即时编译支持运行时要调用clang做表达式编译因此包管理器强制要求系统里得有这个依赖。所以这个报错的本质不是你的系统坏了也不是PostgreSQL安装包损坏而是你正在使用的yum源里根本找不到llvm-toolset-7-clang这个包或者能找到的版本低于4.0.1导致依赖解析失败。2. 为什么安装 PostgreSQL 会扯上 LLVM/clang2.1 PostgreSQL的JIT特性要理解这个依赖得从PostgreSQL的JIT功能说起。JIT技术在很多编程语言运行时里都有比如Java的HotSpot、Go的编译执行它的核心思想是程序运行时把热点代码动态编译成机器码从而大幅提高执行效率。PostgreSQL从11版本开始引入了基于LLVM的JIT加速主要用来优化表达式计算和元组过滤。举个例子假设你有一条SQL查询SELECT * FROM orders WHERE amount * discount 1000。普通执行方式是数据库逐行读取数据对每一条都解释执行amount * discount 1000这个表达式。这个过程涉及到解释器的开销。而开启JIT后PostgreSQL会把这条表达式交给LLVM编译成机器码之后的每一行判断都直接执行机器码速度可以提升数倍。这个功能在OLAP场景下收益尤其明显。比如分析师跑一个大表的聚合查询其中包含大量复杂表达式JIT能把执行时间缩短到原来的三分之一甚至更少。但对OLTP场景来说单个查询的执行时间本身就很短JIT反而会因为编译本身的开销拖慢速度所以PostgreSQL也允许通过jit on/off参数或者jit_above_cost阈值来控制。正是因为PostgreSQL把LLVM作为JIT的底层引擎编译出来的二进制在运行时会动态加载LLVM库并调用clang将SQL表达式编译成字节码。这就让PostgreSQL的软件包从“数据库本体”变成了“数据库本体LLVM运行时依赖”的组合体。2.2 打包策略导致的“硬依赖”这里有一个很多人容易忽略的技术细节PostgreSQL的JIT功能在运行时是需要LLVM库的而不是编译时装一下就完事。所以软件包维护者在打rpm包时必须把这个运行时依赖写进Requires字段里。否则你把PostgreSQL装好了执行一条简单查询没问题一遇到触发JIT的复杂查询就直接崩溃那才是更大的灾难。但问题来了CentOS 7系统仓库里并不是完全没有LLVM。llvm这个包在系统基础源里是存在的只不过版本极老。CentOS 7自带的是LLVM 3.4左右而PostgreSQL的JIT实现要求的是更新的LLVM版本。PostgreSQL官方文档里也说得很明确JIT目前至少需要LLVM 4.0.1以上版本。如果打包者把依赖写成系统自带的llvm包那安装时会发现版本不满足反正结果一样是报错。所以PostgreSQL的rpm包维护者干脆换了一种思路不依赖系统源里的llvm而是依赖SCL仓库里的llvm-toolset-7。SCL的全称是Software Collections是RedHat针对老旧系统提供新版开发工具链的方案。CentOS 7发布时自带的gcc、llvm等编译器都很老但又不能随便升级系统组件因为系统级的编译器更新可能会影响其他软件。SCL的做法是把新版本工具装到/opt/rh/目录下通过环境变量切换使用这样既不污染系统目录又能提供足够新的工具链。PostgreSQL的rpm包信任了SCL仓库作为依赖来源于是就在Requires里写下了llvm-toolset-7-clang 4.0.1。如果你没启用SCL仓库yum自然找不到这个包报错就如期而至。2.3 为什么偏偏是 llvm-toolset-7如果你去翻PostgreSQL的编译脚本会发现它对LLVM的最低版本要求是4.0.1。而llvm-toolset-7里面的LLVM正好是7.x系列完全满足这个要求。选择这个特定版本还有一个重要原因PostgreSQL 12、13这些版本在CentOS 7上是主流安装版本打包维护者希望用一套固定的工具链来构建这样能保证所有用户拿到的是同一套二进制依赖减少兼容性问题。有些人在网上看到别人说装PostgreSQL不需要装LLVM那是因为对方用的可能是PostgreSQL老版本比如9.6或10这两个版本没有JIT功能自然没有这个依赖。或者对方用了官方提供的静态编译版本也就是把LLVM一起打包进PostgreSQL可执行文件里不需要系统级依赖。如果你问能不能用系统自带的clang替代答案是基本不行。一方面版本太老另一方面SCL的包名和系统包名不同即使你手动装了一个clangRPM的依赖解析器也未必认。RPM解析依赖时是严格按照包名来的它不会去判断“系统里有个clang所以这个依赖应该算满足”。它只会在启用的仓库里搜索看有没有叫llvm-toolset-7-clang的包。3. 解决方案三种实操路径3.1 方案一补齐SCL仓库并安装依赖推荐最正统的做法是把SCL仓库加进来让yum能解析到llvm-toolset-7-clang然后重新安装PostgreSQL。整个过程分为三步。第一步确认系统版本。这个方法只适用于CentOS 7/RHEL 7系列。执行cat /etc/redhat-release查看一下。如果显示的是7.x那就没问题。第二步安装SCL仓库。CentOS 7的SCL仓库包含在centos-release-scl这个包里这个包又在extras源里。CentOS 7默认没有启用extras源所以需要先装回来。命令是yum install -y centos-release-scl装完这个包之后/etc/yum.repos.d/下会新增一个CentOS-SCLo-scl.repo文件。注意有些版本还会生成CentOS-SCLo-scl-rh.repo里面包含的是RH系原生SCL包。如果碰到找不到包的情况可以逐个查看这些repo文件里的enabled字段是否为1。第三步安装llvm依赖并安装PostgreSQL。执行下面的命令yum install -y llvm-toolset-7-clang yum install -y postgresql12-server如果仓库配置正常llvm-toolset-7-clang会被安装到/opt/rh/llvm-toolset-7/目录下同时会带上llvm-toolset-7-llvm、llvm-toolset-7-llvm-libs等几个配套依赖。这些包加起来大概占用两三百MB磁盘空间属于正常的体积。这一步执行完依赖问题就解决了。你可以继续执行postgresql-setup --initdb初始化数据库目录然后用systemctl start postgresql-12启动服务。注意如果你用的是PostgreSQL 11或更早版本部分仓库的依赖包名可能会稍有差别但操作逻辑一致都是先装对应的SCL仓库再装clang。3.2 方案二使用PGDG官方仓库自动解决依赖有时候你可能会发现自己已经安装了centos-release-scl但还是报同样的错。这种情况通常是因为系统里启用了多个PostgreSQL源比如某个第三方镜像源里也包含了PostgreSQL的rpm包但它的依赖声明写得更严格。这时候更稳妥的做法是只用PostgreSQL官方社区仓库PGDG。PGDG仓库里已经把PostgreSQL和LLVM的关系处理得很完善只要你的系统能正常访问额外仓库源它会自动把SCL相关的依赖一起拉下来。操作步骤如下。先卸载掉之前可能装坏的PostgreSQL相关rpm避免冲突yum remove -y postgresql* llvm-toolset*然后安装PGDG仓库安装包yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm如果这一步因为网络原因无法访问官网可以换成国内的镜像源但要注意镜像源的同步频率最好挑更新时间的。安装完成后查看仓库列表yum repolist | grep pgdg正常情况下会看到pgdg12、pgdg13等对应版本的仓库。这里有个小坑PGDG仓库默认可能同时启用了所有PostgreSQL主版本如果只用postgresql12-server这个包名yum可能会优先匹配到系统自带或第三方源的版本。稳妥起见可以显式禁用其他版本仓库yum install -y --enablerepopgdg12 postgresql12-server这种做法的核心价值在于PGDG仓库的rpm包在打包时就清楚标注了llvm-toolset-7-clang依赖并且仓库元数据里就包含这个包的引用yum能在它自己的依赖解析过程中自动从SCL仓库拉取。你不需要手动关心依赖顺序比方案一更省心。3.3 方案三绕过依赖的临时做法不推荐但要知道网上确实有人分享过一种“偷懒”做法直接用yum install postgresql12-server --nodeps来跳过依赖检查。这个参数的意思是“不检查依赖关系直接安装rpm包”。从我个人的经验来看这个做法只适合极端场景比如你只是临时在一个开发环境里测试一下PostgreSQL功能压根不在乎JIT能不能用。但我要明确说不推荐在生产环境这么干。原因很简单--nodeps安装之后你在执行某些复杂查询或者开启jit参数时PostgreSQL进程会去调用/opt/rh/llvm-toolset-7/root/usr/bin/clang这个路径下的可执行文件。当系统里没有这个文件时数据库不会直接崩溃但JIT功能会初始化失败日志里会反复出现类似could not load library llvmjit的报错。查询性能在特定负载下会明显下降而且容易造成日志刷屏。如果实在只能用--nodeps安装完之后你必须手动把jit参数关掉。具体做法是编辑postgresql.conf找到#jit on这一行改成jit off然后重启数据库服务。这样至少能保证数据库正常工作只是放弃了JIT速度提升。我遇到的真实案例里有一位开发者是在内网离线环境装的PostgreSQL完全无法访问SCL仓库也没有本地镜像。他最后用--nodeps装完之后通过把JIT参数关闭解决了问题。这个做法虽然合法合规可用但你要是问我我还是建议你优先用方案一或方案二别给自己埋隐患。3.4 方案对比总表方案适用场景操作难度风险等级推荐程度方案一装SCL仓库后补依赖大部分CentOS 7环境能访问互联网低低推荐方案二用PGDG仓库仓库混乱、依赖冲突多、需要官方源中低最推荐方案三跳过依赖检查离线环境、临时测试、明确不需要JIT低高不推荐4. 完整实操记录模拟复现为了让你更直观地理解整个修复过程我这里模拟一次完整的故障处理和修复记录。假设有一台刚初始化好的CentOS 7.9系统准备用来部署PostgreSQL 12。4.1 环境说明系统CentOS Linux release 7.9.2009 (Core)初始yum仓库只有系统自带的base、extras、updates。没有安装过任何SCL仓库也没有配置PGDG仓库。此时执行yum install postgresql12-server报错信息如下Loaded plugins: fastestmirror Resolving Dependencies -- Running transaction check --- Package postgresql12-server.x86_64 0:12.20-1PGDG.rhel7 will be installed -- Processing Dependency: llvm-toolset-7-clang 4.0.1 for package: postgresql12-server-12.20-1PGDG.rhel7.x86_64 -- Finished Dependency Resolution Error: Package: postgresql12-server-12.20-1PGDG.rhel7.x86_64 Requires: llvm-toolset-7-clang 4.0.1这一步的报错跟标题里的几乎一模一样。现在开始修复。4.2 具体修复步骤第一步先检查系统里是否已经有了SCL相关的repo文件。执行ls /etc/yum.repos.d/输出为CentOS-Base.repo CentOS-Debuginfo.repo CentOS-fasttrack.repo CentOS-Sources.repo CentOS-Vault.repo epel.repo确实没有SCL仓库。于是按方案一执行yum install -y centos-release-scl执行过程中yum会自动从extras源拉取这个包安装完成后再次查看repos.d目录CentOS-Base.repo CentOS-Debuginfo.repo CentOS-fasttrack.repo CentOS-Sources.repo CentOS-Vault.repo CentOS-SCLo-scl.repo CentOS-SCLo-scl-rh.repo epel.repo第二步检查SCL仓库是否启用了正确的包。执行yum --disablerepo* --enablerepocentos-sclo-rh list available | grep llvm-toolset这一步可能报错“没有匹配的包”因为repo的ID可能叫CentOS-SCLo-scl-rh或centos-sclo-rh。如果遇到这种情况先执行yum repolist all | grep -i scl看一下启用的仓库。我这里看到sclo-rh处于启用状态所以直接执行yum install -y llvm-toolset-7-clang安装完成后检查一下clang的路径。重点注意这个路径不是/usr/bin/clang而是/opt/rh/llvm-toolset-7/root/usr/bin/clang。可以用以下命令确认/opt/rh/llvm-toolset-7/root/usr/bin/clang --version输出类似clang version 7.0.1 Target: x86_64-redhat-linux-gnu说明依赖已经就位。第三步重新安装PostgreSQLyum install -y postgresql12-server这次yum没有再报错顺利进入下载和安装流程。可以看到它同时安装了postgresql12、postgresql12-server、postgresql12-libs等包。4.3 验证安装成功安装完成后还需要做初始化操作否则数据库服务启动不了。执行/usr/pgsql-12/bin/postgresql-12-setup initdb systemctl enable postgresql-12 systemctl start postgresql-12然后执行一个小查询验证数据库能正常工作sudo -u postgres psql -c select version();输出里能看到12.x版本的PostgreSQL信息。再用以下SQL验证JIT是否可用select name, setting from pg_settings where name like jit%;正常情况下会有jit on、jit_above_cost 100000等参数。这就说明LLVM依赖被正确安装JIT功能已经完全就绪。5. 常见问题与排查技巧实录5.1 安装 llvm-toolset-7-clang 时提示找不到包这是最常遇到的情况。明明加了SCL仓库但yum install llvm-toolset-7-clang却提示“No package llvm-toolset-7-clang available”。原因通常是几个第一你安装的centos-release-scl版本默认只启用了CentOS-SCLo-scl.repo而这个repo里可能不包含llvm-toolset-7它只提供rh系列之外的软件集。这时候你需要看一下CentOS-SCLo-scl-rh.repo这个文件里enabled是否等于1。解决办法很直接手动启用对应的repo。yum install -y --enablerepocentos-sclo-rh llvm-toolset-7-clang如果repo ID不对先执行yum repolist all | grep scl查看完整的repo ID列表再对应调整。第二个原因是网络问题。SCL仓库在国内访问速度有时较慢yum因为超时会误判为“找不到包”。这种时候可以配置一下yum的超时时间或者在/etc/yum.conf里把timeout30改成更大值。也可以更换成国内镜像站的centos-sclo-rh源具体做法是编辑对应的.repo文件把baseurl里的mirror.centos.org替换成镜像站地址。第三个原因是系统架构。llvm-toolset-7虽然同时支持x86_64和aarch64但如果你用的是比较冷门的架构仓库里可能没有对应的rpm。这种场景下建议直接换方案二用PGDG的源码编译安装。5.2 与系统自带 LLVM 冲突有些环境里你之前可能因为别的原因装过llvm或者clang包这时候再装llvm-toolset-7-clang会出现文件冲突。报错里最常见的是file /usr/bin/clang conflicts between attempted installs of llvm-toolset-7-clang and clang-3.4-xxx看到这个不要慌。SCL的设计初衷就是和系统工具并存它不会覆盖/usr/bin/clang而是把文件放到/opt/rh/前缀下。但如果你系统的clang包是通过--nodeps强制安装或者源码编译安装的文件已经占用了目标路径就会产生冲突。解决办法是先确认系统自带的clang是谁装进来的rpm -qf /usr/bin/clang如果输出显示是某个rpm包可以把它卸载掉再装llvm-toolset-7-clang。如果没有任何rpm包信息说明是源码编译安装的直接删掉/usr/bin/clang和相关目录手动清理一下即可。这里有一个比较隐蔽的坑Oracle Linux等RHEL兼容系统自带了一些额外的LLVM模块。删除之前最好确认没有其他服务依赖这个路径下的clang。可以用lsof /usr/bin/clang查一下是否有进程正在占用避免误删导致其他应用崩溃。5.3 数据库服务能启动但JIT功能异常有朋友遇到过这样的情况依赖报错已经解决PostgreSQL也装好了服务能正常启动但每次执行复杂查询时数据库日志里都会出现ERROR: could not load library /usr/pgsql-12/lib/llvmjit.so这种情况让很多人百思不得其解当时明明装了llvm-toolset-7-clang怎么还是加载失败其实问题不在clang本体而在llvm-toolset-7-libs上。PostgreSQL的llvmjit.so库在运行时会动态链接LLVM运行时库。如果你安装时只选了llvm-toolset-7-clang而yum没有自动拉取llvm-toolset-7-llvm-libs依赖就会出现这种诡异局面。解决办法是补装yum install -y llvm-toolset-7-llvm-libs装完再重启PostgreSQLsystemctl restart postgresql-12之后再去跑一次复杂SQL日志就干净了。这个问题一开始很难排查因为启动服务时不报错只有执行特定查询时才暴露。所以我的经验是排查这类JIT加载问题优先看/var/lib/pgsql/12/data/log/下的日志而不是死盯着安装过程。5.4 源码编译时如何规避如果你选择了源码编译安装PostgreSQL而不是用rpm包这个问题其实可以从源头绕开。编译时的配置选项里有一个关键参数控制JIT./configure --without-llvm加上这个参数后编译出来的PostgreSQL完全没有JIT功能自然不会对llvm和clang产生依赖。如果你不追求新特性也不跑OLAP复杂分析场景这是最轻量、最省事的编译方式。反过来如果你明确需要JIT功能编译时需要保证系统里有LLVM和clang开发包。在CentOS 7上推荐先安装SCL工具集然后设置环境变量yum install -y llvm-toolset-7 scl enable llvm-toolset-7 bash在这个启用了工具集的shell环境里执行./configure和make你就能编译出带JIT支持的PostgreSQL。注意这会生成一个动态链接LLVM库的版本部署到其他机器时那台机器也同样需要安装LLVM库否则运行时会报错。我对看到这里的朋友有个建议如果是自用学习环境源码编译时直接--without-llvm就够了省去后续所有依赖烦恼如果是要搭生产环境PostgreSQL优先用官方rpm包并严格按照方案一或方案二把依赖补齐。JIT这个功能说实话在生产OLTP负载下不一定有明显收益反而增加部署复杂度很多人最终都会把jit off关闭。我自己在最早的几次部署中也踩过同一个坑当时图省事用--nodeps跳过依赖结果数据库跑了大半年都没问题直到某次做报表分析时发现查询一直走不快翻日志才发现JIT加载失败。后来老老实实把SCL仓库补上重新安装所有依赖问题才算彻底解决。从那以后我再也没有跳过依赖检查毕竟安装阶段省下的十分钟往往会在后续运维里以更麻烦的方式找回来。最后再分享一个运维习惯每次装完PostgreSQL无论用的哪种方式都顺手执行一次yum list installed | grep -E llvm|clang看一眼相关的包是否齐全、版本是否满足。这个习惯花不了十秒钟但能帮你在问题扩大化之前快速定位到根因。希望这篇内容能帮你顺利度过这个不大不小的安装关卡。