☰
CentOS 7.5源码编译Redis 6.0.1完整指南
2026/10/19 9:16:46 网站建设 项目流程

前阵子要在一批 CentOS 7.5 的老机器上统一部署缓存,业务方态度很明确:就要 Redis 6.0.1。我一开始想偷懒,直接yum install redis,结果拉出来的版本是 3.2.12,ACL、新版集群管理这些能力全是空的;去官方下载预编译二进制,放到 CentOS 7.5 上又经常被 glibc 版本卡住。来回折腾之后发现,在 CentOS 7.5 / RHEL 7.x 环境里编译安装 Redis 6.0.1,反而是最可控、最不依赖外部条件的方式。这篇文章把我完整跑通的命令、参数和踩过的坑全写出来,照着抄基本能一次过。

1. 为什么绕不开"编译安装"这条路

1.1 仓库里的版本和预编译包都卡在哪

CentOS 7 的默认软件仓库里,Redis 停留在 3.2.12。这个版本不是不能用,但和 6.0.1 的差距实在太明显:6.0 引入了 ACL 用户权限体系,可以对不同客户端做精细化权限控制;引入了多线程 I/O,虽然默认不开,但给了你优化空间;RESP3 协议也是从这代开始支持的。业务方如果是冲着这些能力来的,3.2.12 基本没法交差。

有人会说,那装 EPEL 仓库试试?EPEL 在 CentOS 7 上确实能提供更新的 Redis,但版本依然停留在 5.x 左右,而且升级节奏不受你控制,想精确锁定 6.0.1 这个版本号很难。

再看官方下载站里的预编译二进制。官方多数情况下会针对较新的发行版来编译,拿回 CentOS 7.5 这种老系统上,经常会遇到类似GLIBC_2.18 not found的报错。说到底,CentOS 7.5 的 glibc 版本偏旧,官方二进制没有义务向下兼容到这么老的运行库。这种情况下,自己拿源码编译反而是最干净的出路。

1.2 哪些场景适合走编译路线

如果你遇到下面这几种情况,编译安装基本是唯一合理选择:

  • 需要精确锁定某个版本,比如 6.0.1,不跟随发行版仓库的随意变动;
  • 需要把安装目录放到自定义路径下,方便多版本共存或离线打包分发;
  • 想通过编译参数开启特定模块,比如 TLS 支持;
  • 服务器不能连外网,需要在内网离线环境用源码包落地上线。

编译安装也能让你对安装过程完全可控,知道每一步做了什么。对生产环境来说,可控比省事更重要。

所以环境准备上,第一个要解决的不是 Redis 本身有什么依赖,而是编译器。

2. 环境准备:CentOS 7.5 的第一道坎是 gcc,不是 Redis

2.1 默认 gcc 4.8.5 编译 Redis 6.x 为什么会直接挂

CentOS 7.5 自带的 gcc 是 4.8.5。这个版本发布得很早,对 C11 标准的支持并不完整,而 Redis 6.x 源码里大量使用了 C11 的原子操作类型_Atomic和相关头文件。直接用它编译 Redis 6.0.1,你很可能会在server.c或者其他核心文件里看到类似_Atomic未定义、stdatomic.h找不到的报错。

我见过不少新手栽在这里:源码包解压好,make一跑,满屏报错,然后开始怀疑是不是 Redis 源码有问题,甚至去网上搜各种奇怪的“补丁”。其实原因很简单,就是编译器太老。

注意,不要去动系统的 glibc。网络上有一些文章为了编译新软件,会叫你升级系统基础运行库,这在 CentOS 7.5 上风险极高,极有可能把整个系统的动态库依赖搞崩。正确的思路是:用 Red Hat 系的 SCL 软件集,单独装一个新版 gcc,编译完就完事,不污染系统自身。

2.2 用 devtoolset-9 把编译器切到 gcc 9

在 CentOS 7.5 上,我推荐使用 devtoolset-9,它提供的是 gcc 9.x 系列,编译 Redis 6.0.1 足够用了。安装命令如下:

yum install -y centos-release-scl yum install -y devtoolset-9 scl enable devtoolset-9 bash gcc --version

scl enable devtoolset-9 bash这条命令的意思是:在当前 shell 会话里临时把 devtoolset-9 的 bin 目录加到 PATH 前面。跑完gcc --version,如果看到 gcc 9.x,就说明切换成功。

