
简介本资源为南京邮电大学大数据存储与管理课程实验一的完整报告文档面向正在学习Redis与大数据存储技术的高校学生及自学者帮助解决Redis单机与集群环境搭建、以及基于Redis构建文章投票网站后端两大实验任务。压缩包内仅含1个doc格式实验报告大小约933KB内容涵盖实验目的、环境配置、详细操作步骤、关键代码与结果分析结构完整、可直接参考。文档从Ubuntu 16.04下安装配置Redis单机环境讲起逐步延伸到六节点集群的搭建与验证并给出使用String与Sorted Set实现文章发布、投票、分组及评分排序的核心代码同时附有连接报错等常见问题的排错思路。目前已有324人学习适合需要完成同类实验或希望快速掌握Redis集群与实战应用的学习者对照使用。1. 连不上 Redis 报 ECONNREFUSED先别急着关安全模式“RedisClient On Error: Error: connect ECONNREFUSED 127.0.0.1:6379 Config right?”——这个报错我在做 Redis 相关实验时踩过不止一次。表面看是客户端连不上服务端实际上十有八九不是 Redis 没启动而是你连的地址根本不对。很多同学在虚拟机里装完 Redis用宿主机上的 Another Redis Desktop Manager 去连地址栏默认填 127.0.0.1结果就是一直 ECONNREFUSED。网上搜到的第一反应往往是“关闭安全模式”但改完 protected-mode 发现还是连不上白折腾半小时。这篇笔记就把 Redis 单机安装、集群搭建、客户端连接、投票后端开发这条链路上最容易翻车的几个报错拆开讲清楚适合正在做大数据存储实验、被 Redis 连接和集群配置卡住的开发者。2. 单机都连不上ECONNREFUSED 与绑定地址的坑2.1 为什么 127.0.0.1 在虚拟机场景下必然失败ECONNREFUSED 的本质是 TCP 连接被拒绝说明客户端发出的 SYN 包到达了目标地址但目标端口上没有进程在监听或者监听的根本不是你请求的那个地址。在虚拟机 宿主机客户端的架构里Redis 默认配置bind 127.0.0.1意思是只接受来自本机回环地址的连接。你在宿主机上填 127.0.0.1请求发到的是宿主机自己的 6379 端口而 Redis 跑在虚拟机里宿主机 6379 上什么都没有自然被拒绝。很多人第一步会去搜“Redis 连接被拒绝”然后照着改protected-mode no。这个参数确实和远程连接有关但它解决的是“Redis 收到了连接但因为保护模式拒绝服务”的情况报错通常是DENIED Redis is running in protected mode而不是 ECONNREFUSED。把这两个报错混为一谈就会出现在错误的方向上反复尝试的情况。正确的定位顺序是先确认 Redis 进程活着再确认它监听在哪个地址最后确认客户端连的地址和监听地址一致。# 第一步确认 Redis 进程是否存在 ps -ef | grep redis-server # 第二步查看 Redis 实际监听的地址和端口 ss -tlnp | grep 6379 # 或者用 netstat netstat -tlnp | grep 6379 # 第三步在虚拟机内部用 redis-cli 自连排除服务本身的问题 redis-cli -h 127.0.0.1 -p 6379 ping # 返回 PONG 说明服务正常如果ss输出显示监听的是127.0.0.1:6379那宿主机上的客户端无论怎么配都连不上必须让 Redis 监听虚拟机的对外 IP 或0.0.0.0。2.2 改 bind 和 protected-mode 的正确姿势修改配置文件/etc/redis/redis.conf找到bind那一行。默认是bind 127.0.0.1 ::1把它改成虚拟机的实际 IP或者直接改成0.0.0.0表示监听所有网卡。同时把protected-mode设为no否则即使监听了外部地址Redis 也会拒绝非本机来源的连接。# 编辑配置文件 sudo vim /etc/redis/redis.conf # 找到并修改以下两行 # bind 127.0.0.1 ::1 → bind 0.0.0.0 # protected-mode yes → protected-mode no # 重启 Redis 使配置生效 /etc/init.d/redis-server restart # 再次确认监听地址 ss -tlnp | grep 6379 # 应该看到 0.0.0.0:6379 或虚拟机IP:6379参数说明bind 0.0.0.0让 Redis 在所有网络接口上监听虚拟机外部可以通过虚拟机 IP 访问。protected-mode no关闭保护模式允许非回环地址的连接。这两个参数要一起改只改一个往往还是连不上。然后在宿主机上用ifconfig查到虚拟机的 IP比如 192.168.x.x在 Another Redis Desktop Manager 里把连接地址从 127.0.0.1 改成这个 IP端口保持 6379连接就能建立。这一步看着简单但很多人卡在“以为改了安全模式就行”忽略了地址本身才是根因。注意如果虚拟机用的是 NAT 网络模式宿主机访问虚拟机 IP 通常没问题如果是仅主机模式需要确认网段是否互通。改完配置后一定要重启 Redis配置文件不会热加载。2.3 防火墙和端口转发的排查如果改完 bind 和 protected-mode 还是连不上下一步查防火墙。Ubuntu 默认的 ufw 可能拦截了 6379 端口。# 查看防火墙状态 sudo ufw status # 如果防火墙是 active 状态放行 6379 sudo ufw allow 6379/tcp # 再次测试连接 redis-cli -h 虚拟机IP -p 6379 ping还有一种情况是虚拟机网络模式导致宿主机根本路由不到虚拟机 IP。这时候可以在虚拟机里用ip addr确认网卡地址在宿主机上ping一下这个地址ping 不通就先解决网络连通性别在 Redis 配置上继续绕。3. 集群建不起来cluster support disabled 与 Slot 被占用3.1 cluster-enabled 默认关闭导致的启动失败单机跑通之后下一步搭集群。启动节点时如果看到(error) ERR This instance has cluster support disabled原因很直接Redis 默认不开启集群模式配置文件里cluster-enabled是注释掉的或者值为no。集群模式下每个节点需要额外的集群总线端口普通端口 10000并且要能互相通信。# 编辑每个节点的配置文件比如 redis-6379.conf vim /opt/redis/cluster/redis-6379.conf # 确保以下配置存在且未被注释 cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 port 6379 daemonize yes logfile /opt/redis/cluster/log/redis_6379.log dir /var/lib/redis/6379 # 启动节点 /usr/bin/redis-server /opt/redis/cluster/redis-6379.conf # 验证集群模式是否开启 redis-cli -p 6379 info cluster # 输出中 cluster_enabled:1 表示已开启参数说明cluster-enabled yes是集群模式的开关不开这个后面所有集群命令都会报错。cluster-config-file是节点自动维护的集群状态文件每个节点文件名不能重复通常用端口号区分。cluster-node-timeout是节点超时判定时间单位毫秒默认 15000 适合实验环境。daemonize yes让 Redis 后台运行否则终端会被占住。六个节点三个主三个从都要配这些参数端口分别是 6379、6380、6381、6389、6390、6391。启动后用ps -ef | grep redis确认六个进程都在。3.2 Slot 671 is already busy旧数据没清干净集群创建时另一个高频报错是(error) ERR Slot 671 is already busy。这个报错的意思是你要分配的哈希槽已经被占用了。Redis 集群把整个键空间分成 16384 个槽每个主节点负责一部分。如果之前创建过集群、节点里还残留着旧的集群配置和槽分配信息再次创建时就会冲突。根因是旧数据和旧配置没清理。每个节点的数据目录里有nodes-*.conf文件记录了集群状态和槽分配光删数据不够还要重置集群状态。# 对每个节点依次执行连接、清空数据、重置集群 redis-cli -p 6379 flushall cluster reset exit redis-cli -p 6380 flushall cluster reset exit # 6381、6389、6390、6391 同样操作 # 停掉所有 Redis 进程删除旧的集群配置文件 pkill redis-server rm -f /var/lib/redis/6379/nodes-6379.conf rm -f /var/lib/redis/6380/nodes-6380.conf # ... 其余节点同理 # 重新启动所有节点 /usr/bin/redis-server /opt/redis/cluster/redis-6379.conf # ... 其余节点同理 # 重新创建集群 /usr/bin/redis-cli --cluster create --cluster-replicas 1 \ 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:6391flushall清空当前节点的所有数据cluster reset重置集群状态包括槽分配和节点信息。两个命令配合使用才能彻底清理。--cluster-replicas 1表示每个主节点配一个从节点六个节点正好三主三从。创建过程中会提示确认槽分配方案输入yes继续。3.3 集群创建后如何验证节点状态集群创建成功后用cluster nodes和cluster info确认状态。redis-cli -c -p 6379 cluster nodes # 输出六行每行格式节点ID IP:端口总线端口 角色 主节点ID 槽范围 # 三个 master 各带一段槽范围三个 slave 分别指向对应的 master cluster info # cluster_state:ok 表示集群健康 # cluster_slots_assigned:16384 表示所有槽都已分配 # cluster_known_nodes:6 表示六个节点都被识别如果cluster_state显示fail通常是槽没有全部分配或者节点间通信有问题。检查每个节点的总线端口普通端口 10000是否可达以及cluster-node-timeout是否设置合理。4. 客户端连集群报 connection is close连接方式选错了4.1 普通连接和集群连接的区别单机 Redis 连上了集群也建好了但用 Another Redis Desktop Manager 连集群时提示stream on error: connection is close和cluster is ended。这个报错的根因是客户端用单机模式去连集群或者集群连接配置不完整。Redis 集群和单机在协议层面有区别。集群模式下客户端需要支持 MOVED 和 ASK 重定向——当你访问的 key 不在当前节点上时节点会返回重定向指令客户端要能自动跟随。普通客户端不支持这个机制连上之后执行命令就会断。# 命令行验证集群是否正常工作 redis-cli -c -p 6379 # -c 参数表示启用集群模式自动跟随重定向 set foo bar # 如果 foo 的槽不在 6379 上会看到 MOVED 重定向然后自动跳转 get foo # 能正常返回 bar 说明集群读写正常-c是 redis-cli 的集群模式开关不加这个参数连集群遇到重定向就会报错。命令行能通说明集群本身没问题问题出在图形客户端的连接配置上。4.2 Another Redis Desktop Manager 连集群的正确配置在 Another Redis Desktop Manager 里新建连接时要选择集群模式而不是单机模式。具体操作是连接类型选 Cluster然后添加集群中的所有节点地址至少填一个主节点客户端会自动发现其余节点。如果客户端版本不支持直接填集群地址或者网络环境有限制可以用 SSH 隧道的方式。在连接配置里勾选 SSH 选项填入虚拟机的 SSH 地址和端口再配置 Redis 集群地址。这样客户端通过 SSH 连到虚拟机上再由虚拟机内部访问 Redis 集群绕过了宿主机直接访问集群节点的网络问题。注意集群模式下每个节点都要能互相通信包括客户端可能被重定向到的任意节点。如果只开放了部分节点的端口重定向后还是会断。实验环境里建议六个节点的端口全部放行。4.3 用 Python 连接集群的代码验证图形客户端排查完之后最终要落到代码里。Python 连 Redis 集群需要装redis-py-cluster库注意它和单机的redis库用法不同。from rediscluster import RedisCluster # 集群节点列表至少填一个可达节点 startup_nodes [ {host: 127.0.0.1, port: 6379}, {host: 127.0.0.1, port: 6380}, {host: 127.0.0.1, port: 6381}, ] # 创建集群连接decode_responsesTrue 让返回值为字符串而非字节 rc RedisCluster(startup_nodesstartup_nodes, decode_responsesTrue) # 测试读写 rc.set(test_key, hello_cluster) print(rc.get(test_key)) # 输出 hello_cluster # 查看集群信息 print(rc.cluster_info())参数说明startup_nodes是集群的种子节点列表客户端会通过它们发现完整集群拓扑。decode_responsesTrue让返回的字符串自动解码省去手动.decode()的步骤。如果连接时报RedisClusterException检查节点地址是否正确、集群状态是否为 ok。5. 投票后端跑不通数据结构用错与命令不兼容5.1 文章发布后数据没写进去投票后端的第一段逻辑是发布文章生成文章 ID、写入哈希、加入时间有序集合、给自己投初始票。代码跑完没报错但 Another Redis Desktop Manager 里看不到数据或者数据不完整。import time from rediscluster import RedisCluster startup_nodes [{host: 127.0.0.1, port: 6379}] rc RedisCluster(startup_nodesstartup_nodes, decode_responsesTrue) def post(articleData, redisrc): # article 计数器自增作为文章唯一 ID articleId str(redis.incr(article)) article article articleId # 把文章属性写入哈希结构 for k, v in articleData.items(): redis.hset(article, k, v) # 记录发布时间用有序集合按时间排序 post_time time.time() redis.zadd(time, {article: post_time}) print(即将为自己投初始票!, articleData[poster]) vote(article, articleData[poster], True) score(article, redis) return article这段代码的关键点redis.incr(article)生成全局唯一的文章编号保证多篇文章不冲突。redis.hset(article, k, v)把标题、作者、内容等字段存成哈希一个文章对应一个哈希键。redis.zadd(time, {article: post_time})把文章加入时间有序集合score 是发布时间戳后续可以按时间排序或分页。如果数据没写进去先确认articleData字典不为空再检查redis.incr是否返回了预期值。集群模式下跨槽操作会报 CROSSSLOT 错误比如在同一个事务里操作两个不同槽的 key。解决办法是用哈希标签{}强制多个 key 落在同一个槽比如article{1}和time{1}。5.2 投票和评分逻辑的常见错误投票逻辑的核心是用户不能重复投同一篇文章投票后要更新文章的评分和排序。def vote(article, user, redisrc): # 记录用户对这篇文章的投票时间 # 用有序集合存储score 是投票时间戳 if redis.zadd(voted: article, {user: time.time()}): # zadd 返回 1 表示新添加返回 0 表示已存在 # 新投票才需要更新评分 redis.zincrby(score, 1, article) redis.hincrby(article, votes, 1) return True return False def score(article, redisrc): # 获取文章当前评分 return redis.zscore(score, article)redis.zadd(voted: article, {user: time.time()})利用有序集合的成员唯一性实现“一人一票”同一个 user 重复添加时返回 0不会重复计分。redis.zincrby(score, 1, article)给文章评分加一。redis.hincrby(article, votes, 1)同时更新哈希里的投票数字段。常见错误是直接用set存投票记录然后手动判断是否存在这样在并发下会有竞态条件。用zadd的返回值判断是原子操作更可靠。5.3 分组和排序输出的实现实验里还涉及把文章加入分组、按评分降序输出。分组可以用集合实现排序用有序集合的zrevrange。def add_to_group(article, group, redisrc): # 把文章加入指定分组 redis.sadd(group: group, article) def get_group_articles(group, redisrc): # 获取分组内所有文章 return redis.smembers(group: group) def get_top_articles(n10, redisrc): # 按评分降序取前 n 篇文章 # zrevrange 返回 [(article, score), ...] return redis.zrevrange(score, 0, n - 1, withscoresTrue)redis.sadd把文章加入集合集合自动去重。redis.zrevrange(score, 0, n-1, withscoresTrue)按评分从高到低取前 n 篇withscoresTrue让返回值包含评分。如果输出顺序不对检查zadd时 score 是否设置正确以及是否用了zrevrange而不是zrange。6. 快速定位 Redis 故障我的排查顺序和验证习惯做了几轮 Redis 实验之后我总结出一套固定的排查顺序基本能覆盖八成以上的报错。第一步永远是确认进程和端口ps -ef | grep redis看进程在不在ss -tlnp | grep 6379看监听地址对不对。这两条命令花不了十秒但能直接区分“服务没起来”和“服务起来了但连不上”两大类问题。第二步是分层验证连接。在虚拟机内部用redis-cli -h 127.0.0.1 ping自连通了说明服务本身没问题再用redis-cli -h 虚拟机IP ping从外部地址连通了说明网络和绑定配置没问题最后用客户端工具连不通就是客户端配置的问题。这样一层层排除不会在错误的方向上浪费时间。第三步是看日志。Redis 的日志文件在配置文件里指定比如/opt/redis/cluster/log/redis_6379.log。启动失败、集群握手失败、槽分配冲突日志里都有线索。很多人遇到报错只盯着终端输出忽略了日志里更详细的错误上下文。报错关键词最可能的原因第一件事ECONNREFUSED地址填错或 bind 限制查ss -tlnp确认监听地址cluster support disabledcluster-enabled 未开启改配置文件并重启节点Slot xxx is already busy旧集群数据残留每个节点 flushall cluster resetconnection is close客户端未用集群模式换集群连接或加-c参数CROSSSLOT跨槽操作用哈希标签{}绑定同槽还有一个习惯每次改完配置文件重启服务后一定用redis-cli info或cluster info确认配置生效了。配置文件改了但没重启、重启了但读的是另一个配置文件、配置项拼写错了——这些低级错误我全犯过。后来我养成了一个习惯改完配置先redis-cli config get 参数名看实际生效的值确认无误再继续下一步。从那以后我每次搭集群之前都强制走一遍“清数据、删配置、重启、验证”的流程再也没被 Slot 占用坑过。希望这些排查思路能帮到你。本文还有配套的精品资源点击获取