首页/新闻资讯/正文详情

Ubuntu 16.04 Redis 单机与六节点集群搭建及投票后端实战

发布时间:2026/11/3 5:30:03 来源:云帆数科 栏目:资讯中心
Ubuntu 16.04 Redis 单机与六节点集群搭建及投票后端实战
简介南邮大数据存储与管理实验一配套文档面向大数据、存储管理课程学生及Redis初学者聚焦在Ubuntu 16.04下完成Redis单机与集群环境安装配置并基于Redis构建简单文章投票网站后端用于课程实验复现与实验报告撰写参考。包内仅含1个doc文件压缩包约933KB体量轻便内容围绕实验目的、环境、原理、操作步骤、结果分析与小结展开涵盖依赖安装、APT源配置、服务启停、集群节点配置与创建以及投票数据模型、发布文章、投票排序等关键实现并对连接异常等常见问题给出排查思路。现有324人学习说明其具备一定参考价值读者可据此梳理Redis集群搭建流程理解String与Sorted Set在投票场景中的用法并借鉴实验记录与总结写法适合需要完成同类实验或快速上手Redis基础应用的人群。1. 连不上的 6379这套 Redis 实验环境到底卡在哪第一次在 Ubuntu 16.04 上搭这套环境卡住我的不是安装是连接虚拟机里redis-cli敲ping能回 PONG宿主机上的图形客户端却一直抛ECONNREFUSED 127.0.0.1:6379。这类问题在 redis 大数据 相关实验里几乎人人要过一遍——单机只算热身后面还有三个主三个从的六节点集群和拿 Redis 当唯一存储的文章投票后端。版本选错、bind和protected-mode没改、上一轮实验残留的 slot 没清干净都会让后面每一步白做。下面按单机能跑、集群能连、投票后端能出结果的顺序写命令和参数都摊开有一台 Ubuntu 16.04 虚拟机就能照着走。2. 单机 Redis 安装Ubuntu 16.04 上的依赖、APT 源与启动方式单机这一段是整个实验的地基但它的坑不在“装不上”而在“装上了却连不上、装成了两份”。Ubuntu 16.04 是个老发行版系统源里本来就带一份 Redis官方 APT 仓库里又是另一份两条路混着走后面redis-server --version和which redis-server对不上集群配置怎么写都不生效。我的习惯是先花三分钟把版本和来源确认清楚再决定用哪种安装方式而不是拿到命令就往上贴。2.1 先用三条命令确认发行版代号和已有版本Ubuntu 16.04 的发行版代号是xenial官方 APT 仓库的软件源路径就是用它拼出来的代号错一位apt-get update直接 404。所以第一件事不是装是看清楚系统底细lsb_release -cs # 输出 xenial说明系统是 Ubuntu 16.04 which redis-server # 有输出说明系统里已经存在一份 redis-server redis-server --version # 看清楚是 3.x 还是 6.x 以上lsb_release -cs的-c是取代号、-s是只输出值不带标签拼起来才是一段能直接塞进sources.list的字符串。which redis-server非空时要留意如果显示/usr/bin/redis-server且版本号是 3.x那它是系统自带的老包而实验里要用的redis-cli --cluster create子命令是 4.0 之后才并入redis-cli的3.x 时代集群合成靠另一个脚本命令完全不同。这就是为什么后面的安装步骤要绕开系统源走官方仓库。提示如果lsb_release命令不存在先装lsb-release这个包别用cat /etc/issue硬猜代号那玩意儿输出的是给人看的字符串塞进源地址里会拼出错误的 URL。2.2 加官方仓库并装包四条命令的逐条解释依赖、公钥、源、安装这四步原文给的是官方教程里的原样命令我在虚拟机上跑过多次顺序不能乱# 1. 装三个前置工具lsb-release 取代号curl 下载公钥gpg 做密钥去封装 sudo apt install -y lsb-release curl gpg # 2. 下载官方公钥并转成 apt 认识的 keyring 格式--dearmor 是二进制化解封装 curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg # 3. 写软件源$(lsb_release -cs) 会被替换成 xenialsigned-by 让 apt 只认这把钥匙 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 条里的-fsSL是四个短选项拼起来的-f让 curl 在 HTTP 状态码异常时直接失败而不是把错误页当公钥写进去-s静默不打印进度条-S出错时仍然报错-L跟随重定向。这四个少一个都可能把一个 HTML 错误页写进 keyring之后apt-get update会报签名校验失败而且报错信息里看不出根因属于典型的黑匣子型故障。第 3 条里的signed-by是关键它把信任范围限定死只有这把钥匙签的包才被接受不会影响系统里其他源。tee之所以要sudo是因为重定向发生在当前 shellsudo echo 这种写法提权的是 echo 不是重定向会报权限不足——这是很多人第一次配源就翻车的地方。注意apt-get update如果在这一步报404 Not Found或does not have a Release file说明官方仓库已经不再为这个代号提供构建。此时别硬改代号伪装成新系统装出来的包和系统库不匹配。常见做法是改走源码编译或者干脆用系统自带的那份然后接受redis-cli --cluster不可用、改用旧脚本合成集群的事实。2.3 redis.conf 里必须动手的四个参数装完不要急着启动配置文件先改。安装包默认的/etc/redis/redis.conf是给“只允许本机访问”的场景准备的而实验要求宿主机上的图形客户端能连进虚拟机必须放开两处再顺手确认两处# /etc/redis/redis.conf bind 0.0.0.0 # 默认是 127.0.0.1只监听回环改 0.0.0.0 才能被外部网卡接进来 protected-mode no # 默认 yes没有密码又非回环访问时直接拒绝命令 daemonize yes # 后台运行不加这个前台一退出服务就断 port 6379 # 单机保持 6379集群节点在后面单独改 dir /var/lib/redis # RDB/AOF 落地目录必须存在且 redis 用户可写bind和protected-mode是一对只改bind不改protected-mode连接能建立但一发命令就被拒只改protected-mode不改bind外面根本连不进来。原文里那条“关闭安全模式却没解决问题”的记录根因就是连接的目标地址还写着127.0.0.1——客户端在宿主机上跑127.0.0.1指的是宿主机自己那里当然没有 Redis。修改方式用vim /etc/redis/redis.conf改完一定要重启服务Redis 不会热加载这种启动参数# 重启并确认 /etc/init.d/redis-server restart redis-cli ping # 期望输出 PONG2.4 启动与开机自启三种启动方式别混用同一个服务有三种启动方式混着用最容易出现“我明明启动了进程却不在”方式命令适用场景init 脚本/etc/init.d/redis-server start老系统、实验环境启动脚本位置固定systemdsystemctl start redis-server现代发行版能看status和日志手动指定配置/usr/bin/redis-server /path/to/xxx.conf集群多实例必须用这种单机阶段用前两种都行Ubuntu 16.04 上两者其实是同一套东西的两层皮。但到了集群阶段必须切到第三种六个节点共用同一个二进制靠的是启动时传入不同的配置文件/etc/init.d/那套脚本只认/etc/redis/redis.conf改不了。查看虚拟机 IP 用ifconfig记下ens33或eth0那张网卡的inet addr后面填客户端连接地址时要用它不是127.0.0.1。同时确认端口是通的ss -lntp | grep 6379 # 看到 0.0.0.0:6379 说明监听正确看到 127.0.0.1:6379 说明 bind 没生效如果ss显示监听地址还是回环别怀疑客户端回去看bind那一行是不是改完没保存或者redis-server启动时用了另一个配置文件。3. 六节点集群搭建目录规划、配置文件与 cluster create集群这一步是整套实验里最容易反复推倒重来的部分。六个实例、六份配置文件、六个数据目录、六个日志文件任何一处命名冲突或者目录缺失表现都是“进程起不来但没提示”或者“起来了但集群合不成”。我一般的做法是先把目录和端口在纸上排好再一次性把六份配置生成出来避免手抄配置抄错一个字符排查半小时。3.1 目录与端口规划先定死再动手端口分配规则很朴素三个主节点用 6379、6380、6381三个从节点用 6389、6390、6391保持“主节点端口 10”的对应关系出问题时一眼能看出谁是备谁。目录统一放在/opt/redis/cluster下数据、日志、pid 各自分目录# 一条 -p 建完整棵目录树避免逐层 mkdir mkdir -p /opt/redis/cluster/{log,data/{6379,6380,6381,6389,6390,6391}} cp /etc/redis/redis.conf /opt/redis/cluster/redis.conf.bak # 留一份模板做对照data/port这一层是必须的。集群模式下每个节点会往dir里写nodes-port.conf和 RDB 文件六个节点共用一个dir会互相覆盖最典型的现象就是集群明明建好了重启一轮之后cluster nodes显示节点 ID 全变了。节点角色端口配置文件数据目录master16379redis-6379.confdata/6379master26380redis-6380.confdata/6380master36381redis-6381.confdata/6381slave16389redis-6389.confdata/6389slave26390redis-6390.confdata/6390slave36391redis-6391.confdata/63913.2 六个节点的配置文件最小可用集合原文里redis-6379.conf的具体内容被截断了按集群的实际要求一份能用的节点配置至少要有下面这些项。以 6379 为例# /opt/redis/cluster/redis-6379.conf port 6379 bind 0.0.0.0 protected-mode no daemonize yes pidfile /opt/redis/cluster/redis-6379.pid logfile /opt/redis/cluster/log/redis-6379.log dir /opt/redis/cluster/data/6379 cluster-enabled yes # 开启集群模式不写这行后面所有命令都白搭 cluster-config-file nodes-6379.conf # 由 Redis 自己维护文件名必须每个节点唯一 cluster-node-timeout 15000 # 节点失联判定毫秒数本地虚拟机 15 秒够用 appendonly yes # 开 AOF断电后比 RDB 少丢数据四个容易写错的地方cluster-enabled yes是集群的开关默认是注释掉的不改这里redis-cli --cluster create会直接回ERR This instance has cluster support disabledcluster-config-file必须带端口号做区分六个节点写成同一个名字就等于共享了集群状态文件dir指向的目录必须先建好Redis 不会帮你创建目录不存在时daemonize yes会让进程静默退出日志里只有一行含糊的错误logfile同理路径里的log目录漏建报错会直接打到标准输出而被daemonize吞掉。其余五个文件用sed批量改比手抄稳cd /opt/redis/cluster for p in 6380 6381 6389 6390 6391; do # 以上面的 6379 配置为模板把所有出现 6379 的位置整体替换成目标端口 sed s/6379/$p/g redis-6379.conf redis-$p.conf done ls redis-63*.conf # 应该看到 6 个文件sed s/6379/$p/g里的g是关键因为一份配置里 6379 出现四次port、pidfile、logfile、dir、cluster-config-file不加g只替换第一个剩下的还是旧端口。3.3 启动六个节点并确认真的起来了六个节点逐个启动路径写绝对路径不要依赖PATH/usr/bin/redis-server /opt/redis/cluster/redis-6379.conf /usr/bin/redis-server /opt/redis/cluster/redis-6380.conf /usr/bin/redis-server /opt/redis/cluster/redis-6381.conf /usr/bin/redis-server /opt/redis/cluster/redis-6389.conf /usr/bin/redis-server /opt/redis/cluster/redis-6390.conf /usr/bin/redis-server /opt/redis/cluster/redis-6391.conf ps -ef | grep redis | grep -v grep # 期望看到 6 行每行末尾是各自的配置文件路径 ss -lntp | grep -E 63(79|80|81|89|90|91) # 期望 6 个端口都在监听ps -ef | grep redis后面那个grep -v grep是必加的否则总有一行是自己的 grep 进程让人误以为多起了一个实例。数量不对时先看日志日志路径就是配置文件里logfile那行写的tail -n 50基本能看到原因端口被占、目录无权限、配置文件语法错。还有一种情况ps显示 6 个进程都在但ss只看到 5 个端口。这通常是某个节点的端口和系统里别的服务撞了或者上一次的 Redis 实例还挂在那个端口上。判断办法是redis-cli -p 6379 info server | grep tcp_port能看到实际监听端口和预期是否一致。3.4 用一条命令合成集群六个节点各自独立运行还不算集群得让它们互相认识、把 16384 个槽分配下去。集群合成只需要一条命令/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:6391--cluster-replicas 1的意思是每个主节点配一个从节点命令会按传入顺序把前三个当主、后三个自动配对成从并各自分到约 5461 个槽。执行过程中会打印一份分配方案要求输入yes确认不输就一直挂着很多人以为卡死其实是等着你回话。合成之后立刻验证两件事redis-cli -c -p 6379 cluster info # cluster_state:ok 且 cluster_known_nodes:6 才算真的成了 redis-cli -c -p 6379 cluster nodes # 6 行3 个 master 后面各自挂 1 个 slave-c参数是集群模式作用是遇到不属于本节点的 key 时自动跟随后续重定向不加这个参数往 6379 写一个落在 6380 槽上的 key 会直接报MOVED。cluster_state:ok之前如果看到cluster_state:fail多半是有节点没起来或者槽没分完回头看 3.3 的进程和端口检查。3.5 客户端接入Another Redis Desktop Manager 的连接方式图形客户端连单机和连集群是两套参数。单机连接填虚拟机的实际 IP不是127.0.0.1、端口 6379如果配置文件里设了requirepass就一起填上测试连接通了再保存。连集群时不能只填一个地址要把 6 个节点全列进去并且勾选 Cluster 模式否则客户端只认一个节点遇到 MOVED 重定向就断表现就是连接状态来回跳。宿主机和虚拟机网络不通的时候还有一招是 SSH 方式客户端里选 SSH 连接填虚拟机的 SSH 地址和账号再在隧道里填 Redis 的地址端口。原文里“ssh 加 cluster 连接后问题解决”记录的就是这条路它绕开的是网络可达性和端口放行不是 Redis 本身的配置问题——这个区分很重要别把它当成 Redis 的玄学。4. 文章投票后端redis-py-cluster 与三个核心函数单机和集群都通了之后实验的第三块才是真正吃理解的部分用 Redis 做唯一存储实现一个能发文章、能投票、能按时间或评分排序的文章投票后端。它没有 MySQL 兜底所有数据模型都得自己在 key 上设计出来一旦 key 设计错了后面排序、分组全得推倒。4.1 装库与环境隔离版本对上再谈代码# 建议先建虚拟环境避免污染系统 python python3 -m venv ~/venv-redis source ~/venv-redis/bin/activate pip install redis-py-cluster pip install redis4.0 # redis-py-cluster 依赖旧版 redis单独升级会冲突redis-py-cluster这个库对redis客户端的版本有硬要求2.x 版本依赖redis3.x直接pip install redis装上最新版会报ImportError: cannot import name StrictRedis。我一般装完先跑一句pip show redis redis-py-cluster看两者的版本组合确认没问题再动代码省得后面在代码里找半天语法错误。4.2 key 设计一张表定生死key类型用途articlestring自增计数器用INCR生成文章 IDarticle:idhash文章正文字段 title、link、poster、time、votestimezset成员是article:id分值是发布时间戳用于按时间排序scorezset成员同上分值是评分用于按热度排序voted:idset记录给这篇投过票的用户防重复投group:nameset分组成员是文章 keyhash 存字段、zset 做排序、set 做去重这三种结构各自解决一个问题组合起来就是一个最小的内容系统。这里有个集群下必须注意的点time和score是全局唯一的 zset所有文章都往里塞单 key 命令没问题但一旦用到ZINTERSTORE这种多 key 操作Redis 会要求这些 key 落在同一个槽上跨槽直接报CROSSSLOT。所以分组排序那段代码我改成了客户端算见 4.4。4.3 post 与 vote发布和投票的完整实现import time from rediscluster import RedisCluster # startup_nodes 列集群里的任意几个节点即可客户端会自动发现完整拓扑 rc RedisCluster(startup_nodes[ {host: 192.168.1.100, port: 6379}, {host: 192.168.1.100, port: 6380}, ], decode_responsesTrue) ONE_WEEK 7 * 24 * 3600 VOTE_SCORE 432 # 一票 432 分 ≈ 一天的评分衰减量86400/200 def post(article_data, redisrc): # 1. 自增拿 IDINCR 在集群里落在单 key article 上天然原子 article_id str(redis.incr(article)) article article article_id # 2. 正文写 hash字段名来自传入的字典 for k, v in article_data.items(): redis.hset(article, k, v) # 3. 发布时间进 zset成员名就是 hash 的 key后面排序直接取成员 redis.zadd(time, {article: time.time()}) # 4. 作者给自己投一票占住 voted 集合的初始位置 vote(article, article_data[poster], redis) return article def vote(article, user, redisrc): # 文章发布超过一周就不能再投时间从 time 这个 zset 里取 if redis.zscore(time, article) time.time() - ONE_WEEK: return False voted_key voted: article # sadd 返回 1 表示新投0 表示重复投去重靠 Redis 保证 if redis.sadd(voted_key, user): redis.expire(voted_key, ONE_WEEK) # 一周后自动清理投票记录 redis.zincrby(score, VOTE_SCORE, article) # 评分累加 redis.hincrby(article, votes, 1) # hash 里的票数同步加一 return True def score(article, redisrc): return redis.zscore(score, article)post里的redis.zadd(time, {article: time.time()})用的是新版传字典的写法老版本是zadd(time, article, time.time())参数顺序反过来升级客户端时报错多半是这一处。vote里先用zscore查发布时间再判断是否过期zscore对不存在的成员返回None和数字比较在 Python 3 里会抛TypeError所以调用方必须保证文章存在。VOTE_SCORE取 432 不是随手写的一天 86400 秒除以一页 200 篇大约每篇文章的评分每天要衰减 432 分这样一票的分量刚好抵消一天的“老化”新文章不至于因为发布时间晚而永远排在后面。要调整排序倾向只改这一个常量就够了别去改公式。4.4 分组与排序为什么不能直接在集群上用 zinterstore分组就是把文章 key 塞进一个 set然后按分组名取出来排序def add_to_group(article, group, redisrc): redis.sadd(group: group, article) # 一个分组一个 set成员是文章 key def get_group_articles(group, order, page, redisrc): key fscore:{order}:{group} # 理论上的排序缓存 key if not redis.exists(key): # 集群下 ZINTERSTORE 要求两个 zset 同槽group 集合不是 zset这里改用客户端算 members redis.smembers(group: group) scored [(m, redis.zscore(score if order score else time, m)) for m in members] scored.sort(keylambda x: x[1], reverseTrue) for m, s in scored: redis.zadd(key, {m: s}) start (page - 1) * 25 return redis.zrevrange(key, start, start 24)zrevrange的start和end都是闭区间下标分页公式是(page-1)*25到start24写成start25会多出一条。这里之所以不直接ZINTERSTORE是因为集群模式下多 key 操作必须同槽group:xxx和score这两个 key 的槽位基本不可能一样跑一次报一次CROSSSLOT Keys in request dont hash to the same slot。客户端排序多走几次网络但能跑通实验阶段这样写足够。5. 避坑与排查五个把实验卡死的报错这一节全是现象、原因、解决三段式的记录都是我或者身边的人真实撞过的。有些报错看着玄学其实是配置里某一行没生效。5.1 客户端报 ECONNREFUSED 127.0.0.1:6379现象图形客户端测试连接失败提示RedisClient On Error: Error: connect ECONNREFUSED 127.0.0.1:6379 Config right?但虚拟机里redis-cli ping正常返回 PONG。原因客户端跑在宿主机上连接地址填的是127.0.0.1这个地址在宿主机上指的是宿主机自己那儿没有 Redis。另一个叠加原因是redis.conf里bind还写着127.0.0.1服务只监听回环就算填对 IP 也进不来。解决先用ifconfig查虚拟机实际 IP把客户端地址改成它再把bind改成0.0.0.0、protected-mode改成no重启服务最后用ss -lntp | grep 6379确认监听地址不是回环。原文里“关掉安全模式没解决、改 IP 后成功”的经验前半段说的就是这个叠加原因。5.2 合成集群时报 cluster support disabled现象执行redis-cli --cluster create后每个节点都回(error) ERR This instance has cluster support disabled命令直接退出。原因配置文件里cluster-enabled这一行被注释掉了或者写成了no。Redis 默认不支持集群这是个需要显式打开的开关。解决六个配置文件全部把cluster-enabled yes前面的#去掉确认值确实是yes然后逐个重启节点。重启后可以redis-cli -p 6379 info cluster | grep cluster_enabled看返回值1才是开的。只改一个节点不够合成时任何一个节点没开都会失败。5.3 合成集群时报 Slot 671 is already busy现象--cluster create跑到一半报(error) ERR Slot 671 is already busy或者提示某些槽已经被占用。原因上一次实验的数据和集群状态没清干净。槽的归属记录在cluster-config-file指向的nodes-*.conf里旧文件还在Redis 启动后就把自己当成旧集群的成员槽自然被占。解决先登录每个节点执行flushall清数据再执行cluster reset清集群状态for p in 6379 6380 6381 6389 6390 6391; do redis-cli -p $p flushall # 清空当前节点数据 redis-cli -p $p cluster reset # 硬重置集群状态注意会解绑所有节点 done如果重置后仍然报占用说明nodes-*.conf是持久化到磁盘的需要停掉进程、删掉对应dir下的nodes-*.conf再启动。这一步做完再重新--cluster create基本就干净了。5.4 连集群时报 connection is close / cluster is ended现象单机能连切到集群模式后客户端状态栏闪一下stream on error : connection is close、cluster is ended然后断开。原因客户端只填了一个节点地址。集群模式下客户端需要知道全部拓扑只给一个入口时遇到重定向或者拓扑刷新就当成连接异常断开了。解决把六个节点地址全部加进连接列表并显式勾选 Cluster 模式。宿主机和虚拟机网络受限时改用 SSH 隧道方式连接在 SSH 配置里填虚拟机地址在 Redis 配置里填127.0.0.1:6379加 Cluster 模式隧道由客户端自己转发绕开端口不可达的问题。5.5 分组排序报 CROSSSLOT现象调用分组排序接口时抛redis.exceptions.ResponseError: CROSSSLOT Keys in request dont hash to the same slot。原因用了ZINTERSTORE或者MULTI这类一次操作多个 key 的命令而集群把不同 key 哈希到了不同槽服务端直接拒绝执行。解决两条路一是给相关 key 加同样的 hash tag比如都写成{group:reading}:score这种形式花括号里的内容参与哈希能强制同槽二是放弃服务端多 key 操作改成smembers拿成员、客户端逐个zscore再排序4.4 那段代码走的是第二条。实验规模下客户端排几十条数据性能完全够。6. 装完怎么验证从 ping 到投票链路跑通装完不等于跑通。我习惯按“进程 → 端口 → 单机命令 → 集群状态 → 业务链路”这个顺序验每一步都有明确的通过标准不通过就停在当层排查不要跳到下一层。先验进程和端口这一步只证明服务活着ps -ef | grep redis | grep -v grep | wc -l # 期望 71 个单机 6 个集群节点或 6取决于是否保留单机 ss -lntp | grep -cE :(6379|6380|6381|6389|6390|6391) # 期望 6再验单机和集群的状态用一张表对照着看比一条条试快检查项命令通过标准单机存活redis-cli ping返回PONG集群状态redis-cli -c -p 6379 cluster infocluster_state:ok节点数量redis-cli -c -p 6379 cluster nodes6 行3 主 3 从槽覆盖cluster info里的cluster_slots_assigned16384读写跳转redis-cli -c -p 6379 set k1 v1不报CLUSTERDOWN槽那个数字特别值得看cluster_slots_assigned如果是 16383 而不是 16384说明有一个槽没分出去集群状态会一直卡在fail业务代码全报错。这种情况用redis-cli --cluster fix 127.0.0.1:6379让它自己补齐比手动迁移省事。最后验业务链路直接跑一段冒烟脚本比在交互式客户端里敲命令靠谱# smoke_test.py —— 发布、投票、排序三步串起来跑一遍 from vote_app import rc, post, vote a post({title: 冒烟测试, link: http://example.com, poster: u1}, rc) print(新文章 key:, a) print(重复投票返回:, vote(a, u1, rc)) # 同一用户再投sadd 返回 0分数不应变化 print(换人投票后评分:, rc.zscore(score, a)) # 应该比刚才多 432 top rc.zrevrange(score, 0, 4, withscoresTrue) print(评分前五:, top)通过标准很直观post返回的 key 是articleN形式且 N 递增同一用户重复投票评分不变换个用户投一次分数正好加 432zrevrange拿到的第一条就是刚发的文章。这三条都过说明 hash、zset、set 三种结构串起来了集群的槽路由也没问题。我一般还会顺手打开图形客户端把article这个计数器和score这个 zset 各点开看一眼肉眼确认成员名和分值对得上。这一步看着多余但能发现“代码写进去了、连的却是另一个实例”这类问题——集群里六个节点客户端连错一个数据就落到别的槽上光看代码输出是看不出来的。从那以后我每次重建这套环境都会强制先跑一遍槽数检查cluster_slots_assigned不是 16384 就不往下走宁可多花两分钟重置也不在业务代码里找一小时的CLUSTERDOWN。这套单机加六节点集群的配置和投票后端的代码我整理成了可直接复现的版本照着上面几章的顺序走第一次搭也能少绕几个弯。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