这里有个特别容易忽略的细节:SCL 的切换只对当前终端会话有效。如果你开两个 SSH 窗口,一个窗口scl enable之后开始编译,另一个窗口没切,直接用旧的 gcc,那你第二个窗口编译必然报错。更隐蔽的场景是:你用脚本或 CI 工具跑编译,脚本里没激活 SCL 环境,编译一样会失败。

我习惯在~/.bashrc末尾加上一行:

source /opt/rh/devtoolset-9/enable

这样新开的终端会话会自动带上 gcc 9。如果你担心影响其他编译任务,那就只在编译时执行scl enable devtoolset-9 bash,用完退出终端即可。

2.3 顺手准备的系统依赖与目录规划

编译 Redis 6.0.1 本身不依赖太多东西,但如果你后面想跑make test,就需要 tcl,官方测试框架依赖它。建议提前装好:

yum install -y tcl

另一个可选依赖是 openssl-devel。Redis 6.0.1 支持 TLS,如果你打算启用加密传输,需要这个头文件库;暂时用不上也可以跳过。

目录规划上,我不建议把 Redis 安装到/usr/local/bin这种全局目录了事。更好的做法是使用带版本号的独立目录,方便日后升级切换:

mkdir -p /usr/local/redis-6.0.1/bin mkdir -p /usr/local/redis-6.0.1/etc

数据目录和日志目录建议独立放在系统数据区:

mkdir -p /var/lib/redis mkdir -p /var/log/redis

目录建好之后先放着,等创建了 redis 用户再一起调整权限。

环境准备好了,下面去拿源码包。

3. 源码下载、编译、安装:我把每条命令都拆开讲

3.1 下载官方源码包并做 SHA256 校验

源码包一定要从官方渠道下载,Redis 官方下载页对应地址如下:

cd /usr/local/src wget https://download.redis.io/releases/redis-6.0.1.tar.gz

下载完成后不要急着解压,先做校验。官方页面会给出对应文件的 SHA256 值,你在本机用sha256sum算一遍,再和官方列出的值比对:

sha256sum redis-6.0.1.tar.gz

这一步很多人会跳过。说实话,如果只是内网自己用,跳过可能也没事;但如果是生产环境部署,源码包被下载源掉包的风险虽然低,后果却不可控。多花十秒钟,买个心安。

校验没问题再解压:

tar xzf redis-6.0.1.tar.gz cd redis-6.0.1

3.2 make 与 MALLOC=libc:为什么必须认识这个参数

Redis 的源码没有 configure 脚本,它和经典的 autotools 流程不一样,不需要./configure && make && make install三步走。Redis 的 Makefile 自带依赖检查和构建逻辑,你进入源码目录后直接make就能编译。

但这里有一个很关键的参数:MALLOC。

Redis 默认使用自己内置的 jemalloc 内存分配器,这是为了在高并发场景下减少内存碎片、提高分配效率。但问题在于,Redis 6.0.1 自带的 jemalloc 在 CentOS 7.5 配合 gcc 9 的环境下,偶尔会出现编译失败或运行期异常。最典型的现象就是卡在zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory。

所以我在这类老系统上执行 make 时,习惯直接指定使用系统的 libc 分配器:

make -j$(nproc) MALLOC=libc

-j$(nproc)表示用 CPU 核心数并行编译。如果服务器内存不大,比如只有 2GB,却是个 8 核机器,并行编译可能会把内存吃满导致 OOM。这种情况建议保守一点,手动限制并发数:

make -j2 MALLOC=libc

用MALLOC=libc会不会损失很多性能?对绝大多数应用来说,差异远没有想象中大。jemalloc 的优势主要体现在长时间运行、高并发、内存分配频繁的场景,如果内存碎片率本身不严重,libc 分配器完全够用。我曾在一台 2C4G 的虚拟机上分别跑过,日常业务 QPS 基本看不出差别。这一点后面验证章节也会提到。

3.3 make install PREFIX 与重新编译的坑:make distclean

编译本身过程比较安静,跑完没有报错之后,建议做一次官方测试:

make test

make test需要 tcl 环境,前面已经装好。测试会跑一段时间,看到类似\o/ All tests passed without errors!的输出才算完整通过。这一步很多教程会略过,但我建议至少在新环境第一次编译时跑一遍,它能提前暴露出运行时问题,而不是等你把 Redis 部署上去之后才发现进程起不来。

测试通过后执行安装,指定 PREFIX 路径:

make install PREFIX=/usr/local/redis-6.0.1

