夜雨聆风学习资料网

ARTICLE · 1090631

ARM上没有现成安装包:Tendis源码编译全实录,从gcc 14自举到3主3从集群

ARM上没有现成安装包:Tendis源码编译全实录,从gcc 14自举到3主3从集群

Tendis · ARM 架构 · 源码编译

Tendis 源码安装 ARM 版:openEuler 上从 gcc 14 编到 3 主 3 从集群

gcc 14.1.0 自举 · --recursive 拉源码 · 两个编译坑 · ARM 专用 redis-cli · meet/addslots/replicate

上一篇在 X86 上装 Tendis,用的是官方编译好的发布包;到了 ARM 机器(鲲鹏/飞腾等国产化环境)就没这个待遇了——官方没有 ARM 预编译包,只能源码编译。而源码编译的第一道门槛是:Tendisplus 2.7.0 起要求 gcc ≥ 11,很多国产化系统自带的 gcc 达不到标,得先自己把 gcc 编出来,再拿它去编 Tendis。本文在 openEuler 22.03 LTS-SP4(ARM)上完整走一遍:源码自举 gcc 14.1.0 → 递归拉取 Tendis 源码 → 踩掉两个编译坑 → 单实例启动 → 手动编译 redis-cli → 组建 3 主 3 从集群。ARM 路线独有的坑都在里面,照着走能少熬一晚上。

开源说明:Tendis 是腾讯开源项目(GitHub 仓库 Tencent/Tendis),本文编译安装与建集群步骤参照该项目官方开源文档与源码仓库实践整理,向开源社区致谢;各版本编译参数可能有差异,请以所用版本的官方仓库说明为准。

阅读说明:文中服务器 IP 已替换为 10.0.0.19,git 邮箱/用户名为占位符,实操时请换成自己的;演示环境为测试机,弱口令与 root 操作请勿照搬到生产。

本文脉络:环境与依赖 → 源码自举 gcc 14.1.0 → git 递归拉 Tendis → 两个编译坑(git 邮箱 / USE_JEMALLOC)→ 编译与单实例启动 → ARM 编 redis-cli → 6 实例集群 → meet/addslots/replicate 建群 → 八条避坑

01 环境需求与依赖准备

先看官方的编译门槛,这里有个版本分水岭要注意——Tendisplus 2.7.0 前后对 gcc 的要求完全不同:

依赖
版本要求
gcc
≥ 11(tendisplus ≥ 2.7.0;2.7.0 之前用 gcc 5.5.0 编译)
cmake
≥ 3.13.0
git
任意可用版本(拉源码 + 子模块)

本文实测环境三要素:

项目
值
操作系统
openEuler release 22.03 (LTS-SP4),ARM 架构
Tendis 版本
tendisplus-2.8.0-rocksdb-v8.5.3(源码编译)
Gcc 版本
14.1.0(源码自举,见 02 节)

先把编译工具链的基础依赖一把装齐:

[root@localhost ~]#yum install libxslt autoconf automake libtool cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc libatomic -y

版本提示:这一步 yum 装的 gcc 只是" bootstrap 引子"——真正用来编 Tendis 的 gcc 14 是下一步源码编出来的。如果你的系统自带 gcc 已经 ≥ 11 且能正常工作,自举这步可以跳过。

02 源码自举 gcc 14.1.0

拉包 + 下载前置依赖

用清华镜像拉 gcc 14.1.0 源码包,然后跑官方脚本 contrib/download_prerequisites 下载 5 个前置库(gmp / mpfr / mpc / isl / gettext):

[root@localhost ~]#wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-14.1.0/gcc-14.1.0.tar.gz

[root@localhost ~]#tar -zxvf gcc-14.1.0.tar.gz

[root@localhost ~]#cd gcc-14.1.0

[root@localhost gcc-14.1.0]#contrib/download_prerequisites

2026-09-27 15:03:12 URL:http://gcc.gnu.org/pub/gcc/infrastructure/gettext-0.22.tar.gz [26105696/26105696] -> "gettext-0.22.tar.gz" [1]

# ...... gmp-6.2.1 / mpfr-4.1.0 / mpc-1.2.1 / isl-0.24 依次下载 ......