投机解码加速的真相:瓶颈份额决定端到端收益
投机解码加速的真相:瓶颈份额决定端到端收益

投机解码这几年的热度一直不降,从LLM推理服务到端侧部署,到处都能看到“xx倍加速”的宣传数字。但说实话,我日常接触到的很多项目,真正把投机解码大规模落地到生产环境,反而很少能复现出论文里的加速比。仔细追下去&am… · 2026/11/3 5:30:03

浏览器MCP实战:让AI接管网页导航、点击与数据采集的自动化指南
浏览器MCP实战:让AI接管网页导航、点击与数据采集的自动化指南

最近被我那台“会自己干活”的浏览器折腾得有点上头。起因是我想让 AI 帮我巡检十几个网页状态,再整理成报告。以前这种活无非两条路:要么写 Playwright 脚本,要么自己手动一个个开页面看。脚本要维护,手动看要命。后来我换成 MCP… · 2026/11/3 5:30:03

Docker容器化部署Redis的最佳实践与优化策略
Docker容器化部署Redis的最佳实践与优化策略

1. Redis与Docker的黄金组合:为什么选择容器化部署Redis作为当今最流行的内存数据库之一,其高性能、低延迟的特性使其成为缓存、会话存储和实时数据分析的首选方案。而Docker的轻量级容器技术,则为Redis的部署和管理带来了革命性的便利。传统… · 2026/11/3 5:30:03