这个命令会把redis-server、redis-cli、redis-sentinel、redis-benchmark、redis-check-aof、redis-check-rdb等二进制复制到/usr/local/redis-6.0.1/bin目录下。你可以验证一下:

ls -l /usr/local/redis-6.0.1/bin /usr/local/redis-6.0.1/bin/redis-server --version

到这里,二进制就算装好了。

这里必须提醒一个坑:如果你在编译过程中改变了参数,比如之前用默认 malloc 编译到一半,现在想换成MALLOC=libc,或者在切换 gcc 版本之后重新编译,一定要先执行:

make distclean

make distclean会把之前的编译产物全部清掉,强制全量重新编译。不要小看这一步。Makefile 的增量编译逻辑有时候不会重编所有文件,新旧对象文件混在一起,链接阶段就会出现各种诡异错误,比如找不到符号、重复定义、版本不匹配。我踩过一次之后,凡是改动编译参数或换了编译器,都先make distclean再说。

3.4 编译完成后的文件布局建议

二进制已经放到/usr/local/redis-6.0.1/bin,接下来把源码包里的配置文件复制到之前规划好的目录:

cp /usr/local/src/redis-6.0.1/redis.conf /etc/redis/redis.conf cp /usr/local/src/redis-6.0.1/sentinel.conf /etc/redis/sentinel.conf

记住一个原则:源码目录里的东西是“一次性构建产物”,别在里面直接改配置运行。配置归/etc/redis,数据归/var/lib/redis,日志归/var/log/redis,二进制归/usr/local/redis-6.0.1/bin。各司其职,升级时也好处理。

二进制到位了,真正的运行环境也得同步准备。

4. redis.conf 关键参数配置:保姆级不等于什么都默认

4.1 daemonize、supervised 与 systemd 的三角关系

这个部分特别容易踩坑,先说清楚。

很多旧教程会让你把daemonize yes配上,让 Redis 自己后台化。但在用 systemd 管理的生产环境里,我建议把它设为no,再用 systemd 配合supervised systemd来托管进程。为什么?

systemd 的Type=notify模式要求进程启动后主动向 systemd 发送“我准备好了”的通知。如果 Redis 自己先fork到后台,父进程退出,systemd 就很难精确掌握服务的就绪状态,容易出现“systemctl start 返回了,但实际还没有完全就绪”的尴尬局面。

所以我的配置做法是:

daemonize no supervised systemd

这样 Redis 以前台进程方式运行,由 systemd 通过sd_notify机制来感知它的状态,服务启停的反馈非常明确。

4.2 bind、protected-mode 和密码配置

配置文件里的bind和protected-mode必须一起看,不然容易懵。

默认情况下,Redis 只监听127.0.0.1,这是最安全的姿势。如果只有本机应用需要访问,保持默认即可。如果有多台内网服务器要连这台 Redis,就把bind改成内网 IP 和回环地址,比如:

bind 127.0.0.1 192.168.10.20

protected-mode yes的含义是:当 Redis 没有配置密码、且监听地址不是仅限回环地址时,它会拒绝外部连接。如果你既改了 bind 又开了 protected-mode,但忘了设密码,外部客户端会被直接拒绝,这是保护机制在起作用,不是故障。

密码配置上,老版本用requirepass,Redis 6.0.1 虽然支持 ACL,但最简单的方式依然是:

requirepass "你的强密码"

如果你要用 6.0.1 的 ACL,可以单独在配置里定义用户:

user default on nopass ~* +@all user legacy on >你的强密码 ~* +@all

不过 ACL 那套对新手来说入门成本更高,初期用requirepass完全够。还有一种场景需要警惕:如果把 3.x 老项目的客户端直接迁移到 6.0.1,老客户端可能不认识 ACL 相关配置,用requirepass反而是最兼容的方案。

4.3 内存、RDB/AOF 和慢日志设置

maxmemory这个参数我建议一定要显式配置,否则 Redis 可能把整台机器内存吃满。具体值取决于你的业务形态:

  • 纯缓存场景:可以设置物理内存的 70% 左右,配合maxmemory-policy allkeys-lru,内存满了自动淘汰最久未使用的 key;
  • 数据不可丢失的场景:设置一个保守值,并且maxmemory-policy noeviction,内存满了直接拒绝写入,保留数据完整性。

持久化方面,默认的 RDB 快照机制建议保留,同时按需开启 AOF:

appendonly yes appendfsync everysec