gettext-0.22.tar.gz: OK

gmp-6.2.1.tar.bz2: OK

mpfr-4.1.0.tar.bz2: OK

mpc-1.2.1.tar.gz: OK

isl-0.24.tar.bz2: OK

All prerequisites downloaded successfully.

configure 参数与编译

configure 是这步的关键,几个参数各有用途:--disable-multilib 不编 32 位库(省一半时间)、--disable-shared + -static 静态链接(编出来的 gcc 不依赖系统 so,给后面编 Tendis 用更稳)、--enable-default-pie/ssp 开安全加固:

[root@localhost ~]#chown -R root.root gcc-14.1.0

[root@localhost ~]#cd gcc-14.1.0 && mkdir build && cd build

[root@localhost build]#../configure --prefix=/usr/local/gcc-14.1.0 --enable-languages=c,c++ --disable-shared --disable-multilib --enable-threads=posix --enable-bootstrap --enable-checking=release --enable-default-pie --enable-default-ssp CFLAGS='-fPIC -fPIE -static' CXXFLAGS='-fPIC -fPIE -static'

[root@localhost build]#make -j16

[root@localhost build]#make install

耗时提示:gcc 全量编译是出了名的慢,-j16 并行也要按小时计。这台机器建议放在午饭前后跑,别盯着屏幕干等。

切换到新 gcc:三个环境变量

编完不是结束——必须把 PATH、CC、CXX 三个变量都指向新 gcc,否则后面编 Tendis 时用的还是系统旧 gcc,等着报一屏版本错误:

# 在 /etc/profile 文件末尾追加,然后 source 生效

export PATH=/usr/local/gcc-14.1.0/bin:${PATH}

export CC=/usr/local/gcc-14.1.0/bin/gcc

export CXX=/usr/local/gcc-14.1.0/bin/g++

[root@localhost ~]# source /etc/profile

[root@localhost ~]# gcc --version

gcc (GCC) 14.1.0

03 递归拉取 Tendis 源码:主仓 + 9 个子模块

Tendis 的第三方依赖(rocksdb、jemalloc、asio、glog 等)全部以 git submodule 形式管理,所以克隆必须带 --recursive。这一步的量不小:主仓 85 MiB,最大头 rocksdb 一个子模块就 222 MiB,需要自行解决访问 GitHub 的网络问题:

[root@localhost ~]#git clone https://github.com/Tencent/Tendis.git --recursive

Cloning into 'Tendis'...

remote: Total 24165 (delta 1476), reused 1415 (delta 1407), pack-reused 21787 (from 3)

Receiving objects: 100% (24165/24165), 85.34 MiB | 735.00 KiB/s, done.

Submodule 'src/thirdparty/asio' registered for path 'src/thirdparty/asio'

Submodule 'src/thirdparty/gflag' registered for path 'src/thirdparty/gflag'

Submodule 'src/thirdparty/glog' registered for path 'src/thirdparty/glog'

Submodule 'src/thirdparty/googletest' registered for path 'src/thirdparty/googletest'

Submodule 'src/thirdparty/jemalloc' registered for path 'src/thirdparty/jemalloc'

Submodule 'src/thirdparty/lz4' registered for path 'src/thirdparty/lz4'

Submodule 'src/thirdparty/rapidjson' registered for path 'src/thirdparty/rapidjson'

Submodule 'src/thirdparty/rocksdb/rocksdb' registered for path 'src/thirdparty/rocksdb/rocksdb'

Submodule 'src/thirdparty/snappy' registered for path 'src/thirdparty/snappy'

# ...... 9 个子模块依次克隆,其中 rocksdb 最大 ......

Receiving objects: 100% (138459/138459), 222.54 MiB | 742.00 KiB/s, done.

Submodule path 'src/thirdparty/rocksdb/rocksdb': checked out 'f32521662acf3352397d438b732144c7813bbbec'

子模块克隆失败兜底:--recursive 中途断掉(网络抖动很常见)不用重新 clone 整个仓库,进 Tendis 目录补一刀:

[root@localhost Tendis]#git submodule update --init --recursive

