
简介这份面向大数据存储与管理课程学习者的Redis实验报告文档对应南邮实验一适合正在接触内存数据库、键值存储与集群部署的本科或初学读者用于完成从单机环境到集群搭建再到简易文章投票网站后端实现的完整实验链路。文档共1个doc文件压缩包约933KB体量轻便便于直接查阅和整理。内容围绕Ubuntu 16.04环境展开涵盖Redis单机安装与配置、六节点主从集群组织、PyCharm下结合rediscluster库实现发帖投票与排序展示并保留实验目的、环境、原理内容、结果分析与小结等报告结构给出客户端连接验证与常见报错处理思路。已有324人学习下载可作为实验预习、报告撰写、关键命令复盘与排错参考帮助读者理解Redis在大数据场景中的缓存与键值存储用法。1. 一份能直接跑通的 Redis 实验包值不值得照着做“redis 大数据”这个词在课程群里出现频率极高但落到手上多数人拿到的是一份截图比命令多的实验报告——装完了不会改配置集群拉起来了不知道 16384 个槽是怎么分的投票后端跑通了也说不出为什么用有序集合存分数。这份资源不一样它把 Ubuntu 下的单机 Redis 安装、六节点三主三从集群、以及一个基于 Redis in Action 1.3 节思路的文章投票后端串成一条线每个环节都留了真实命令和真实报错记录。适合三类人要交实验但不想只抄截图的学生、第一次把 Redis 集群从零拉起来的后端新手、需要一份最小可跑的业务 demo 来验证 key 设计的工程师。它不讲数据结构语法讲的是“装完能用、集群能连、业务能跑”这条链路上的每一步实际形态。2. 单机先跑通APT 源安装与 redis.conf 里真正要改的五个参数单机这一步在实验报告里通常只有五行命令但恰恰是后面所有故障的源头。我见过太多情况是集群搭不起来回头一查是单机阶段的bind没改图形客户端连不上回头一查是protected-mode还开着。所以这一章不急着敲命令先把“为什么要这么装、每个参数动了会怎样”讲清楚再落到可复制的脚本上。整个链路按“加源 → 装包 → 改配置 → 重启 → 三处验证”走每一步都有明确的失败信号。2.1 走官方源而不是系统源keyring 与 apt update 的顺序Ubuntu 自带的redis-server包版本往往偏旧集群子命令的行为和新版有差异所以实验里走的是 Redis 官方 APT 仓库。这个仓库的接入分三步顺序不能乱乱一步就报NO_PUBKEY或者404。# 1. 装依赖lsb-release 用来取发行版代号curl 拉 keygpg 做格式转换 sudo apt install -y lsb-release curl gpg # 2. 下载官方签名公钥dearmor 转成 apt 能识别的二进制 keyring curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg # 3. 写入源列表$(lsb_release -cs) 会展开成 xenial / bionic / focal 这类代号 echo deb [signed-by/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main | sudo tee /etc/apt/sources.list.d/redis.list # 4. 刷新索引并安装 sudo apt-get update sudo apt-get install -y redis逻辑上第 2 步的gpg --dearmor是把 ASCII armored 的公钥转成二进制格式apt只认后者第 3 步的signed-by是把这个 keyring 绑定到这一条源上而不是全局信任属于更克制的做法。参数-fsSL四个字母分别是静默失败、跟随重定向、不输出进度、跟随跳转后仍按原地址校验少一个都可能在某些网络环境下静默拿到空文件。这里有个容易被忽略的点lsb_release -cs返回的代号必须真的存在于官方源的dists/目录下。老发行版的代号如果已经不在索引里apt-get update会直接抛 404而不是报一个友好的“不支持”。遇到这种情况常见做法是退回系统自带源安装功能能跑通但集群相关子命令的参数支持度会打折后面--cluster create的行为要以redis-cli --version的实际输出为准不要照抄教程。装完之后先确认服务状态Ubuntu 上/etc/init.d/redis-server start是 sysv 兼容脚本底层仍然走 systemd两条路都能起来但重启和看日志建议统一用 systemctl否则日志会分散在两处不好排查。# sysv 兼容方式启动实验里的写法 sudo /etc/init.d/redis-server start # 更推荐systemd 方式能直接看状态和日志 sudo systemctl enable --now redis-server sudo systemctl status redis-server --no-pager2.2 redis.conf 里必须动的五项以及动了之后的代价配置文件默认在/etc/redis/redis.conf。这个文件有上千行真正影响“能不能从外部连上、能不能被集群复用”的只有五项其余绝大多数保持默认即可。下面这张表是我按实际影响面排的顺序参数默认值实验建议值为什么动它动了之后的代价bind127.0.0.1 ::10.0.0.0或虚拟机内网 IP默认只监听回环外部客户端连不进来暴露到网络上必须配合防火墙或密码protected-modeyesno无密码且非回环监听时Redis 会拒绝外部连接同上等于放弃一层兜底daemonizenoyes前台运行会占住终端集群多节点无法并行启动进程异常退出时终端看不到报错只能看日志dir.//var/lib/redis或自定义数据目录相对路径会跟随启动目录变化重启后数据“消失”目录权限必须给 redis 用户可写logfile/var/log/redis/redis-server.log空值表示输出到 stdoutdaemonize 后就丢了日志文件需要滚动策略否则长期跑会涨改法有两种vim手工改看得清上下文sed批量改适合写进脚本重复执行。实验环境里我一般先用sed打底再grep复核一遍防止某行被注释掉导致替换不生效。CONF/etc/redis/redis.conf # 只替换未被注释的行^ 锚定行首可以避开注释行 sudo sed -i s/^bind 127.0.0.1 ::1/bind 0.0.0.0/ $CONF sudo sed -i s/^protected-mode yes/protected-mode no/ $CONF sudo sed -i s/^daemonize no/daemonize yes/ $CONF # 这两项默认可能带引号或被注释用 grep 先看实际形态再决定 grep -nE ^(# *)?(dir|logfile) $CONF # 改完必须复核确认没有残留 grep -nE ^(bind|protected-mode|daemonize) $CONF^锚定行首这个细节值得多说一句配置文件里大量出现# bind 127.0.0.1、# protected-mode yes这类注释示例不带锚定会把注释行一起改掉表面上没影响实际上你改的是注释真实配置纹丝不动重启后一切照旧。这是排查“改了没用”时第一个要看的点。改完重启然后立刻做三件事本机 ping、跨机 ping、端口监听确认。三件事对应三个不同的失败层次缺一个就会在后面浪费半小时。sudo systemctl restart redis-server # 1. 本机回环验证进程本身活着 redis-cli ping # 2. 用虚拟机实际 IP验证 bind 是否生效IP 用 ifconfig 或 ip addr 查 redis-cli -h 192.168.x.x -p 6379 ping # 3. 看监听地址0.0.0.0:6379 说明 bind 生效127.0.0.1:6379 说明没改上 ss -lntp | grep 63792.3 图形客户端连不上先分清是网络层还是协议层图形客户端实验里用的是 Another Redis Desktop Manager 这类工具第一次连接失败报错通常是ECONNREFUSED含义很明确TCP 层就没握上手。这时候不要急着去关安全模式先按下面顺序排查能省掉大量试错。第一步看客户端填的地址。在虚拟机里跑服务、在宿主机上开客户端客户端里填127.0.0.1指的是宿主机自己当然连不上必须换成虚拟机的实际 IP。实验报告里记录的正是这个坑网上给的“关掉 protected-mode”方案没解决问题因为病灶根本不在那一层。第二步看虚拟机网络模式。NAT 模式下宿主机访问虚拟机的 IP 是通的但虚拟机的 IP 可能每次重启都变最稳的做法是在虚拟机里配静态 IP或者用 host-only 网络固定网段。第三步才是看bind和protected-mode。这两个改完没重启等于没改systemctl restart之后再ss -lntp确认监听地址比看配置文件可靠得多。第四步如果前三步都通了还报错看服务端日志tail -f /var/log/redis/redis-server.log拒绝连接的原因会明确写出来比如“DENIED Redis is running in protected mode”。日志比任何猜测都准。连接成功之后建议在客户端里手工执行一次SET test:key hello再GET确认读写都通而不是只看连接状态是绿色的就以为完事。这一步验证的是权限和持久化路径很多dir配错的情况就是在这里暴露的——写能成功重启后数据没了。3. 三主三从六份配置只差三行cluster create 的节点顺序别弄反单机跑通之后进入集群。这一步的实验报告往往只给了六条启动命令和一条cluster create中间“六份配置文件到底哪里不一样”被省略了而这恰恰是后面Slot already busy、cluster support disabled这类报错的总根源。我的做法是先手写一份完整可用的模板再用循环生成其余五份最后统一启动、统一建集群、统一验收。3.1 一份节点配置模板逐项说清为什么以 6379 这一份为例模板内容如下六份文件只有端口号不同# 对外端口集群里每个节点必须唯一 port 6379 # 集群节点之间要互相通信不能只监听回环 bind 0.0.0.0 # 后台运行否则六个节点要开六个终端 daemonize yes # pid 文件按端口区分不能共用 pidfile /opt/redis/cluster/redis-6379.pid # 日志按端口区分方便单节点排错 logfile /opt/redis/cluster/log/redis_6379.log # 数据目录按端口区分这一项最容易被忽略 dir /opt/redis/cluster/data/6379 dbfilename dump.rdb # 开启集群模式这一行不写后面建集群必报 disabled cluster-enabled yes # 节点自身维护的集群状态文件由 Redis 自动生成和改写 cluster-config-file nodes-6379.conf # 节点心跳超时内网实验环境 15 秒足够 cluster-node-timeout 15000 # 关闭保护模式集群节点间通信不走密码 protected-mode nodir这一项是踩坑重灾区。六份配置如果都写成默认的./或者同一个绝对路径六个节点会往同一个dump.rdb和同一个nodes-*.conf上写表现是集群能建起来但重启之后节点身份错乱或者干脆起不来报“配置文件被占用”。按端口分目录是成本最低的规避方式。cluster-config-file也有讲究。这个文件由 Redis 自己维护内容是当前节点看到的集群拓扑包括节点 ID、槽位归属、主从关系。它不能手工编辑更不能在多个节点之间复制。手工复制会导致多个节点共享同一个节点 ID建集群时报“节点 ID 冲突”或者直接卡在Waiting for the cluster to join。3.2 批量生成与批量启动手写六份容易抄错端口用sed从模板生成更稳。关键是替换范围要覆盖port、pidfile、logfile、dir、cluster-config-file这五处它们都以端口号作为区分标识。BASE/opt/redis/cluster mkdir -p $BASE/log $BASE/data # 把模板放在 $BASE/redis-template.conf然后循环生成 for port in 6379 6380 6381 6389 6390 6391; do mkdir -p $BASE/data/$port # 全局把模板里的 6379 换成目标端口模板中只在这五处出现 6379 sed s/6379/$port/g $BASE/redis-template.conf $BASE/redis-$port.conf done # 复核每份文件的端口、目录、日志路径都应该不同 grep -H -E ^(port|dir|logfile|cluster-config-file) $BASE/redis-*.conf全局替换的前提是模板里除这五处之外不能再出现6379字样包括注释。所以模板里的注释不要写具体端口号写“PORT”之类的占位符更安全。这一点属于血泪经验注释里残留的端口号被替换掉配置本身不会错但你后面grep排查时会看到相互矛盾的信息白白多绕一圈。启动六个节点然后确认进程数正好是六多一个少一个都要停下来查for port in 6379 6380 6381 6389 6390 6391; do /usr/bin/redis-server $BASE/redis-$port.conf done # 期望输出正好 6 行不含 grep 自身多出来说明有残留进程 ps -ef | grep -c [r]edis-server # 逐个确认端口在监听且监听地址是 0.0.0.0 ss -lntp | grep -E 6379|6380|6381|6389|6390|6391grep -c [r]edis-server这个写法里的方括号是技巧让 grep 匹配redis-server但自身命令行里不出现纯字符串redis-server从而不会把自己算进计数。不加这个括号计数永远多 1新手很容易以为多起了一个节点。3.3 cluster create 的节点顺序决定主从分配六个节点都起来之后执行建集群命令。--cluster-replicas 1表示每个主节点配一个从节点六个节点正好三主三从。/usr/bin/redis-cli --cluster create \ 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 \ 127.0.0.1:6389 127.0.0.1:6390 127.0.0.1:6391 \ --cluster-replicas 1这条命令的节点顺序有实际含义前三个作为主节点候选后三个作为从节点候选Redis 会按这个顺序做配对。执行后会打印一份分配方案并要求输入yes确认确认前务必看清楚——如果六个节点里已经有残留的集群状态方案可能和预期不符这时候应该中断去清理状态而不是硬着头皮确认。建完之后做三项验收三项都过才算集群真的可用# 1. 集群整体状态cluster_state 必须是 ok槽位必须 16384 全覆盖 redis-cli -c -p 6379 cluster info # 2. 拓扑结构应该看到 3 个 master、3 个 slave且 slave 各自挂在某个 master 下 redis-cli -c -p 6379 cluster nodes # 3. 跨节点读写写一个 key看它是否被重定向到正确的槽 redis-cli -c -p 6379 set demo:key v1 redis-cli -c -p 6379 get demo:key-c参数是集群模式的关键它让客户端在收到MOVED重定向时自动跳到目标节点重试。不加-ckey 不在当前节点负责的槽位上就会直接返回MOVED 866 127.0.0.1:6380看起来像报错其实只是没开重定向。这个差异在写代码的时候更明显后面第 4 章会专门讲客户端侧的对应处理。cluster info里另一个要看的指标是cluster_known_nodes是否为 6。如果是 3说明只发现了主节点从节点还没完成握手通常等几秒会恢复如果一直是 3去查对应节点的日志多数是端口没通或者配置文件里cluster-enabled没生效。4. 文章投票后端用 hash、zset、set 三类结构把业务拆开业务部分是整个实验里最有价值的一段因为它把“Redis 的几种数据结构分别解决什么问题”落到了具体场景上。文章投票站的核心需求有四条发布文章、给文章投票、按时间排序、按分组归类并按评分排序。这四条需求对应四种完全不同的访问模式用同一种结构硬做会非常别扭拆开之后就顺了。4.1 数据模型五个 key 各管一件事key 形态类型存放内容对应需求articlestring文章自增 ID 计数器发布时生成唯一编号article:idhashtitle、link、poster、time、votes文章详情按字段读写timezsetmember 是文章 keyscore 是发布时间戳按时间倒序取最新文章scorezsetmember 是文章 keyscore 是当前得分按评分排序、分页voted:articleset投过票的用户名防止重复投票group:nameset该分组下的文章 key分组归类这套模型的关键判断是为什么时间和分数要用两个 zset 而不是一个。因为排序维度不同时间只写一次、永不更新分数每次投票都要ZINCRBY混在一个 zset 里就没法同时按两个维度取 Top N。分开存代价是多一次写换来的是两种排序都能做到 O(log N)。为什么防重复投票用 set 而不是在 hash 里存一个计数。计数只能告诉你“有几票”不能告诉你“谁投过”。用 set 的SADD返回值判断是否首次投票天然幂等这个模式在实际业务里非常常用。4.2 post、vote、score 三个函数的实现与参数含义下面这段是可直接运行的核心逻辑单机版连接参数按实际环境改。import time import redis # decode_responsesTrue 让返回值是 str 而不是 bytes省掉到处 decode rc redis.Redis(host192.168.x.x, port6379, db0, decode_responsesTrue) ONE_WEEK 7 * 24 * 3600 # 投票有效期秒 def post(article_data, rrc): 发布文章生成 ID、写 hash、入时间索引、给自己投初始票 # INCR 是原子自增多客户端并发发布也不会拿到重复 ID article_id str(r.incr(article)) article article: article_id article_data[time] time.time() article_data[votes] 1 # 作者自己的一票 # mapping 关键字参数要求 redis-py 3.5老版本用 hmset(article, article_data) r.hset(article, mappingarticle_data) # 时间索引score 用时间戳后续按时间倒序直接 ZREVRANGE r.zadd(time, {article: article_data[time]}) # 分数索引先占位避免 ZSCORE 返回 None 导致后续比较报错 r.zadd(score, {article: 1}) # 把自己记入已投票集合防止作者重复投 r.sadd(voted: article, article_data[poster]) return article def vote(article, user, rrc): 投票先校验有效期再用 SADD 返回值做幂等判断 # 取发布时间None 说明文章不存在 post_time r.zscore(time, article) if post_time is None: return False # 超过一周的文章不再接受投票 if post_time time.time() - ONE_WEEK: return False # SADD 返回 1 表示首次加入0 表示已存在天然防重 if r.sadd(voted: article, user) 1: r.hincrby(article, votes, 1) r.zincrby(score, 1, article) return True return False def score(article, rrc): 取单篇文章当前得分 return r.zscore(score, article)逻辑上有三个地方值得展开。第一处是INCR生成 ID这是 Redis 做分布式 ID 生成器的最小用法原子性由单线程模型保证不需要额外加锁。第二处是sadd返回值做幂等这个判断比“先查再写”可靠因为查和写之间有空窗期并发下会重复计票。第三处是先zadd(score, {article: 1})占位而不是等第一次投票再创建——如果作者初始票走的是sadd分支之外的另一条路zincrby也能从 0 开始建但显式占位能让zscore永远有值省掉一堆None判断。参数上ONE_WEEK是业务规则改成ONE_DAY就能做日榜hincrby的第二个参数必须是整数增量写字符串会直接报类型错误zincrby的分数可以是浮点如果要做加权评分比如票数除以时间衰减把1换成算出来的权重即可。4.3 分组归类与按评分取 Top N分组用 set因为分组不需要顺序只需要“在不在这个组里”SADD和SISMEMBER都是 O(1)。def add_to_group(group, article, rrc): # 幂等重复添加同一篇文章不会产生重复成员 return r.sadd(group: group, article) def top_in_group(group, n10, rrc): 取分组内评分最高的 n 篇做法是取交集后按分数排序 # ZINTERSTORE 把 group 的 set 和全局 score 的 zset 求交结果落到临时 key tmp tmp:group: group r.zinterstore(tmp, [group: group, score], aggregateMAX) result r.zrevrange(tmp, 0, n - 1, withscoresTrue) r.delete(tmp) # 临时 key 用完即删避免污染键空间 return resultzinterstore是这套模型里最关键的一条命令含义是“把多个集合的交集存成一个新的有序集合”其中 set 成员的默认分数按 1 处理zset 成员保留原分数aggregateMAX表示同一成员在多个输入里出现时取最大分数。这样得到的结果就是“这个分组里所有文章的分数排名”。要注意tmp必须删。临时 key 如果忘了删随着分组变多键空间会被撑大而且下次同名zinterstore会覆盖表面上不出错实际上多了一次无谓的写。另一个容易忽略的点zinterstore要求输入的所有 key 在同一个槽位上单机模式无所谓集群模式下不满足就会报CROSSSLOT Keys in request dont hash to the same slot。这一点在第 4.4 节展开。4.4 换到集群模式key 的落点决定代码能不能跑单机跑通之后把连接换成集群客户端代码不是完全零改动。集群版客户端有两种主流的# 方案一redis-py-cluster实验里用的包名 redis-py-cluster导入名 rediscluster from rediscluster import RedisCluster startup_nodes [{host: 192.168.x.x, port: 6379}] rc_cluster RedisCluster(startup_nodesstartup_nodes, decode_responsesTrue) # 方案二redis-py 自带的集群客户端新版更推荐导入路径以实际安装版本为准 from redis.cluster import RedisCluster as RedisClusterNew rc_cluster RedisClusterNew(host192.168.x.x, port6379, decode_responsesTrue)两种客户端都会自动处理MOVED和ASK重定向startup_nodes只需要给集群里任意一个可达节点客户端会自己拉CLUSTER SLOTS拿到完整拓扑。这里有个实际影响只给一个种子节点是够的但如果这个节点挂了客户端启动时会失败所以生产上一般把六个节点都写进startup_nodes。真正需要改代码的是跨 key 操作。集群把 16384 个槽分散到三个主节点上zinterstore涉及group:name、score、tmp三个 key它们大概率不在同一个槽直接报CROSSSLOT。解决办法是用 hash tag把花括号里的内容作为哈希依据保证同一组 key 落在同一槽# 用 {group:name} 作为 tag三个 key 都会按 group:name 计算槽位 def top_in_group_cluster(group, n10, rrc_cluster): tag {group:%s} % group group_key tag :articles score_key tag :score # 注意这已经是分组内的分数副本不是全局 score tmp tag :tmp r.zinterstore(tmp, [group_key, score_key], aggregateMAX) result r.zrevrange(tmp, 0, n - 1, withscoresTrue) r.delete(tmp) return result这段改动的代价要说清楚为了让 key 同槽分组内的分数必须单独维护一份{group:x}:score也就意味着一次投票要同时更新全局score和分组内的分数副本写放大了一倍。实验规模下这个代价可以接受但如果业务上分组很多、投票很频繁更划算的做法是放弃服务端求交改成客户端SMEMBERS拿文章列表、再用ZSCORE或 pipeline 批量取分、本地排序。数据量在几千篇以内客户端排序的开销远小于写放大。这个取舍是整章最值得记的一点集群模式下凡是涉及多 key 原子操作的命令都要先问一句“这些 key 会不会同槽”答案是否定的时候要么用 hash tag 改写 key 设计要么把计算搬到客户端没有第三条路。5. 避坑四类报错背后的真实病灶实验报告里记了四条问题我按“症状最像、原因差得最远”的原则重新排了序并补上了实际排查时该看的位置。这几条几乎覆盖了从单机到集群的全过程遇到同类报错先对照这张表比盲目搜要快得多。5.1 客户端报 ECONNREFUSED改了 bind 也没用现象图形客户端连接时报RedisClient On Error: Error: connect ECONNREFUSED 127.0.0.1:6379按网上说法关掉protected-mode后仍然一样。原因报错里的127.0.0.1是客户端侧填的地址指的是客户端所在机器而不是虚拟机的地址。服务端bind改成0.0.0.0只是让服务监听所有网卡和客户端填什么地址是两件事。解决先把地址改成虚拟机的实际 IPip addr或ifconfig查再用ss -lntp | grep 6379确认服务端监听的是0.0.0.0而不是127.0.0.1最后用redis-cli -h 虚拟机IP ping在宿主机侧验证一次。三步都过再开图形客户端。这个顺序的意义是每一层失败都有不同的报错形态混在一起改会互相掩盖。5.2 建集群报 cluster support disabled现象执行cluster nodes或--cluster create时报(error) ERR This instance has cluster support disabled。原因cluster-enabled默认是no配置文件里通常写成# cluster-enabled yes的注释形态看起来“有这一行”实际没生效。用模板生成六份配置时如果漏了这一项六个节点全都是单机模式。解决确认每一份端口配置里都有未被注释的cluster-enabled yes改完必须重启该节点不是 reload重启后再redis-cli -p port info cluster看cluster_enabled:1。这一步要在建集群之前逐个节点验一遍六份都过再执行--cluster create能省掉一次全量返工。5.3 建集群报 Slot 已经占用现象--cluster create执行到一半报[ERR] Node 127.0.0.1:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.或者是(error) ERR Slot 671 is already busy。原因这个节点上残留了上一次建集群的状态。残留可能在两处nodes-port.conf记录的旧拓扑以及dump.rdb里的旧数据。上一次建集群失败后没清理就重来最容易撞上。解决登录每个节点执行FLUSHALL清数据、CLUSTER RESET HARD清集群状态然后停掉进程、删掉nodes-port.conf和dump.rdb再重新启动。注意CLUSTER RESET要在节点处于集群模式时执行如果节点还没开集群模式直接删文件更干脆。# 逐个节点清理端口按实际改 for port in 6379 6380 6381 6389 6390 6391; do redis-cli -p $port flushall redis-cli -p $port cluster reset hard done # 停进程后删残留文件再重启 pkill -f redis-server.*redis-6 rm -f /opt/redis/cluster/data/*/nodes-*.conf rm -f /opt/redis/cluster/data/*/dump.rdbcluster reset hard比soft更彻底soft只清槽位和节点表保留节点 ID 和 epochhard连节点 ID 一起重置相当于变回一个全新的空节点。实验场景下用hard省心。5.4 图形客户端连集群报 stream on error 或 cluster is ended现象单机连得好好的切到集群模式后客户端报stream on error: connection is close、cluster is ended之类。原因图形客户端不一定实现了集群重定向协议直连某个节点端口后一旦该节点返回MOVED客户端不知道该怎么跳连接就断了。也有可能是客户端把集群当成单机连端口填的是集群总线端口普通端口 10000而不是数据端口。解决实验里记的方案是走 SSH 通道再加集群连接本质是让客户端只看到一个稳定的本地端口重定向由隧道转发。另一个更省事的办法是干脆不用图形客户端看集群改用redis-cli -c加--cluster check做验证需要可视化时连单个节点看局部数据即可。这里没有银弹选哪个取决于你是要看数据还是要看拓扑。6. 把实验跑成能写进报告的证据cluster check、slowlog 与一次小压测实验做完、截图存好很多人到这一步就收工了但报告里“实验结果分析”那一节往往写不出东西因为手上只有“跑通了”这一个结论。我的习惯是再补三步验证把“跑通”变成“有数据支撑的跑通”这三步都不复杂但能让报告从“我操作了一遍”变成“我验证了这些指标”。第一步是集群健康检查一条命令出全量报告# 任意一个节点作为入口会输出槽位分布、节点角色、迁移状态 redis-cli --cluster check 192.168.x.x:6379这条命令的输出里有三个关键数字16384 slots covered表示槽位全覆盖缺一个就说明有主节点没分到槽3 masters表示主节点数每个 master 后面会列出它的 slave 和 slot 区间。如果看到[ERR] Not all 16384 slots are covered by nodes不用慌先看cluster info里的cluster_state多数是某个节点刚重启还没完成握手等十几秒再查一次。第二步是用SLOWLOG看有没有意料之外的长耗时命令。投票站这种写多读少的场景正常应该全是 O(1) 或 O(log N) 的操作一旦出现几十毫秒的记录基本是误用了KEYS、SMEMBERS全集遍历或者大 key 上的HGETALL。# 把慢查询阈值调到 5 毫秒只保留最近 128 条便于实验观察 redis-cli -p 6379 config set slowlog-log-slower-than 5000 redis-cli -p 6379 config set slowlog-max-len 128 # 跑一段业务后再看 redis-cli -p 6379 slowlog get 10输出里每条记录包含命令、耗时微秒、时间戳和客户端地址。我一般会重点看有没有zinterstore出现在慢日志里——分组多、成员多的时候它是 O(N*K)容易成为瓶颈这时候就该考虑第 4.4 节说的客户端排序方案了。第三步是做一次小规模并发写验证集群下的重定向和写入分布。不需要压测工具用redis-benchmark做定向测试就够# -c 并发 50-n 总请求 10000-t 只测 set/get-P pipeline 每条 16 个命令 redis-benchmark -h 192.168.x.x -p 6379 -c 50 -n 10000 -t set,get -P 16 -q-q只输出汇总行适合直接贴进报告。这里要看的不是绝对 QPS 数值虚拟机环境下的数字没有参考意义而是set和get的耗时分布是否在同一量级。如果get明显慢于set常见原因是数据分布不均导致某些槽位热点用redis-cli --cluster check看槽位分布是否均匀再看cluster nodes里各节点的 key 数量。最后一步是验证持久化这一步最容易被跳过但也最容易在答辩时被问到。做法很简单往集群写一批 key然后依次重启三个主节点再看数据是否还在。# 写 100 个测试 key for i in $(seq 1 100); do redis-cli -c -p 6379 set bench:$i v$i /dev/null; done # 重启一个主节点等它重新加入集群 redis-cli -p 6379 shutdown /usr/bin/redis-server /opt/redis/cluster/redis-6379.conf sleep 5 # 抽查数据是否完整DBSIZE 应该在集群各节点上分别看 redis-cli -c -p 6380 get bench:50这里有个细节shutdown之后不要立刻启动先确认端口释放否则新进程起不来报端口占用。另外如果没开 AOF重启之间写入的数据可能因为dump.rdb的保存策略而丢失一部分看到数据少了不要急着判定为故障先看redis.conf里的save配置和appendonly状态。这个差异正好可以写进报告的“实验结果分析”RDB 和 AOF 在重启场景下的表现不同实验里默认配置对应的是哪种行为。那条“cluster node 重启后身份错乱”的坑我就是这么踩出来的——当时六份配置共用了同一个dir重启一个节点后它的nodes-*.conf被另一个节点覆盖集群直接认不出它。从那以后我每次生成节点配置都强制走一遍grep -H -E ^(port|dir|logfile|cluster-config-file)复核六份输出逐行对一遍确认端口、目录、日志、拓扑文件四项两两不同才敢启动。这条看起来笨的检查比事后翻日志省太多时间。希望帮到你。本文还有配套的精品资源点击获取