appendfsync everysec是性能和安全的平衡点,每秒钟刷一次磁盘,崩溃最多丢一秒数据。不建议用always,写性能下降非常明显。

慢日志也是运维排查问题时的利器:

slowlog-log-slower-than 10000 slowlog-max-len 128

前者表示超过 10 毫秒的命令会被记录,后者表示最多保留多少条慢日志。

另外,如果想让 Redis 更安全,可以重命名危险命令:

rename-command FLUSHALL "" rename-command FLUSHDB "" rename-command CONFIG ""

但注意,这么做的同时,你的监控脚本、备份脚本里如果有调用CONFIG的命令,会直接失效。改之前先确认一下业务脚本里有没有用到这些命令。

配置改完还不能直接裸跑,得让系统来接管它的生命周期。

5. 把 Redis 6.0.1 交给 systemd 托管,以及我实测的坑

5.1 创建 redis 用户与服务目录

Redis 的生产环境管理,我几乎不用 root 直接跑服务,而是创建一个专用账号,最小化权限:

useradd -r -s /sbin/nologin redis

-r表示创建系统账号,-s /sbin/nologin表示禁止登录。然后调整之前建好的目录属主:

chown -R redis:redis /var/lib/redis chown -R redis:redis /var/log/redis chown -R redis:redis /usr/local/redis-6.0.1

最后一项是让 redis 用户能读取二进制目录。如果之后做磁盘扩容或者数据归档,也要保证新目录属主是 redis。

5.2 systemd unit 文件的正确写法和 RuntimeDirectory 报错

CentOS 7.5 内置的 systemd 版本偏老,对较新版本新增的一些指令支持不完整。我在网上看到很多高版本系统的 unit 模板,里面会用RuntimeDirectory=redis让 systemd 在/run下自动创建属主为 redis 的目录。这个写法在 CentOS 7 的 systemd 219 上能不能用,取决于具体系统版本和 systemd 小版本,有时候会直接报Unknown lvalue 'RuntimeDirectory' in section 'Service'。

所以我在 CentOS 7.5 上的做法是:不依赖RuntimeDirectory,手工把需要用的运行目录建好:

mkdir -p /var/run/redis chown redis:redis /var/run/redis

然后在 unit 文件里用/var/run/redis作为运行目录。

我的 unit 文件放在/etc/systemd/system/redis.service,内容如下:

[Unit] Description=Redis 6.0.1 server After=network.target [Service] User=redis Group=redis Type=notify ExecStart=/usr/local/redis-6.0.1/bin/redis-server /etc/redis/redis.conf ExecReload=/bin/kill -s HUP $MAINPID TimeoutStopSec=30 Restart=on-failure RestartSec=5 LimitNOFILE=65535 PrivateTmp=true [Install] WantedBy=multi-user.target

注意这里Type=notify和配置文件里的supervised systemd、daemonize no是配套的。如果redis.conf里你写成了daemonize yes,服务启动时 systemd 很可能报启动超时或者状态判定不准。

写好 unit 后执行:

systemctl daemon-reload systemctl enable redis systemctl start redis systemctl status redis

如果一切正常,你会看到Active: active (running)。

5.3 启动时可能出现的内核参数警告

服务能起来,不代表日志里干干净净。Redis 启动时经常在内核层面给出两类警告,一类是内存 overcommit,一类是 TCP backlog。

vm.overcommit_memory默认值在 CentOS 7 里可能是 0。Redis 做 RDB 快照或者主从全量同步时,需要 fork 子进程,内核的内存分配策略在 overcommit 为 0 的情况下,可能因为“虚拟内存承诺过量”而拒绝 fork,导致后台保存失败。官方建议设置为 1:

echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf sysctl -p

另一条是 TCP backlog,Redis 默认监听队列长度是 511,但如果你系统的net.core.somaxconn只有 128,Redis 的队列长度会被强制压到 128,高并发情况下表现为连接排队甚至丢连接:

echo 'net.core.somaxconn = 1024' >> /etc/sysctl.conf sysctl -p

还有一个隐蔽问题是透明大页 THP。如果 Redis 日志里出现WARNING you have Transparent Huge Pages (THP) support enabled in your kernel,建议临时关闭:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

THP 在 Redis fork 子进程做写时复制时,会显著增加内存页分配延迟,可能造成延迟尖刺。不过这个设置不是持久化的,重启后需要重新执行,所以通常配合 systemd service 片段或 rc.local 来做。