04 两个编译坑:先踩为敬

ARM 源码编译 Tendis 有两个隐蔽坑,报错信息都很有迷惑性,单独拎出来讲。

坑一:不配 git 邮箱用户名,编译报头文件缺失

这是全文最反直觉的一个坑:编译前必须先给 git 配置邮箱和用户名,否则会报一个跟 git 毫不相干的错——rocksdb/tendis_extension.h: No such file or directory。看到这个错十有八九会去折腾 rocksdb 路径,其实根因在编译预处理阶段:

# 先配置 git 邮箱和用户名(换成自己的)

[root@localhost Tendis]#git config --global user.email "yourname@example.com"

[root@localhost Tendis]#git config --global user.name "yourname"

# 如果不配置,编译时会报:

/root/Tendis/src/tendisplus/server/server_params.cpp:23:10: fatal error:rocksdb/tendis_extension.h: No such file or directory

23 | #include "rocksdb/tendis_extension.h"

|          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

compilation terminated.

make[2]: *** [src/tendisplus/server/CMakeFiles/server_params.dir/build.make:76: ...] Error 1

make[1]: *** [CMakeFiles/Makefile2:2511: ...] Error 2

make: *** [Makefile:156: all] Error 2

坑二:jemalloc 补丁打不上,加 USE_JEMALLOC=no

第二个坑在 jemalloc 子模块:Tendis 编译时要给 jemalloc 打补丁,某些版本/环境下补丁匹配不上,报 patch failed: src/prof.c:23。解法是预处理时加参数 USE_JEMALLOC=no,改用系统默认分配器绕过打补丁环节:

error: patch failed: src/prof.c:23

error: src/prof.c: patch does not apply

# 解法:cmake 预处理时追加 USE_JEMALLOC=no

[root@localhost build]#cmake .. USE_JEMALLOC=no

取舍说明:jemalloc 在多线程内存分配上通常优于 glibc malloc,性能敏感场景能绕则不绕;但 ARM 环境下补丁打不上就别硬刚,先让它跑起来,压测确认性能满足业务再谈优化。

05 编译与单实例启动

坑扫完就是标准三连:建 build 目录 → cmake 预处理 → make 编译:

[root@localhost Tendis]#mkdir build && cd build

[root@localhost build]#cmake ..

[root@localhost build]#make -j16

编完先跑一个单实例验证二进制可用。Tendis 目录下自带配置模版 tendisplus.conf,直接用它启动(默认监听 51002):

[root@localhost Tendis]#./build/bin/tendisplus tendisplus.conf

[root@localhost log]#tail -100f tendisplus.WARNING

Log file created at: 2026/09/27 15:08:11

Running on machine: localhost

W0927 15:08:11.534560 493735 index_manager.cpp:342] index manager running...

W0927 15:08:11.534597 493477 server_entry.cpp:850] ready to accept connections at 127.0.0.1:51002 :51002

# 日志目录与端口确认

[root@localhost log]# pwd

/root/Tendis/home/log

[root@localhost log]#netstat -lpn|grep 51002

tcp        0      0 127.0.0.1:51002         0.0.0.0:*               LISTEN      493477/./build/bin/

判断标准:WARNING 日志里出现 ready to accept connections 且 netstat 能看到 51002 LISTEN,二进制就算编译成功。ARM 编译全流程顺利的话约 20~40 分钟(视核数)。

06 ARM 特有坑:自带 redis-cli 用不了

X86 发布包里 bin/ 目录自带一整套客户端工具(redis-cli、redis-benchmark、predixy 等),但源码编译的 ARM 环境里这些预编译二进制是 X86 架构的,直接执行会报架构错误:

[root@localhost bin]# ls

checkdts  compare_instances  memtier_benchmark  predixy  README.md  redis-benchmark  redis-cli  redis-server  redis-sync

[root@localhost bin]# ./redis-cli --help

-bash: ./redis-cli: cannot execute binary file: Exec format error

Exec format error = 架构不匹配。解法很直接:手动编译一份 redis,用它自带的 redis-cli 客户端。redis 5.0.14 编译很快,几分钟搞定:

