最近有个任务是把一部分服务的内存缓存往底层迁移,正好重新走了一遍 tendis环境部署。tendis这个名字有些同学可能还不太熟,一句话解释就是:兼容Redis协议、基于rocksdb引擎的分布式存储系统,它最大的价值是让数据从昂贵的内存里解放出来,落到磁盘上,同时业务侧几乎不需要改代码。这篇文章我打算把从零开始部署tendis的完整过程拆开讲,包括原理认知、环境选型、配置文件解析、启动验证、集群扩展,以及我实际踩过的高频坑,适合运维、后端开发以及对存储成本敏感的同学参考。
1. 部署前先把原理和应用场景搞清楚
1.1 tendis到底能解决什么问题
很多第一次接触tendis的人,第一反应是:这不就是一个Redis吗?为什么要额外部署一套?
这个理解不算错,但恰恰忽略了tendis真正的位置。传统Redis所有数据都放在内存里,内存成本高、容量受限。当单机数据量到了几十GB甚至上百GB,Redis要么做大量分片,要么淘汰冷数据,无论哪种都让人头疼。tendis的思路是让数据落到磁盘,但对外仍然使用Redis协议。频繁访问的热数据可以通过rocksdb的缓存机制留在内存,偶发读取的数据直接从磁盘返回,这样容量可以从内存级别扩展到TB级别,成本却比同等容量的内存方案低一个数量级。
说直白点,tendis适合的场景就是“Redis放不下、MySQL又太重”的中间地带。比如用户画像缓存、订单快照、推荐结果集这类数据量很大、但单个value不复杂的场景。我实际接触过的案例里,很多团队就是因为内存成本翻倍增长,才认真评估这类磁盘兼容方案的。另外,tendis的数据直接持久化在rocksdb上,重启之后不用再依赖刷盘时机,故障恢复时少了很多提心吊胆。
1.2 环境选型的三个关键判断
部署之前,我建议先用三个问题给自己定位,避免一上来就照抄别人的配置。
第一,数据规模是多少。如果单节点数据预计超过20GB,用tendis是合理的;如果只有几百MB,直接用Redis,别为了技术新鲜感增加一套系统。
第二,QPS形态是怎样的。tendis的设计目标不是跟Redis拼峰值,而是承接大容量的低热数据。如果你所有请求都是高频热查询,延迟和吞吐还是Redis更强;如果大部分请求命中率很低,tendis的性价比就上来了。判断方法也不复杂,看现有Redis的key访问分布,如果占比前20%的key承担了80%以上的访问量,这就是典型的冷热分层场景。
第三,是否要用集群。tendis支持类似Redis Cluster的槽位分配模式,但单机部署和多节点部署在配置上差异不小。我见过很多团队一上来就搭集群,结果单节点的rocksdb参数还没调明白,集群排障时一头雾水。先单机跑通,再上集群,节奏会稳很多。
硬件层面也说一下。CPU要求不高,控制逻辑本身不复杂,多核主要是为了并发处理IO。内存主要被rocksdb的block cache和write buffer占用,一般建议按数据量的5%到15%预留,具体要看配置里给rocksdb分配多少。磁盘是大头,SSD是基本要求,机械盘几乎跑不动compaction时的随机IO,这个钱不能省。
1.3 版本与安装包的选择思路
tendis的部署方式大体分两种:直接用编译好的release包,或者拉源码自己编译。
我的建议是:能拿到release包就别自己编译。源码编译看着灵活,但实际会遇到gcc版本、cmake版本、依赖库路径的连环问题,光环境排查就能耗掉大半天。release包下载解压就能用,适合快速验证;生产环境如果团队对安全合规有要求,再考虑在内部编译一套,流程也是一样的。
版本选择上不要盲目追新,重点看团队的兼容性基线。比如你的服务端长期跑在某个内核版本上,就选一个在该平台上被验证过的稳定版本。部署这种事情,稳定压倒一切。另外,README和CHANGELOG一定要看,有些版本对配置文件格式做了调整,直接拿旧配置启动新版本,会出现报错或者静默降级,到时候排查起来非常痛苦。
2. 配置文件核心参数拆解
2.1 基础参数:端口、线程与目录
tendis的启动不复杂,麻烦都在配置上。第一次部署,先把最基础的几个参数设对:
port 9100 bind 127.0.0.1 thread-num 8 dir ./data logadir ./log loglevel infoport是服务端口,bind是监听地址。如果只是本机验证,设127.0.0.1就够了;生产环境需要业务机访问时,再绑内网IP,不要为了省事直接绑0.0.0.0,暴露面会明显变大。
thread-num是工作线程数。连接多、小value多的场景可以多开几个,但不是越多越好,线程太多反而会增加上下文切换损耗。一般先按CPU核数的1到2倍设置,跑一段时间观察负载再微调。这里有一个容易忽略的点:thread-num调整需要重启进程,所以上线前尽量压测到位,避免频繁重启。
dir是rocksdb的数据目录,所有落盘数据、WAL、manifest都在这下面;logadir是运行日志目录。这两个目录必须提前创建,并确保运行用户有写权限。我踩过一个坑:目录没建,启动日志里报了个权限相关错误,人还在那折腾别的配置,后来才发现只是路径不存在。
2.2 rocksdb参数:容量和写入表现的关键
tendis的性能手感,很大程度由rocksdb的几个内存参数决定。新手最容易犯的错,是把所有空闲内存都塞给block cache,以为缓存越大越快,结果系统自己内存不够用。
先看几个常用参数:
rocksdb.blockcachedSize 4096 rocksdb.writeBufferSize 256 rocksdb.maxWriteBufferNumber 4 rocksdb.targetFileSizeBase 64blockcachedSize单位是MB,作为读缓存。对读多写少的场景,适当调大能明显降低磁盘IO。但请注意,这个值和操作系统的页缓存是重叠的,调得太大,内存反而会被反复回收,效果适得其反。我给过一个参考范围:总内存的10%到20%,再高性价比就会下降。
writeBufferSize可以理解为memtable的大小,单位同样是MB。写入操作先落到这里,满了再刷成磁盘上的SST文件。maxWriteBufferNumber则是内存中允许积压多少个memtable。写入压力大的场景,适当加大这两个值能降低flush频率,代价是内存占用变高。这里有个细节:如果memtable积压数量长期接近上限,说明写入速度跟不上,光调参数治标不治本,要考虑扩容或拆分key。
targetFileSizeBase决定SST文件的目标大小。文件越小,compaction越频繁,写放大更明显;文件越大,单次合并代价越高。建议先用默认值起步,观察compaction时间和写放大指标后,再一点点调整,一次只改一个参数。
用生活类比来理解:block cache是家里的茶杯柜,常用杯子放近处,取用快;writeBuffer是厨房水槽的蓄水池,满了才往下水管倒一次。容量和速度是跷跷板,没有固定最优值,必须结合你的数据和访问模式来定。
2.3 集群模式要提前留意的配置项
如果打算后面上集群,单机部署时就要给某些参数留好位子,不然后面改模式需要重启进程,影响面会很大。
需要提前关注的有节点角色标记、集群开关、心跳超时等。比如单机模式下别开启cluster开关,否则节点启动后会额外做一些集群相关初始化,日志也会多出一堆干扰项。相关配置项名称不同版本存在差异,具体以当前版本官方配置模板为准,配置文件里的注释通常写得比较清楚。
我的经验是,先按单机模式把服务起来,数据链路验证通过后,再单独规划集群。一来排障范围小,二来你能在单机上先熟悉rocksdb的行为,集群出了问题也知道从哪下手。很多人一上来就想要集群最终形态,结果被各种交互逻辑绕晕,反而耽误时间。
3. 从编译到启动的完整实操
3.1 获取release包与编译环境准备
假设你已经拿到release包,下面是快速上手的路径:
tar xzf tendis-release.tar.gz cd tendis ls bin/bin目录下应该有tendis主程序。如果拿不到release包,需要源码编译,环境至少要满足:Linux x86_64系统、gcc 4.8.5以上、cmake 3.1以上。编译命令大致是:
cd tendis mkdir -p build && cd build cmake .. make -j$(nproc)cmake过程会自动检查依赖,缺什么补什么。这一步最耗时间的其实是等待,建议用make -j让多核并行。编译完成后,二进制通常会生成在build/bin目录下。源码编译的唯一好处是能自定义某些编译选项,日常部署用默认就够。过程中报错,先把报错里出现的第一条missing路径解决掉,比翻整个日志高效。
有一点要提醒:编译机器的CPU架构要和运行机器一致,否则二进制拷过去可能直接无法执行或者数据校验出错。跨版本升级时,最好保留旧二进制,新版本验证通过再切换,不要顺手删掉。
3.2 一份可用的配置文件长什么样
下面是我常用的一份单机配置,参数做了删减,保留核心部分:
port 9100 bind 127.0.0.1 thread-num 8 dir ./data logadir ./log loglevel info daemonize no rocksdb.blockcachedSize 4096 rocksdb.writeBufferSize 256 rocksdb.maxWriteBufferNumber 4 rocksdb.targetFileSizeBase 64daemonize设成no的好处是前台启动能直接看日志,确认启动成功后再改成yes,或者配合nohup放后台。dir和logadir最好使用绝对路径,免得脚本里切来切去导致数据目录漂移。
这份配置不是万能模板,单机功能验证已经完全够用。当你要设置集群或调整容量时,再按需打开对应开关即可。注意,配置文件里的#注释行不要随便删,很多参数的说明都写在注释里,删掉之后下一个人接手会很难受。
3.3 启动、日志观测与功能验证
启动命令很简单:
./bin/tendis ./conf/tendis.conf第一次启动,重点看日志。正常启动后,日志里会出现端口监听、rocksdb打开完成的记录,类似“ready to accept connections”的标识。如果卡住不动,大概率是rocksdb在恢复历史数据,耐心等一会儿;如果直接退出,优先检查目录、端口和权限。
服务起来后,用Redis生态的工具直接验证:
redis-cli -p 9100 ping redis-cli -p 9100 set user:10001 "hello" redis-cli -p 9100 get user:10001tendis的设计目标就是兼容Redis命令,原生redis-cli可以直接连。验证完简单读写,再执行info看存储状态,重点看rocksdb相关统计项,比如当前SST文件数、磁盘占用大小。
这一步确认没问题,单机部署就算走通了。接下来可以做个简单的容量测试,写入一批数据,观察磁盘使用曲线,同时对比延迟变化。测试数据的key分布要尽量均匀,否则LSM结构容易因为热点key出现性能波动,这不是tendis的问题,是存储引擎本身的特性。压测的时候建议用16字节以上的随机key,避免所有key落在一个前缀区间里。
4. 从单机走向多节点:集群部署要点
4.1 集群规划:主从数量与槽位分配
单机跑通后,如果数据量继续涨,就该认真规划集群了。
tendis集群模式在思路上沿用了Redis Cluster的槽位概念,数据按key哈希分布到16384个slot上。常规规划是主从配对,主节点承担读写,从节点用于容灾和只读扩展。节点数少的集群,至少3主3从;节点资源紧张,可以先3主1从观察,但生产环境我不建议让从节点数量少于主节点的一半。
槽位分配可以手动指定,也可以通过工具自动完成。自动分配省事,但如果你希望某些大key集中在特定节点上,手动调整更可控。槽位迁移是集群操作里风险最高的动作,迁移期间如果出现大量写入,可能让节点负载迅速打满。大促前的黄金法则是:不主动做迁移。
4.2 搭建集群的具体步骤
部署多节点时,本质上是把单机流程复制多份,再让节点之间互相通信。
先让每个节点以集群模式启动,确保配置里开启集群相关开关,设置好节点超时时间和每个节点的监听端口。然后使用Cluster协议里的节点加入操作,把各节点互相加入。节点加入后,分配或迁移slot,让每个主节点承担一部分槽位。
具体命令在不同版本里可能有差异,但大体流程是:启动所有节点、建立节点关系、分配槽位、验证。每一步都要用集群状态命令确认当前进度,不要跳步。我见过最典型的翻车案例是:三个节点都起来了,但因为节点加入顺序不对,有节点一直处于等待状态,日志里又不显示明确错误,最后逐条检查节点关系才发现漏了一步。
集群模式下还有一个容易忽略的点:从节点的迁移或者重建需要占用大量磁盘IO。如果你的机器是共享存储,多个节点同时做全量同步有可能把磁盘带宽打满,进而影响主节点的读写,所以同步窗口要错开。
4.3 集群运维中容易被忽略的点
第一,节点时间要同步。集群依赖节点之间的心跳和超时判断,时间偏差一旦过大,很容易误判主节点失联,触发无谓的故障切换。
第二,槽位迁移的窗口。尽量安排在业务低峰期,并且迁移前确认目标节点磁盘余量足够。rocksdb在写入新数据时本来就有额外空间消耗,迁移过程会成倍放大这个需求。
第三,从节点的角色确认。主从关系建立后,一定要观察从节点是否正常追平主节点数据。很多人只看主节点状态正常就以为万事大吉,结果从节点数据停在几天前,真到切换的时候才发现对不上。建议日常巡检里加一条主从复制偏移量的检查项,数值持续增大说明堆积,需要及时处理。
5. 常见问题排查与运维速查
5.1 部署阶段高频问题排查表
下面按我实际遇到的频率整理:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动后立即退出 | 配置文件路径不对 | 确认加载的是预期conf,检查日志路径 |
| 日志报Permission denied | 目录未创建或属主不对 | 检查dir和logadir权限,chown/chmod |
| 报Address already in use | 端口被占用 | lsof或ss查看端口,换端口或清理旧进程 |
| 启动卡住不监听 | rocksdb正在恢复 | 等待,同时看磁盘IO是否繁忙 |
| redis-cli连接超时 | bind地址或防火墙 | 确认bind是否为可访问IP,检查防火墙策略 |
| 读写正常但延迟偶发变高 | compaction在跑 | 观察IO曲线,评估是否调整SST参数 |
这张表覆盖了90%的一次性部署问题。真正花时间的,往往是日志级别太低导致输出不明确,这时候先把loglevel调到debug重启一次,能省大量排查时间,生产环境上线前再调回info。
5.2 容量监控与压缩策略
tendis的磁盘使用不是线性的。除了数据和索引,rocksdb还有WAL、manifest文件和compaction临时空间,实际占用通常比纯数据多不少。
建议监控曲线至少包含:数据总量、SST文件数、磁盘余量、compaction进行状态。可以用定时脚本捞info里的统计字段,也可以接入已有的监控体系。关键是设好磁盘水位告警,在部署阶段就把预留空间留足,不要等到磁盘报警再临时清理。
如果发现compaction频繁拖慢写入,可以适当调大targetFileSizeBase,减少合并次数,代价是单次合并更久。这种取舍没有标准答案,只能在你自己的IO负载上测出来。另外也留意是否有超大key存在,一个几百MB的value在compaction时会显著拉长耗时,这种情况需要从业务侧拆分key。
5.3 备份恢复的常规做法
备份其实可以沿用Redis的思路。因为协议兼容,直接用redis-cli导出数据,或者做目录级别的冷备份,都可以。
在线备份要注意一致性问题。最稳妥的方式是给节点做一次全量快照,尽量选在写入低频期;如果业务不允许停写,可以考虑从节点快照,流量继续走主节点,备份完成后再做校验。直接在主节点做快照会消耗磁盘IO,对线上延迟有影响,能避开就避开。
恢复时,准备一个全新的数据目录,导入备份后再启动节点。注意恢复后要跑一遍较完整的校验读取,尤其是每种数据结构都要抽查,避免因为版本差异导致个别命令不兼容。我见过数据能启动但个别字段读不出来的情况,十有八九是跨版本恢复没提前验证。
6. 部署后沉淀下来的几点体会
tendis环境部署,并不像装个数据库客户端那样无脑下一步,但它也没有难到需要专职内核工程师才能搞定。整个链条里,真正容易翻车的往往不是程序本身,而是部署前的方案判断和部署后的参数微调。
我个人的建议是:先用release包把单机流程跑通,用真实数据验证读写和持久化,确认你的场景确实适配了,再逐步加集群节点,最后再补上监控和备份。每一步的证据都留好,比如info输出、监控截图、测试报告,后续排障会顺很多。
我自己的习惯是,每次部署完都会把当时的系统版本、内核参数、rocksdb配置、启动日志里的关键行存一份快照。一两个月后如果出现诡异问题,翻这份快照比重新回忆要可靠得多。这算是最土的办法,但也是我近期屡试不爽的办法。如果你手头正好要做tendis环境部署,希望以上内容能帮你少走几段弯路。