5.4 防火墙与 SELinux 的补刀

服务跑起来了,但你从另一台机器用redis-cli -h 你的IP ping却连不上,第一反应别急着怀疑 Redis 配置,先看防火墙和 SELinux。

CentOS 7.5 默认开着 firewalld,放行端口:

firewall-cmd --permanent --add-port=6379/tcp firewall-cmd --reload

如果防火墙已经放行还是连不上,再看 SELinux 有没有拦。SELinux 默认 enforcing 状态下,自定义的 Redis 监听端口如果没有对应策略,连接会被直接拒绝。可以查一下journalctl -u redis或ausearch -m avc。

解决办法是给端口加上 redis 类型标签:

yum install -y policycoreutils-python semanage port -a -t redis_port_t -p tcp 6379

如果端口已经存在,把-a换成-m修改。不建议为了省事直接setenforce 0,那相当于把整个系统的安全机制关掉了,隐患太大。

服务起来了,不代表就没有暗坑,验证要跟上。

6. 安装后的验证:从 redis-cli ping 到 benchmark 数据

6.1 服务启动与基本验证命令

先确认服务状态:

systemctl status redis

再用客户端做一次最基本的连通性测试:

redis-cli ping

如果你配置了requirepass,直接redis-cli ping会得到NOAUTH Authentication required。正确的做法是用环境变量传密码,避免密码出现在命令行历史里:

export REDISCLI_AUTH="你的强密码" redis-cli ping

输出PONG就说明 Redis 核心服务和认证都没问题。

接着看服务器信息:

redis-cli info server

重点看redis_version是否显示 6.0.1,以及tcp_port是否和配置一致。再看内存和客户端连接:

redis-cli info memory redis-cli info clients

used_memory_rss和used_memory的比值可以初步判断内存碎片情况。如果比值长期偏高,比如超过 1.5,说明内存碎片严重,这时候 jemalloc 的优势才会体现出来。

6.2 redis-benchmark 怎么读结果

安装目录自带redis-benchmark,可以快速给服务做个压力摸底:

/usr/local/redis-6.0.1/bin/redis-benchmark -t set,get,incr,lpush -n 100000 -c 50 -r 1000000

参数含义:-t指定要测的命令集,-n是每个命令的请求总数,-c是并发客户端数,-r表示使用随机 key,避免把所有操作压到同一个 key 上造成误导。

我拿一台 2C4G 的虚拟机跑出来的典型结果大概是:

==== SET ==== 100000 requests completed in 0.83 seconds 50 parallel clients ... Requests per second: 120481.93

get 通常会比 set 高一点,lpush 和 incr 也都在十万级别。如果你看到某个命令的 QPS 明显偏低,先检查是不是 key 冲突严重,或者网络延迟异常,不要立刻怀疑 Redis 性能。另外,benchmark 结果只能作为这台机器上的参考值,不能直接当容量规划依据,生产环境还要结合真实命令分布和键大小来评估。

6.3 日志确认和后续升级的注意点

启动完成后,养成看日志的习惯:

tail -f /var/log/redis/redis.log

正常启动日志应该只包含基础信息,比如版本号、监听地址、配置加载路径。如果看到 5.3 节提到的 warning,顺手把内核参数优化掉。

这里再多说一点升级和回滚的维护习惯。编译安装最怕升级时把旧目录直接覆盖,一旦新版本有问题,你想回滚都找不回原样。正确做法是保留版本目录,比如这次装在/usr/local/redis-6.0.1,之后升级新版本时装到/usr/local/redis-6.0.2,然后通过软链切换:

ln -s /usr/local/redis-6.0.1 /usr/local/redis

unit 文件里的ExecStart写成/usr/local/redis/bin/redis-server。升级时停服务、改软链、启服务,整个过程秒级完成,出问题也能立刻切回旧目录。这个习惯帮我避免过好几次线上事故,值得保留。

安装过程中最值得反思的其实不是命令本身,而是对老系统兼容性的敬畏。千万别为了省事去动系统的 glibc 或全局 gcc,用 SCL 做工具链隔离是最稳妥的方案。我也见过有人编译到一半发现内存不够,改小并发数之后不清理旧对象直接重编,最后被各种莫名其妙的链接错误折磨,白白浪费几个小时。遇到这种情况,先make distclean,再重新编译,基本都能解决。生产服务器上的部署,慢一点没关系,重要的是每一步都可控、可回滚、可解释。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询