[root@localhost ~]#tar -xvf redis-5.0.14.tar.gz

[root@localhost ~]#cd redis-5.0.14

[root@localhost redis-5.0.14]#make

拿新编出来的 redis-cli 连 Tendis 单实例,读写一把过:

[root@localhost src]# pwd

/root/redis-5.0.14/src

[root@localhost src]#./redis-cli -h 127.0.0.1 -p 51002

127.0.0.1:51002> keys *

1) "a"

127.0.0.1:51002> set b 123

OK

127.0.0.1:51002> get b

"123"

ARM 环境必记:Tendis 源码树 bin/ 下的所有客户端工具在 ARM 上都是摆设,一律自己编 redis 替代;同样的道理,建集群也不能用 redis-cli 的 --cluster 子命令,07/08 节会展开。

07 集群部署:6 实例配置与拉起

目录规划与配置文件

单机伪集群 6 实例(3000~3005):3000 直接用默认模版配置跑,3001~3005 各拷一份配置、各用一套目录。目录一把创建:

[root@localhost Tendis]#mkdir -p home{1..5}/db

[root@localhost Tendis]#cp tendisplus.conf tendisplus.conf_3001

[root@localhost Tendis]#cp tendisplus.conf tendisplus.conf_3002

# ...... 3003 / 3004 / 3005 同理 ......

六份配置结构完全一样,只有端口和 home 目录编号不同。3000(默认模版)与 3001 对比着看:

# tendisplus.conf —— 3000 实例(默认目录 ./home)

cluster-enabled yes

port 3000

bind 10.0.0.19

daemon on

loglevel notice

logdir ./home/log

dumpdir ./home/dump

dir ./home/db

pidfile ./home/tendisplus.pid

slowlog ./home/log/slowlog

rocks.blockcachemb 4096

executorThreadNum 48

# tendisplus.conf_3001 —— 仅 port 和 home 编号不同(3002~3005 同构)

cluster-enabled yes

port 3001

bind 10.0.0.19

daemon on

loglevel notice

logdir ./home1/log

dumpdir ./home1/dump

dir ./home1/db

pidfile ./home1/tendisplus.pid

slowlog ./home1/log/slowlog

rocks.blockcachemb 4096

executorThreadNum 48

两个参数提醒:bind 必须写本机对外 IP(写 127.0.0.1 集群总线握手会出问题);executorThreadNum 48 按机器核数调整,测试机可以改小。

拉起 6 实例并确认监听

[root@localhost Tendis]#./build/bin/tendisplus tendisplus.conf

[root@localhost Tendis]#./build/bin/tendisplus tendisplus.conf_3001

# ...... conf_3002 ~ conf_3005 依次启动 ......

[root@localhost Tendis]#ps -ef|grep tendis

root  557225  1  4 15:12 ?  00:01:01 ./build/bin/tendisplus tendisplus.conf

root  557281  1  0 15:12 ?  00:00:11 ./build/bin/tendisplus tendisplus.conf_3001

root  557322  1  4 15:12 ?  00:01:02 ./build/bin/tendisplus tendisplus.conf_3002

root  557378  1  0 15:12 ?  00:00:11 ./build/bin/tendisplus tendisplus.conf_3003

root  557449  1  4 15:12 ?  00:01:00 ./build/bin/tendisplus tendisplus.conf_3004

root  557504  1  0 15:12 ?  00:00:11 ./build/bin/tendisplus tendisplus.conf_3005

[root@localhost Tendis]#netstat -lpn|grep 300

tcp  0  0 10.0.0.19:3000  0.0.0.0:*  LISTEN  557225/./build/bin/

tcp  0  0 10.0.0.19:3001  0.0.0.0:*  LISTEN  557281/./build/bin/

tcp  0  0 10.0.0.19:3002  0.0.0.0:*  LISTEN  557322/./build/bin/

tcp  0  0 10.0.0.19:3003  0.0.0.0:*  LISTEN  557378/./build/bin/

tcp  0  0 10.0.0.19:3004  0.0.0.0:*  LISTEN  557449/./build/bin/

tcp  0  0 10.0.0.19:3005  0.0.0.0:*  LISTEN  557504/./build/bin/