排列构造与mex函数在算法竞赛中的应用
排列构造与mex函数在算法竞赛中的应用

1. 题目背景与核心思路解析P15652 [省选联考 2026] 排列游戏是一道考察排列构造与mex函数性质的交互题。题目要求我们通过不超过n次的mex查询,构造出一个长度为n的排列,使其满足特定的mex条件。核心思路在于利用mex函数的单调性质:前缀mex数组… · 2026/11/3 6:03:13

优先队列原理、实现与工程实践指南
优先队列原理、实现与工程实践指南

1. 优先队列的本质与应用场景优先队列(Priority Queue)是一种特殊的抽象数据类型,它不同于普通队列的先进先出(FIFO)原则,而是按照元素的优先级进行动态排序。每次从队列中取出的都是当前优先级最高的元素&… · 2026/11/3 6:03:13

双重检查锁定模式在多线程环境下的实现与优化
双重检查锁定模式在多线程环境下的实现与优化

1. 问题背景与Double Check的概念解析在编程实践中,"Double Check"通常指对关键代码逻辑进行双重验证的设计模式。这种模式常见于并发编程、表单验证、数据一致性检查等场景。一个典型的例子是单例模式中的双重检查锁定(Double-Checked Locking)&#xff… · 2026/11/3 6:03:13

深入解析Double Check模式与多线程安全初始化
深入解析Double Check模式与多线程安全初始化

1. 问题背景与Double Check概念解析在编程实践中,Double Check(双重检查)是一种常见的代码优化模式,特别是在多线程环境下初始化资源时。这种模式的核心思想是通过两次条件判断来减少不必要的同步开销,同时保证线程安全… · 2026/11/3 6:03:13

优先队列原理与实现:高效任务处理的核心数据结构
优先队列原理与实现:高效任务处理的核心数据结构

1. 优先队列:任务处理的高效引擎在计算机科学的世界里,优先队列(Priority Queue)就像医院急诊科的护士长,能够快速判断哪些病人需要优先处理。这种数据结构不同于普通的先进先出队列,它允许我们为每个元素分… · 2026/11/3 6:03:13

RAID技术详解:从原理到实战选型指南
RAID技术详解:从原理到实战选型指南

1. RAID技术概述与核心价值RAID(Redundant Array of Independent Disks)技术自1988年由加州大学伯克利分校提出以来,已成为存储领域的基石技术。这项技术通过将多个物理磁盘组合成逻辑单元,实现了数据存储的性能提升和可靠性保障的… · 2026/11/3 6:02:12

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码