在 CentOS 7.5 / RHEL 7.x 上编译安装 Redis 6.0.1乍一听就是经典的configure make make install三连击实际走一遍才发现坑全藏在一行行报错里jemalloc 头文件找不到、GCC 版本太老、systemd 接管后进程 PID 对不上、内存参数告警……这篇保姆级教程把我踩过的和预判到的坑全部提前拆开按真实部署顺序把每一步的命令、参数背后的原因、排错思路都讲透适合刚上手 Linux 服务器的新人也适合正在把业务 Redis 从 3.x 往 6.x 升级的运维和开发同学。1. 为什么非要用编译安装的方式部署 Redis 6.0.11.1 系统自带的 Redis 实在太老了CentOS 7.5 默认软件源里的 Redis 版本停留在 3.2.xRHEL 7.x 的情况基本一致。这个版本能用但确实有些落后了没有 ACL 权限控制没有CLIENT TRACKING客户端缓存也没有 RESP3 协议更别说 6.0 主打的 IO 多线程能力。业务一旦需要精细控制某个账号只能读不能写或者想把热点数据往应用端缓存推一层老版本就卡脖子了。有人说那我不升级自己写一层缓存壳子这种方案往往比升级 Redis 本身更复杂而且分布式锁、Lua 脚本这些基础能力在老版本上也存在一些边界行为差异。与其绕来绕去不如直接把版本升到 6.0.1功能到位后续维护也省心。1.2 三种获取新版本的方式为什么选了编译想拿到新版 Redis常见路线有三条替换系统 Yum 仓库、下载官方二进制包、直接用源码编译。替换第三方仓库的方式最省事但风险在于它会引入一套别人维护的 RPM 依赖你不知道这些包会动到系统里的哪些底层组件生产环境里我一般不赌这个。官方二进制包的问题是它面向通用 Linux 发布对 CentOS 7 的 glibc 版本和目录约定兼容性虽然没问题但二进制包通常不会帮你处理好 systemd 集成、用户隔离、目录规划这些落地细节还是得自己补一遍。编译安装的“成本”看起来高一点实际上是最可控的产物全部落在独立目录比如/usr/local/redis-6.0.1不污染系统版本切换通过软链实现回滚就是改一下链接指向可以按实际场景定制编译参数也能预先排除 malloc 相关隐患整个编译过程都在自己眼皮底下出问题查起来有理有据这套做法对 CentOS 7.5 和 RHEL 7.x 完全通用RHEL 用户只需要提前把订阅源配好剩下的步骤一模一样。1.3 编译前最容易被忽略的环境准备准备阶段我通常分成三块基础编译工具、解析库、以及目录规划。基础编译工具必须要有 gcc 和 makeCentOS 最小化安装通常没有直接两条命令补上yum install -y gcc make wget tarRedis 的redis-cli依赖 readline 库缺失时会提示安装 readline-devel所以干脆一起装上yum install -y readline-devel另外强烈建议装一个 tcl。make test需要tclsh来执行测试脚本虽然跳过也能完成安装但生产环境我建议跑一遍能提前发现平台兼容性问题。目录部分后面实操章节详细说这里只需要记住一个原则把软件、配置、数据、日志四类文件分开后面排障时能省下大量时间。2. 编译 Redis 6.0.1 必须搞懂的三个技术点2.1 GCC 4.8.5 到底能不能编译 6.0.1CentOS 7 默认的 GCC 版本是 4.8.5这是个老编译器但我可以明确说编译 Redis 6.0.1 是完全可行的官方在 C 代码层面并没有刻意引入很新的语法特性。真正会跳出来的问题是某些情况下编译器报“内部错误”或者莫名其妙的汇编指令错误这种情况通常发生在个别老 CPU 配合老编译器时。所以我的建议是先直接用系统 GCC 试一次遇到编译中段报错再考虑升级编译套件。不要一上来就折腾工具链白白增加复杂度。如果确实需要新版 GCC用 Software CollectionsSCL最省事比手动编一个 GCC 出来不知道快到哪里去了yum install -y centos-release-scl yum install -y devtoolset-8 scl enable devtoolset-8 bash gcc --version进入这个 bash 环境后生成的进程都会继承新版 GCC 的环境变量之后的make会自动使用更新版本的编译器。切换只对当前会话有效不会把系统全局的 GCC 换掉安全干净。2.2 内存分配器jemalloc 和 libc 之争这是新手编译 Redis 时遇到概率最高的报错之一报错信息长这样fatal error: jemalloc/jemalloc.h: No such file or directory意思很直白Redis 默认想让进程绑定 jemalloc 内存分配器但系统里没有对应的开发头文件。两种解法效果差异值得展开说说。第一种解法是安装 jemalloc 的开发包让编译顺利通过yum install -y jemalloc-devel第二种解法是绕过 jemalloc明确告诉编译脚本改用操作系统自带的内存分配器make MALLOClibc为什么 Redis 这么执着于 jemalloc因为 Redis 的 key-value 结构里充斥着大量小对象内存申请频繁的 malloc/free 会在进程内存里留下许多碎片。jemalloc 专为这类高并发小内存分配场景设计内部按大小分区管理碎片率明显低于 glibc 的默认分配器。用生活化的比喻来说glibc 的 malloc 像随手把东西往房间各处一扔时间长了整个屋子都是空隙jemalloc 则是有分区的收纳大师每个格子都按尺寸归位。长时间跑生产业务、频繁写入删除的场景下jemalloc 的稳定性优势会被时间放大得非常明显。所以我给生产环境的建议永远是优先装 jemalloc-devel让 Redis 用上 jemalloc。只有在内网离线环境实在搞不到 jemalloc 相关 RPM 包时才退而求其次用MALLOClibc并且后续要额外关注碎片率指标。2.3 make 和 make install 背后隐藏的参数玄机很多人直接一条make就完事了这样也对但在服务器上我更建议这样执行make -j$(nproc) make install PREFIX/usr/local/redis-6.0.1make -j后面的数字代表并行编译任务数$(nproc)会自动读取 CPU 核数。注意如果服务器内存只有 512MB 或 1GB并行编译很容易触发 OOM表现为cc1: out of memory。这种情况下老老实实写make -j1或者先加一点 swap 再编译。PREFIX参数决定了安装目录。默认不写的话二进制会被装进/usr/local/bin也能用但对于需要多版本共存、随时回滚的场景明确的版本目录要好得多。指定目录之后make install会在/usr/local/redis-6.0.1/bin下生成 redis-server、redis-cli、redis-benchmark、redis-check-aof、redis-check-rdb、redis-sentinel 这几个可执行文件后续再用软链把它们接到/usr/local/bin下对外暴露升级时直接换软链目标方向即可。这里还要提一个容易被忽略的make distclean指令。编译到一半报错、你修改了配置或环境变量之后再次编译旧的编译缓存可能残留导致问题被缓存污染出现奇怪的链接错误。根治手段就是先执行make distclean清掉全部中间产物再重新编译。3. 保姆级实操一步一步编译安装 Redis 6.0.13.1 先规划目录再建独立账号很多教程上来就让下载源码我习惯先做两件事规划目录和创建专用用户。这样既符合最小权限原则也方便后面 systemd 托管。mkdir -p /usr/local/redis-6.0.1 mkdir -p /etc/redis mkdir -p /var/lib/redis mkdir -p /var/log/redis mkdir -p /var/run/redis useradd -r -s /sbin/nologin redis目录各自承担不同责任/etc/redis放配置文件/var/lib/redis放 RDB 快照和 AOF 追加文件/var/log/redis放日志/var/run/redis放 PID 文件。千万别图省事把所有东西塞到源码目录里否则以后升级时手一抖把数据目录删了哭都来不及。useradd -r表示创建系统账号-s /sbin/nologin禁止这个账号交互式登录之后让 Redis 以这个低权限用户运行即使某个入口被攻破攻击者能拿到的权限也十分有限。3.2 下载源码并校验完整性源码包我习惯放到/usr/local/src下正式下载前先确认手里拿到的包是完整的、没被篡改过的。Redis 官方在发布页面会同步给出 SHA256 校验值下载后手动算一下再解压。cd /usr/local/src wget https://download.redis.io/releases/redis-6.0.1.tar.gz sha256sum redis-6.0.1.tar.gz tar xzf redis-6.0.1.tar.gz cd redis-6.0.1再强调一次6.0.1 是 2020 年的版本后续受 CVE 影响已经推出了很多安全修复版。如果这篇文章是你第一次接触 Redis 编译我的真实建议是操作流程完全照着本文走但最终在线上运行的版本请务必换成官方安全建议的 6.0.x 最新补丁版本或者直接选择更高的大版本。思维方法是一样的只是版本号替换一下。3.3 编译、安装与软链进入源码目录后先看一眼 README 确认没有特殊要求然后开始编译make -j$(nproc)如果你这一路真遇到了jemalloc/jemalloc.h的报错别慌先执行yum install -y jemalloc-devel make distclean make -j$(nproc)如果离线环境装不了才考虑make distclean make MALLOClibc -j$(nproc)编译完成后强烈建议跑一遍官方测试集虽然耗时比较长但能提前暴露平台兼容性问题make test确认无误后执行安装并建立软链make install PREFIX/usr/local/redis-6.0.1 ln -sf /usr/local/redis-6.0.1/bin/redis-server /usr/local/bin/redis-server ln -sf /usr/local/redis-6.0.1/bin/redis-cli /usr/local/bin/redis-cli ln -sf /usr/local/redis-6.0.1/bin/redis-benchmark /usr/local/bin/redis-benchmark验证版本redis-server --version输出里能看到v6.0.1 sha...说明编译安装链路已经通了。3.4 生产环境 redis.conf 关键参数配置思路源码目录自带一份redis.conf这是官方默认模板直接用它起服务没问题但离生产要求还有距离。我习惯把配置拷贝到/etc/redis/6379.conf再逐项调整cp redis.conf /etc/redis/6379.conf下面这份是经过多轮生产验证的配置骨架每一项都尽量写清楚“为什么选这个值”bind 0.0.0.0 protected-mode yes port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no supervised auto pidfile /var/run/redis/6379.pid loglevel notice logfile /var/log/redis/6379.log databases 16 dir /var/lib/redis save 900 1 save 300 10 save 60 10000 appendonly yes appendfilename appendonly.aof appendfsync everysec maxmemory 4gb maxmemory-policy allkeys-lru requirepass your-strong-password几个要特别解释的点bind 0.0.0.0意味着所有网卡都能访问通常用于内网服务。如果这台机器有公网暴露面打死我都不会这么设而是绑定具体内网 IP。protected-mode yes是 Redis 6.0 默认就开启的保护机制在没有密码、没有 bind 白名单时拒绝外部访问即使你 bind 了0.0.0.0只要没设密码非本机连接会被直接拒绝。所以看清楚它不是万能钥匙密码还是要设。daemonize no配合 systemd 的 Typesimple 模式最稳这个在下一节展开。save与appendonly是持久化双保险。RDB 按时间窗口落盘AOF 每次 fsync 策略选everysec兼顾性能和数据安全。appendfsync always是最安全的但每次写都刷盘性能下降明显一般业务everysec足够。maxmemory 4gb要按机器实际内存调别拍脑袋。maxmemory-policy allkeys-lru表示内存满了之后优先淘汰最久没用的 key适合做缓存的场景如果 Redis 里存的是不能丢的业务数据这个策略就要慎重了。requirepass那一行就是我实践里踩过坑的地方。早期我总以为内网环境不用设密码直到有次内网扫描工具把所有开放 6379 的机器全扫了一遍。不要在安全策略上赌运气即使内网也要设强密码。3.5 systemd 托管Typesimple 比 Typeforking 省心老教程清一色让你daemonize yes然后用redis-server /etc/redis/6379.conf启动配合 init.d 脚本管理。这种方式其实留下了很多隐患其中最常见的 bug 是systemd 用 Typeforking 等待 Redis 创建 PID 文件时两个组件对“就绪”的理解不一致时不时会出现服务启动后又被 systemd 判定为启动超时或 PID 漂移的问题。我现在的标准做法是配置文件里daemonize no让 redis-server 前台运行systemd 直接用 Typesimple 管理。这个模式下 systemd 能直接跟踪到主进程停止和重启都非常干净。写一个 unit 文件[Unit] DescriptionRedis 6.0.1 Server Afternetwork.target [Service] Userredis Groupredis Typesimple ExecStart/usr/local/redis-6.0.1/bin/redis-server /etc/redis/6379.conf ExecStop/usr/local/redis-6.0.1/bin/redis-cli -p 6379 -a your-strong-password shutdown Restarton-failure LimitNOFILE65535 PrivateTmpyes NoNewPrivilegestrue ProtectSystemfull ReadWriteDirectories/var/lib/redis [Install] WantedBymulti-user.target把文件放到/etc/systemd/system/redis.service然后chown -R redis:redis /var/lib/redis /var/log/redis /var/run/redis systemctl daemon-reload systemctl enable --now redis systemctl status redisExecStop为什么用redis-cli shutdown而不是默默 kill 进程因为 shutdown 命令会触发 Redis 优雅保存数据并关闭所有客户端连接最大程度避免 AOF 或 RDB 写出时被打断导致文件损坏。注意它的写法里带了-a密码参数实际操作你会发现不使用密码时这里还要考虑鉴权方式所以配置文件里的密码必须和 unit 里的参数保持一致。LimitNOFILE65535是经常被忽略的高并发关键项。默认ulimit是 1024这个值对 Redis 这种高连接数服务来说太局促了用 systemd 的LimitNOFILE能彻底绕过登录会话的 ulimit 限制。4. 常见问题与排查技巧实录4.1 编译期的报错速查表这一路上最容易撞见的报错基本就下面这些我把原因和解决方案整理成一个表方便你遇到问题直接照方抓药。报错信息根本原因解决方案fatal error: jemalloc/jemalloc.h: No such file or directory缺少 jemalloc 开发头文件yum install -y jemalloc-devel后make distclean makegcc: command not found系统未安装 gccyum install -y gcc makecc1: out of memory小内存机器并行编译导致 OOMmake -j1或先加 swapmake: tclsh: Command not found执行 make test 缺少 tclyum install -y tclPlease install readlineredis-cli 缺少 readline 库yum install -y readline-devel编译到一半异常中断老编译器在某些优化选项下崩溃用 devtoolset-8 切换新 GCC 后重新编译这里面 “编译到一半异常中断” 最容易让人抓狂因为报错可能是杂乱无章的比如某个.c文件里说 “internal compiler error”。这时候不要急着改源码先想想是不是编译器太老。切换到 devtoolset-8 之后绝大多数这类问题都会消失。还有一次让我记忆深刻的经历某次连续编译三遍都失败后来才发现是源码目录里残留了上一次编译缓存执行make distclean后一次性通过。所以任何修改环境变量或编译器版本之后都别省这一步。4.2 启动日志里那几条 WARNING 的解法Redis 首次启动时日志里经常会看到几条 WARNING这不是启动失败但它是在明确告诉你系统内核参数需要针对 Redis 场景调优。第一条是关于 overcommit_memory 的WARNING: overcommit_memory is set to 0! Background save may fail under low memory condition.Redis 做 RDB 持久化的时候要 fork 子进程如果内核内存 overcommit 策略设为 0在内存已经用了不少的情况下 fork 内存申请可能被内核拒绝导致持久化失败。解决方案是让内核不要在这种时刻拒绝申请sysctl -w vm.overcommit_memory1 echo vm.overcommit_memory1 /etc/sysctl.conf第二条是关于 TCP backlog 的WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is lower than 511.高并发场景下连接排队长度被内核参数压得很低瞬时大流量进来会直接丢弃连接sysctl -w net.core.somaxconn1024 echo net.core.somaxconn1024 /etc/sysctl.conf第三条是关于 Transparent Huge Pages透明大页WARNING: you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis.THP 是内核为了减少页表开销搞的机制但它会引入明显的延迟毛刺对 Redis 这种毫秒级响应要求的服务非常不友好。关闭它echo never /sys/kernel/mm/transparent_hugepage/enabled写到/etc/rc.local里时别忘给可执行权限否则重启之后优化又被系统恢复原样。4.3 启动确认与性能基准测试配置好这一切之后用systemctl status redis确认服务是 running 状态然后跑几个基础验证命令redis-cli -p 6379 -a your-strong-password ping redis-cli -p 6379 -a your-strong-password info memory redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000ping返回 PONG 说明服务正常info memory重点关注used_memory_human和mem_fragmentation_ratio。redis-benchmark是官方自带的基准测试工具-c 50表示 50 个并发连接-n 100000表示总共发 10 万条请求。跑完后你会看到每秒请求数轻松大几万甚至十万以上。不用太纠结这个数字不同机器差异很大更重要的是记录下来作为基线以后调优或排查性能问题时可以做前后对比。4.4 6.0.1 的 IO 多线程到底要不要开Redis 6.0 最大的卖点就是 IO 多线程但很多人有个误区以为装上 6.0.1 就自动享受多线程红利。实际上 Redis 的默认配置里io-threads 1也就是多线程根本没启用。想开启的话在配置文件里设置io-threads 4 io-threads-do-reads yes先讲讲原理Redis 主线程依旧负责执行命令和执行 Lua 脚本IO 线程只负责网络读写的数据收发把收发命令数据的工作分给多个线程并行处理。它能提升的是“网络吞吐量”而不是单个命令的执行延迟。什么时候值得开你的业务大多是大批量、大体积的请求或者客户端连接数非常多网络包停顿明显这时候多线程可以帮助提升整体吞吐。如果业务全是小命令、小包主线程本身处理起来绰绰有余开了多线程反而是负担还可能引入锁竞争。另外注意io-threads的值不要超过机器物理核数。设置完之后重启 Redis用redis-cli info stats查看instantaneous_ops_per_sec等指标确认吞吐量是否有实质变化没有明显收益就调回默认值多线程不是玄学是老老实实的性能取舍。5. 最后分享几点真实体会这套编译安装流程我在多个内网环境反复用过最大的感触是别跟报错硬刚。遇到编译失败先停下来对照报错想一下它缺的到底是依赖还是工具链版本百分之八十的问题集中在 jemalloc 和 GCC 这两件事上。systemd 托管那一步我一开始也迷信老教程的 daemonize yes 配合 Typeforking结果被 PID 文件的问题折腾过一次后来全面改成 Typesimple 配合前台运行再也没有出现过启动超时或者停不干净的情况。还有一个不少人都栽过的坑执行make install之后就把源码目录删了结果后面排查问题时发现有些辅助脚本、默认配置模板都躺在源码目录里真到需要的时候只能重新下载解压。建议源码包留着不是什么大事但能让你在排查问题时少折腾一轮。升级版本的时候也别慌目录规划的意义就在这里把新版本编译到/usr/local/redis-6.0.x改一下软链把配置文件拷贝过去校验一遍关键参数最后重启服务。数据目录和日志目录不动正常情况下几秒钟就能完成版本切换。万一新版本表现不佳软链指回去再重启一次就回滚了整个过程对业务的影响非常小。我的建议是把今天这套流程完整跑通一遍之后重点盯一下日志里的 WARNING 和info memory的碎片率这两个地方能提前暴露很多潜在问题。编译安装只是第一步把实例稳定地接入监控和日常运维体系才是生产环境真正要下功夫的地方。