# 另有 13000~13005 监听 —— 每实例还会开 port+10000 的集群总线端口

08 组建集群:meet → addslots → replicate

先说不能走的路:--cluster create

Redis 原生的一键建群命令在 Tendis 上不能用,会报节点非空错误——Tendis 不支持原生 redis-cli 的 --cluster 子命令,老老实实手动三步走:

[root@localhost src]#./redis-cli --cluster create 10.0.0.19:3000 10.0.0.19:3001 10.0.0.19:3002 10.0.0.19:3003 10.0.0.19:3004 10.0.0.19:3005 --cluster-replicas 1

[ERR] Node 10.0.0.19:3000 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

第一步:cluster meet 握手

在 3000 上把其余 5 个节点逐个 meet 进来:

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3000

10.0.0.19:3000> cluster meet 10.0.0.19 3001

OK

10.0.0.19:3000> cluster meet 10.0.0.19 3002

OK

10.0.0.19:3000> cluster meet 10.0.0.19 3003

OK

10.0.0.19:3000> cluster meet 10.0.0.19 3004

OK

10.0.0.19:3000> cluster meet 10.0.0.19 3005

OK

第二步:addslots 分槽(三段给三个节点)

和 Redis 一样,Tendis 把数据映射到 16384 个 slot,节点只有分到槽才能响应相关命令,否则返回 MOVE 错误。16384 个槽平均分给 3 个主节点,用 cluster addslots + bash 花括号展开:

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3000 cluster addslots {0..5461}

OK

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3002 cluster addslots {5462..10922}

OK

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3004 cluster addslots {10923..16383}

OK

# 等待若干秒,cluster info 确认集群状态

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3000 cluster info

cluster_state:ok

cluster_slots_assigned:16384

cluster_slots_ok:16384

cluster_slots_pfail:0

cluster_slots_fail:0

cluster_known_nodes:6

cluster_size:3

cluster_current_epoch:5

cluster_my_epoch:1

# ...... 消息统计略 ......

判断标准:cluster_state:ok、cluster_slots_assigned:16384、cluster_size:3 三个值齐了,3 主分槽才算完成。注意分配方案是 3000/3002/3004 当主(偶数端口),3001/3003/3005 留作从库。

第三步:replicate 挂从库

先从 cluster nodes 拿到 3 个主节点的 node id,再到从库节点上逐个 cluster replicate。node id 是 40 位十六进制串,直接从输出里复制:

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3000 cluster nodes

f10091eeec94edeb9eb1ce0829ce75b8444a1f19 10.0.0.19:3005@13005 master - 0 1790493300000 0 connected

6b8f37bdebc282993590cd1e23d351b6419eb242 10.0.0.19:3004@13004 master - 0 1790493302000 4 connected 10923-16383

37635ad0d847b010fb273832b2eb9ce0ffad549b 10.0.0.19:3003@13003 master - 0 1790493302832 3 connected

95a6a6c7a9389489efb126ffa36bb5a79c8570ea 10.0.0.19:3002@13002 master - 0 1790493301831 5 connected 5462-10922

8b24860e8a32ff44a4e0ad434e4848c284bbd4d2 10.0.0.19:3001@13001 master - 0 1790493300830 2 connected

4bd32c6e9adf85f0c586429b1670dd3e5cfb9d14 10.0.0.19:3000@13000 myself,master - 0 1790493301000 1 connected 0-5461

# 3001 挂到 3000(主),3003 挂到 3002(主),3005 挂到 3004(主)

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3001 cluster replicate 4bd32c6e9adf85f0c586429b1670dd3e5cfb9d14

OK

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3003 cluster replicate 95a6a6c7a9389489efb126ffa36bb5a79c8570ea

OK

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3005 cluster replicate 6b8f37bdebc282993590cd1e23d351b6419eb242

OK

验证:主从拓扑 + 读写重定向

再刷一次 cluster nodes,三个从库已经带着自己 master 的 node id 出现(第三列从 master 变成 slave + 主节点 id)。三主三从拓扑成型:

[root@localhost src]#./redis-cli -h 10.0.0.19 -p 3000 cluster nodes

f10091eeec94edeb9eb1ce0829ce75b8444a1f19 10.0.0.19:3005@13005 slave 6b8f37bdebc282993590cd1e23d351b6419eb242 0 1790493380916 4 connected

6b8f37bdebc282993590cd1e23d351b6419eb242 10.0.0.19:3004@13004 master - 0 1790493381917 4 connected 10923-16383

37635ad0d847b010fb273832b2eb9ce0ffad549b 10.0.0.19:3003@13003 slave 95a6a6c7a9389489efb126ffa36bb5a79c8570ea 0 1790493380000 5 connected

95a6a6c7a9389489efb126ffa36bb5a79c8570ea 10.0.0.19:3002@13002 master - 0 1790493380000 5 connected 5462-10922

8b24860e8a32ff44a4e0ad434e4848c284bbd4d2 10.0.0.19:3001@13001 slave 4bd32c6e9adf85f0c586429b1670dd3e5cfb9d14 0 1790493380000 2 connected

4bd32c6e9adf85f0c586429b1670dd3e5cfb9d14 10.0.0.19:3000@13000 myself,master - 0 1790493380000 1 connected 0-5461

最后用 -c(集群模式)写一个 key,看它怎么按槽位重定向——key abc 算出的槽 7638 落在 3002 区间,客户端自动跳过去:

[root@localhost src]#./redis-cli -c -h 10.0.0.19 -p 3001

10.0.0.19:3001> set abc 123

-> Redirected to slot [7638] located at 10.0.0.19:3002

OK

10.0.0.19:3002> get abc

"123"

ARM 版建集群至此闭环:gcc 14 自举 → Tendis 源码编译 → 单实例 51002 验证 → 自编 redis-cli → 6 实例 meet/addslots/replicate → -c 读写重定向成功。后续 failover、扩缩容与 X86 版一致,可参考同系列上一篇。

09 八条避坑清单(ARM 源码版专属)

#
坑
解法 / 要点
1
gcc 版本分水岭
tendisplus ≥ 2.7.0 要求 gcc ≥ 11;2.7.0 之前用 gcc 5.5.0。先确认系统 gcc,不够就按 02 节自举
2
编 Tendis 用错了 gcc
/etc/profile 里 PATH、CC、CXX 三个变量都要指向新 gcc,source 后用 gcc --version 复核
3
git clone 断在子模块
不用整仓重拉,进 Tendis 目录 git submodule update --init --recursive 补齐
4
报 tendis_extension.h 缺失
根因是没配 git 邮箱用户名(不是 rocksdb 路径问题),先 git config --global 再编
5
jemalloc 补丁失败
patch failed: src/prof.c:23 → cmake 时加 USE_JEMALLOC=no,性能敏感场景先跑通再调优
6
自带 redis-cli 报 Exec format error
bin/ 下客户端全是 X86 预编译,ARM 上一律自己编 redis 用其 redis-cli
7
--cluster create 报 not empty
Tendis 不支持原生 --cluster 建群,手动 cluster meet × 5 → addslots 三段 → replicate × 3
8
建群后端口检查只看一半
每实例监听两个端口:服务端口(3000~3005)+ 集群总线端口(port+10000,即 13000~13005),缺一组都建不成群

一句话收尾:ARM 上装 Tendis,难的不是 Tendis 本身,而是工具链——gcc 版本、X86 二进制、原生建群命令这三样都不能照搬 X86 的惯性操作。把 04/06/08 三节的坑绕开,剩下的就是按部就班。

写在最后

国产化替代的机器越来越多,ARM 路线的源码编译会是常态操作。这篇把 gcc 自举、两个编译坑、ARM 专属 redis-cli 问题一次踩平,配上 X86 那篇的运维实战,Tendis 的两套部署路线就都齐了。如果对你有用,欢迎点赞、在看、转发三连。

本文为开源项目(Tencent/Tendis)实践整理,命令与输出基于实测环境记录,版本差异请以官方仓库最新文档为准。文中 IP、邮箱均为脱敏占位。Tendis 及相关商标归其权利人所有。

相关学习资料