ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis服务器部署与生产级排障实战:从安装到缓存治理

Redis服务器部署与生产级排障实战:从安装到缓存治理 服务器之 Redis从零搭建到生产级排障的完整实战笔记Redis 在服务器端的重要性根本不需要我再多吹。只要你的系统扛过一定的并发Redis 基本就是那根绕不开的“救命稻草”。它是高性能键值存储服务器能做缓存、分布式锁、消息队列、排行榜甚至直接把数据库顶到前面扛读流量。这篇文章我会从零开始把服务器上部署 Redis 的完整链路捋一遍安装选型、配置避坑、数据类型怎么用、缓存治理怎么做、分布式锁怎么写、线上问题怎么查。适合刚接触 Redis 的运维也适合正在把 Redis 从“能跑”升级成“生产可用”的开发者。所有内容都是我实际踩过坑之后的总结不会给你抄那些官方文档。在开始之前先说下我的环境参考两台 CentOS 7.9 / Rocky Linux 8 的服务器一台 4C8G一台 8C16GRedis 版本从 5.x 一直用到现在的 7.2应用以 Java Spring Boot 为主。后面所有操作都是在普通用户下执行需要 root 权限时用 sudo全程不涉及任何敏感操作。1. 服务器上部署 Redis为什么它总在缓存层的第一位1.1 为什么你的服务器一定要有个 Redis很多第一次接触服务器的人会问我现在数据库都用了 MySQL为什么还要在中间加一个 Redis我通常用一个比喻解释MySQL 就像一个大仓库东西都放在仓库里找起来慢、盘点也慢Redis 就像仓库门口的一排货架把最常用的货直接摆上去伸手就能拿到。你每天有很多读请求总不能每次都去仓库翻箱倒柜那样仓库迟早被打爆。实际场景里我见过一台 MySQL 在 3000 左右的 QPS 下就开始出现慢查询、连接池堆积而同样的服务前面挂一个 Redis读请求全部打到 Redis 上单实例轻松扛 5 万到 10 万 QPSMySQL 的压力瞬间降到几百。所以从架构角度讲Redis 不是可选项而是解决“服务器撑不住高并发”的最廉价手段。另外 Redis 不止做缓存。它的“原子性操作 过期机制 持久化 发布订阅”这些特性让它可以承担分布式锁、限流计数器、会话共享、排行榜、消息队列等工作。也就是说你在一台服务器上部署好 Redis它就相当于给你的整个系统架构补上了好几个重要组件。1.2 部署前的容量规划内存、CPU 和网络我见过太多人直接yum install redis装上就用结果上线第二天内存不够了才来做优化。在装之前你一定要算清楚三件事。第一是内存预算。Redis 的数据全部在内存里这一点和 MySQL 不同MySQL 可以把热数据留在 buffer pool冷数据留在磁盘但 Redis 没有这个层级。你要根据业务峰值估算数据量。我有一个简单的估算公式最终常驻内存数据量 单条 key 的平均大小bytes × key 总数 × 1.5 到 2 的膨胀系数。膨胀系数是因为 Redis 自身的数据结构、dict entry 头部、过期信息都会显著增加内存占用。比如你计划存 1 亿个用户 token每个平均 100 字节那你至少需要100 * 100000000 * 1.5 ≈ 15GB的内存那台服务器就不能只配 8GB不然 redis 进程会在内存写满后触发淘汰策略最坏情况下把刚写的缓存全部清掉引发缓存雪崩。第二是 CPU。Redis 是单线程模型这不是它的缺点反而让它没有锁竞争、上下文切换开销小。但单线程意味着单个 Redis 实例只能吃满一个 CPU 核心如果你这台服务器是 32 核开一个 Redis 实例只能用到其中一核。所以配置高一点的路由器建议直接部署多个实例如每个端口 6379 / 6380 / 6381或者用 Redis Cluster 自带的多分片机制。不过对大多数中小企业一个主从架构或者哨兵架构就够用了没必要上来就上 Cluster。第三是网络。Redis 的吞吐量强也意味着它对网络的消耗非常快。如果业务是跨机房访问RTT 带来的延迟会直接主导服务性能。比如你从华东访问华南的 Redis每次命令 30ms 延迟就算 Redis 本身 0.1ms 也没用。部署时尽量和应用同机房、同 VPC 网络跨机房的场景一定考虑内网域名 专线。1.3 三种安装方式实测对比包管理器、编译源码、Docker装 Redis 有几种常见方式我把实际体验给你们列出来省得你们走弯路。方式一系统包管理器apt / yum / dnfCentOS 默认源里的 redis 版本通常很老3.2 菜但如果用 EPEL 源能到 5.x。Ubuntu 20.04 的 apt 源自带 redis 5.022.04 自带 6.0。好处是安装简单、systemd 服务都帮你配好了适合快速验证和简单的内部服务。坏处是版本不跟手很多新特性如 ACL、Redis Functions用不了。如果只是为了做个测试环境这方式足够。方式二编译源码安装这是生产环境最推荐的方式。因为你可以自定义安装路径、指定编译参数拿到最新稳定版源码后自己把关。我一般在/opt/redis部署步骤是下载源码、解压、make make install然后自己写 systemd unit 文件。编译对机器的构建环境有要求需要gcc、make、pkg-config等Redis 源码本身不算大编译也就几分钟。需要注意的一点是编译前最好看一下README.md有些版本需要不同的BUILD_TLSyes参数才能启用 TLS 支持。方式三Docker 容器如果你服务器上已经全面容器化管理那 Docker 跑 Redis 非常合适。官方镜像redis和redis/redis-stack-server都是直接可用。但有一个关键点要记住容器里的 redis 默认无法数据持久化除非挂载 volume你需要在启动命令里挂一个宿主机目录并且加上--appendonly yes。我经常看到有人在容器里跑 Redis 重启后数据全丢就是没挂数据卷。此外Docker 网络对访问延迟有一定影响如果是低延迟场景裸机或宿主机部署更容易把控。我这里给一个我比较满意的裸机编译安装过程结合生产习惯供参考# 下载解压 wget https://download.redis.io/releases/redis-7.2.4.tar.gz tar xzf redis-7.2.4.tar.gz cd redis-7.2.4 # 编译 make -j$(nproc) make install PREFIX/opt/redis # 建立目录和配置 mkdir -p /opt/redis/etc /opt/redis/data /opt/redis/logs cp redis.conf /opt/redis/etc/redis.conf # 创建专用用户不要用 root 跑 redis sudo useradd --system --no-create-home redis sudo chown -R redis:redis /opt/redis之后用 systemd 管理unit 文件精简如下[Unit] DescriptionRedis Server Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Userredis Groupredis ExecStart/opt/redis/bin/redis-server /opt/redis/etc/redis.conf ExecStop/opt/redis/bin/redis-cli -p 6379 shutdown Restarton-failure LimitNOFILE65535 [Install] WantedBymulti-user.target关于为什么不能直接 root 运行我后面会专门说服务器安全是个大话题Redis 被攻击的例子太多了。2. 核心配置从默认参数到一套可用的生产级参数2.1 内存策略与持久化怎么选RDB、AOF 还是混合Redis 默认配置的save 3600 1 300 100 60 10000是 RDB 快照策略配合 AOF 关闭。这在开发环境没问题但生产环境如果只开默认配置故障恢复时可能会丢一段时间的数据。你需要想清楚你的业务到底能不能接受丢失最近几十秒到几分钟的数据。RDB 是“全量快照”它会 fork 一个子进程把内存中的数据生成一个压缩的二进制文件 dump.rdb。优点是恢复快、文件小、对性能影响相对小。缺点是快照之间如果服务器宕机中间写入的数据全部丢失。另外如果数据集特别大fork 子进程瞬间会消耗大量内存和 CPU甚至导致主进程阻塞。我见过一台 64GB 内存的 Redis开着大 RDB 配置做快照的那几十毫秒内客户端超时后来我调整了 save 策略才解决。AOF 是“追加日志”每次写命令都会追加到 aof 文件末尾。优点是最多丢 1 秒的数据如果你设置appendfsync everysec缺点是文件比 RDB 大很多恢复速度也慢极端情况下fsync会造成一定的 IO 压力。生产上我现在更推荐混合持久化aof-use-rdb-preamble yes即 AOF 文件开头用 RDB 格式压缩已有数据后续用 AOF 追加新命令。这样既有 AOF 的可靠性又有 RDB 的快恢复速度。Redis 4.0 以后就支持这个特性默认开着非常推荐。配置上面我建议至少给以下参数留出位置appendonly yes appendfilename appendonly.aof appendfsync everysec no-appendfsync-on-rewrite yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb save 3600 1 300 100 60 10000 stop-writes-on-bgsave-error no rdbcompression yes rdbchecksum yes注意stop-writes-on-bgsave-error默认配置是 yes意思是如果 RDB 写盘失败Redis 会拒绝所有写入请求这在磁盘故障时能保护数据但也可能阻塞业务。生产环境我一般改成 no因为磁盘临时故障时我宁愿让业务继续跑也不愿直接停止写入。但这个决策需要结合你的具体场景不要盲改。2.2 网络与安全绑定 IP、设置密码、ACL 权限控制Redis 默认只绑定 127.0.0.1。如果你只是本机应用访问这个配置最安全完全不需要担心被外网扫描。但如果要跨服务器访问比如应用服务器连独立的 Redis 服务器必须修改bind配置指定内网 IP 或 0.0.0.0不建议。绑定方式我推荐用子网或内网 IP而不是直接 0.0.0.0。比如bind 192.168.1.10 protected-mode yes port 6379如果同时设置protected-mode yesRedis 只允许绑定地址来源的客户端访问。这样比单纯放防火墙更可靠。密码方面5.0 以上我基本不用requirepass这种全局密码而是改用 ACL 细粒度权限user default off user app on AppPssw0rd_2024 ~cache:* all -admin上面创建了一个app用户密码是AppPssw0rd_2024只能操作cache:前缀的 key且禁用了 admin 命令。ACL 真正好用能避免某个客户端flushall把整个库清掉这种事故我见过不止一次。还有一个极其重要的安全设置禁用危险命令。即使有密码我依然建议在配置里把重命令重命名或直接禁用rename-command FLUSHALL rename-command FLUSHDB rename-command KEYS rename-command SHUTDOWN 单独提一下KEYS它是 Redis 性能杀手。生产环境禁止用 KEYS*去匹配大量 key一旦 key 数量过百万它会阻塞 Redis 单线程执行线上直接“假死”。排查的时候用SCAN替代KEYS这是基本功。防火墙层面我习惯配合 firewalld 或 ufw 只放行应用服务器 IP 到 Redis 端口。如果 Redis 端口被暴露在公网再强的密码也可能扛不住暴力破解恶意扫描工具专挑 6379 下手。2.3 日志与监控别等故障了才来看Redis 默认把日志打到 stdout在 systemd 环境下可以直接journalctl -u redis查看。但我更推荐把日志指向文件配置里一行logfile /opt/redis/logs/redis.log然后把 logrotate 配上避免日志无限膨胀。监控方面我不喜欢过度建设。如果你刚上手用两招就够INFO stats、INFO memory手动看一眼尤其注意used_memory、used_memory_rss和瞬时instantaneous_ops_per_sec。部署一个简单的采集脚本把INFO的字段推送给监控系统。如果要更高级的可以用 Redis 自带的redis-cli --latency-monitor或者用 Prometheus 的redis_exporter。我这里分享一个实时查看 Redis 状态的常用命令redis-cli -h 127.0.0.1 -p 6379 -a AppPssw0rd_2024 --latency-history你会看到类似输出用于判断网络延迟是否正常。如果 avg 稳定在 0.1ms~0.5ms 算正常如果持续超过 5ms就要检查服务器负载、网络抖动和 Redis 慢查询了。3. Redis 核心数据类型每个场景该用哪个别再用错3.1 五大数据结构的基础与应用映射Redis 不是一个“大 Map”这么简单。它的五种核心数据结构有各自的使用姿势很多人项目里不管什么都用 string结果性能和容量都很吃亏。String字符串最经典。适合存 session token、验证码、商品库存、接口缓存。内部可以存任意二进制字节。但请注意它不是无限大单个 value 最大 512MB虽然实际不会有人这么存。做计数器时可以用INCR/DECR能做到线程安全的自增无需加锁。Hash哈希适合存对象。比如用户信息、商品详情、配置项。每个 hash 里面可以有几十个 field。为什么不用 string 去拼 JSON因为 Hash 支持单个 field 的增删改查对比幂等来说省流量、省内存。如果你要更新用户头像直接用HSET user:1001 avatar 新地址不需要把整个 JSON 取出来反序列化再写进去。List列表底层是链表或压缩列表适合做任务队列、消息队列、粉丝列表、时间线。LPUSHBRPOP是一个标准的阻塞消费模型常用于生产者消费者场景。如果你把 List 当消息队列生产端LPUSH消费端BRPOP阻塞等待能实现一个轻量可靠的队列。但要注意List 不提供消息确认机制消费者取出后崩溃消息就丢了这个和专业的 MQ 比还是有差距。Set集合无序去重适合做标签、黑白名单、共同好友还能做交集、并集、差集计算。比如运营要做“给看过A活动但没看过B活动的人推送”一个SDIFF就计算出结果。它的去重能力天然适合 UV 统计。ZSet有序集合为每个元素附加一个 score按 score 排序适合排行榜。ZADD加分数ZRANGEBYSCORE查区间ZREVRANGE取排名。秒杀系统的限流、在线用户列表、延迟队列都可以用 ZSet 做。例如延迟队列score 存执行时间戳开启一个定时任务用ZRANGEBYSCORE获取到期任务执行。实操中我经常看到一个经验不足的人把所有业务数据都丢进 string导致 keys 数量极多然后内存和序列化效率双双变差。建议按数据领域的访问模式选择结构比如一个“用户对象”用它自然应该用 Hash一个“用户的好友列表”是 Set一个“按时间排序的新闻列表”用 List一个“按热度排序的热点列表”用 ZSet。选好结构以后性能提升和内存节省是立竿见影的。3.2 高级结构Bitmap、HyperLogLog、GEO、Stream 的加分用法除了五种基础类型Redis 还有几个经常被忽略、但非常能打的特殊类型。Bitmap位图用最小存储做统计。比如记录一个用户一年 365 天是否登录用 string 类型配合 SETBIT / BITCOUNT每天只占 1 bit一年也就 46 字节/用户。一亿用户年登录状态约 46MB比用 set 或 string 存省太多。这个常用于签到、在线状态、活跃用户统计。HyperLogLog基数统计用固定 12KB 内存估算几亿不同值的数量误差在 0.81%。如果要算独立访客 UV不需要精确到个位数用 PFADD / PFCOUNT 非常完美。比如要统计今天访问页面的用户数你只需要PFADD today:uv userId1 userId2 ...最后PFCOUNT就能得到近似 UV内存占用固定不随用户数量增长。但要注意它无法反查具体有哪些用户。GEO地理位置Redis 3.2基于 ZSet 实现可以存经纬度计算两点距离以及查找“附近的人”。GEOADD添加位置GEOSEARCH按半径搜索。做附近门店、周边推荐非常实用。Stream流Redis 5.0 加入的全功能消息队列支持消费者组、确认、重试。如果你不想引入 Kafka / RabbitMQStream 一定比 List 更可靠。它有XADD写消息XREADGROUP消费XACK确认XPENDING查看未消费列表。我用它做过一套轻量任务分发系统替代了原来基于 MySQL 的状态轮询机制。对于大多数人来说掌握基础和 Bitmap / HyperLogLog 已经能在服务器资源上省不少钱。Stream 则在你准备把 Redis 当 MQ 的时候再深入。3.3 序列化方式与 key 设计的门道Redis 本身存的是字节流你怎么序列化对象直接影响性能和兼容性。常见有 JDK 原生序列化、Jackson / Fastjson、Protobuf、Kryo 等。如果你用 Spring Boot默认 RedisTemplate 的序列化器是 JdkSerializationRedisSerializer。这个有一个巨大问题序列化后的内容带了一堆类名信息体积膨胀并且在反序列化时容易出现类版本不一致。第一次用 Redis 时我就踩过这个坑——一台服务器的 JDK 版本变化缓存里的旧对象就反序列化失败。更麻烦的是JDK 序列化后二进制里面有大量自己的类结构信息用redis-cli --scan看着全是乱码也不方便调试。我现在采用“String 一层通吃”的方式Value 用 Jackson 或 Fastjson 序列化成 JSON 字符串如果哈希结构field 对应的 value 也是 JSON 字符串如果追求极致性能用 ProtoBuf 或 Kryo但要考虑到后续跨语言消费问题。不要过度优化先用方案简单、排查方便的方式等到性能瓶颈再逐热点优化。key 的命名规范我强烈建议提前统一。我一般用业务名:模块名:id[:属性]格式比如user:profile:1001 order:detail:20240101123456 cache:hot:goods:2001这样好处很多一是好排查一个扫描或一次匹配就能看到一类 key二是配合 Cluster 时哈希槽分布更合理三是避免 key 冲突四是 ACL 能按前缀精细化控制。我见过有人用无规则的 key如abc123x、xxxx2024这种项目维护到后期没人知道这个数据是谁存进去的也不敢删纯属给自己埋雷。4. 缓存治理与分布式锁线上最容易翻车的两座山4.1 缓存穿透、击穿、雪崩对症下药很多服务器扛不住大流量不是 Redis 本身不行而是使用姿势不对。常见的“缓存三兄弟”你只要开过线上大概率都遇到过。缓存穿透请求查询一个不存在的 key缓存里没有数据库里也没有每次请求都透传到数据库。如果恶意用随机 key 打你数据库会被打爆。解决方法有几种一是把空值也缓存起来设置短过期时间比如 5 分钟但注意如果大量空 key 会让 Redis 被垃圾数据占满二是用布隆过滤器先把所有可能存在的 id 过滤一遍不存在直接返回三是网关层做参数校验。我现在常见做法是空值缓存 限流兜底。缓存击穿某个热点 key 在过期瞬间大量请求同时穿透到数据库。解决手段是互斥锁或逻辑过期。互斥锁就是在缓存未命中时只让一个线程去查库并写缓存其他线程等待。逻辑过期就是不给 key 设置物理过期value 里存放过期时间后台线程专门检查并更新缓存。前者适合对一致性要求高的场景后者适合高并发读多写少的热点。缓存雪崩大量 key 同时过期或者 Redis 实例挂掉导致流量全部打到数据库数据库跟着挂。解决方式有三层过期时间加随机偏移用高可用架构避免单点主从 哨兵降级策略数据库暂时不可用时直接返回默认值或友好错误页不让请求打到底层。我做缓存治理时会先画一张“读写链路图”读请求走缓存缓存未命中走数据库写请求先更新数据库再删缓存。接下来关键难点是顺序到底是先更新数据库再删缓存还是先删缓存再更新数据库我做过总结稳妥方案是“先更新数据库再删除缓存”因为并发更新时删除缓存后哪怕有一个老请求把旧值写回缓存也会在下一次更新后删除最终一致。如果反过来先删缓存再更新数据库中间很短的时间窗口其他线程会读到数据库旧值容易不一致。4.2 分布式锁别自己造轮子但原理必须懂“Redis 分布式锁”是面试高频题也是线上实际要用的东西。最基本的版本是 SETNX 过期时间Boolean locked redisTemplate.opsForValue().setIfAbsent(lock:order:1001, value, 30, TimeUnit.SECONDS);这个命令等于“只有在 key 不存在时才能设置成功并自动过期”标准姿势。没有它的话普通 SETNX 锁能获得但永远不会释放需要手动 EXPIRE异常情况下锁就死在那了。但只靠原生命令还不够稳妥。比如锁的持有者是 A当 A 还在执行业务时锁因为超时自动释放B 拿到锁进入此时 A 执行完释放锁直接把 B 的锁释放了。这个 bug 只靠简单 SETNX 是防不住的。你必须保证释放的是自己的锁常见的做法是 value 放一个唯一 ID释放前比对确认再用 Lua 脚本保证“读取 判断 删除”是原子的。这段 Lua 脚本是标准答案if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end如果对可靠性有更高要求就要考虑 Redisson 的看门狗自动续期机制。Redisson 默认会给锁加 30 秒的 leaseTime然后起一个定时任务每隔 1/3 时间续期一次防止业务没执行完锁就过期。它还能保证释放时只释放自己的。所以生产环境我建议直接用 Redisson 封装好的 RLock而不是自己手写复杂逻辑。自己撸代码很容易漏掉续期、锁重入、主从切换后的锁丢失等问题。4.3 缓存与数据库一致性最终一致比强一致更能落地有人一听到缓存不一致就慌实际上很难做到绝对强一致。我的态度是区分业务场景。如果这笔数据对账要求极高比如账户余额就不应该只依赖缓存而是用数据库事务 可靠事件缓存只作为加速读取如果只是一般的商品库存展示先更新数据库后删缓存延迟在毫秒级完全可以接受。所谓“删缓存”我在实战中发现一个细节删除缓存前最好把删除操作发到 MQ 里由消费端统一删除。这样即使应用服务在删除缓存时崩溃MQ 里的消息还能继续执行提高最终一致的可靠性。另一个细节是缓存更新时加分布式锁防止并发更新导致覆盖旧值。我做过一个双删方案这里分享给大家更新数据库后先延迟 200ms 删除缓存再次延迟删除一次。原因是有种极端情况如果请求 A 更新数据库请求 B 读旧数据并写回缓存此时 A 删除缓存已经执行完缓存就永远保留旧值了。双删策略配合短延迟能大幅降低这种概率。上生产之前先做压测确认这个延迟不影响体验。5. 运维操作与可视化管理命令行不万能但要熟练5.1 高频命令与慢查询日志定位不要小看命令行很多问题你不登录服务器根本查不出来。我常用的几组命令列出来查看配置CONFIG GET *修改配置CONFIG SET查看服务状态INFO查看所有客户端连接CLIENT LIST杀掉某个连接CLIENT KILL查看慢查询SLOWLOG GET。高负载时第一件事就是CLIENT LIST看看是否有异常数量的连接数。曾经有一次线上告警 Redis 连接数超过了 3000我用CLIENT LIST一查全是同一个应用服务器的连接没释放马上定位到某个项目的 JedisPool 配置过小导致排队而不是 Redis 本身有问题。慢查询日志非常关键。Redis 的默认 slowlog 阈值是 10000 微秒10ms在生产上把阈值调到 5000 甚至 1000 微秒CONFIG SET slowlog-log-slower-than 5000 CONFIG SET slowlog-max-len 128然后SLOWLOG GET 20查看。如果发现大 key 操作比如HGETALL一个超大 hash或SMEMBERS一个大 set频繁出现在慢查询中你需要把它拆分成小批量操作或者调整数据结构。这是 Redis 调优最直接的手段。5.2 可视化管理工具Redis Desktop Manager 与 Another Redis Desktop Manager命令行虽好但很多人更习惯用图形界面。网上最常用的 Redis 可视化管理工具是Redis Desktop Manager简称 RDM它的正版在较新版本转成商业收费模式旧版开源。另一个社区分枝Another Redis Desktop Manager是免费开源的支持 Windows/macOS/Linux我自己是主力用这工具。它能看 key 列表、树形结构、查看 TTL、打开命令行终端、甚至看慢日志和 bulk 操作。需要提醒的是连接生产 Redis 建议先开一个只读账号用 ACL 配置再用图形工具连接避免摸到生产环境直接删 key。我吃过亏某次用 GUI 一键删除一个 pattern 里的 key把一批正在使用的前缀 key 全删了还好有 RDB 备份才恢复过来。可视化工具只是辅助操作前先想清楚自己有没有权限、影响范围多大。5.3 服务器资源监控Redis 自己的瓶颈如何发现服务器监控板块我关心几个指标内存使用率、used_memory_rss与used_memory的差距、evicted_keys、rejected_connections、blocked_clients、mem_fragmentation_ratio。used_memory是 Redis 认为它占用的内存used_memory_rss是操作系统视角分配的物理内存。如果mem_fragmentation_ratio超过 1.5说明内存碎片严重可能需要重启节点或调整activedefrag yes。如果evicted_keys持续增长说明内存超过 maxmemory正在淘汰 key这会导致缓存命中率下降数据库压力和雪崩风险上升。此时第一件事不是去加内存而是检查业务里是否有大量无用的 key或者 TTL 没设好数据无限膨胀。CLI 里我常用的快速健康检查redis-cli info stats | grep -E instantaneous_ops_per_sec|evicted_keys|rejected_connections redis-cli info memory | grep -E used_memory|used_memory_rss|mem_fragmentation_ratio redis-cli info clients | grep connected_clients5.4 主从、哨兵与集群单台服务器不是终点单实例 Redis 一旦挂掉整个服务链都会雪崩。所以生产上至少要有主从结构一台主库负责写一台从库负责读或备份。Redis 主从复制配置很直接在从库的 config 里写replicaof 主库IP 主库端口或者通过命令SLAVEOF动态指定。我见过有人一台服务器上同时跑多个 redis 实例主从复制就部署在同一台机器上——这样毫无意义主库挂了从库也挂必须要跨服务器部署才叫高可用。再往上可以使用 Sentinel哨兵监控主库自动切换。哨兵至少部署三台形成自己对主库主观下线与否的判断。如果你只想有一个稳定的缓存层不需要分片那“主从 哨兵”已经够用。Redis Cluster 则是把数据自动分片到多个节点适合单实例内存无法容纳数据的情况。这个复杂度较高新手不建议直接上先搞明白主从和持久化再说。6. 常见问题排查与避坑实录6.1 “连接超时”和 “failed to fetch”类问题排查思路我在服务器运维时经常遇到以下情况应用报Redis command timed out或类似nested exception is io.lettuce.core.RedisCommandTimeoutException偏偏这台 Redis 看着“好像活着”。出现这种问题不能只看 Redis 进程有没有在因为进程在也不代表它响应及时。第一步用redis-cli ping从本地测第二步从应用服务器上ping和telnetRedis 端口第三步看 Redis 端慢查询和 CPU 负载。常见原因有单线程执行了慢命令比如大 key、KEYS、AOF 重写瞬间阻塞、内存耗尽后触发淘汰风暴、网络带宽被打满甚至 Redis 所在服务器负载过高被其它进程抢占了 CPU。这些都可能造成“连接能建但命令超时”。我处理过一个案例一台 Redis 部署在虚拟机上线上时不时报超时进一步排查发现宿主机上其它虚拟机占用大量网络 I/O导致这台 Redis 的网络数据包延迟飙升。当时定位到核心指标是redis-cli --latency出现几百毫秒的尖刺。如果你排查完这些都正常再看应用端连接池配置。Lettuce 的默认超时时间是 2 秒太短也会造成误报合理设置 3 到 5 秒之间更稳妥。6.2 内存暴涨与 OOM优先检查数据结构和过期策略Redis 内存持续增长时别急着FLUSHALL。先用redis-cli --bigkeys扫一遍它会把最大的 key 找出来通常是某个 Hash / Set / String 过于巨大导致某次操作阻塞和内存暴涨。如果一个 key 中的数据量达到几十万甚至百万级别建议拆分成多个小 key或者改用 Stream / List 分批处理。还有一个容易被忽略的是“大 key 过期”如果大 key 设置了过期时间Redis 在惰性删除或定期删除时一次性释放大量内存会导致服务器内存使用率瞬间冲高甚至引起主线程卡顿。Redis 4.0 之后可以用unlink替代del异步释放内存。对于所有超大 key能拆就拆实在不能拆至少要用UNLINK删除千万别用DEL。如果 OOM 是因为内存碎片高还需要关注activedefrag yes配置。它可以在服务运行时整理碎片但有一定的 CPU 消耗。你可以在低峰期开一段观察效果再决定长期使用。6.3 主从复制延迟与分裂问题主从架构也不是铁板一块。复制延迟、脑裂问题仍然常见。如果主库写入量极大而网络又差从库的repl_backlog被覆盖可能导致从库需要FULLRESYNC重新全量复制期间从库不可用。此时可以调大repl-backlog-size比如从默认 1MB 改成 32MB 或更大覆盖低峰期的同步积压减少全量重同步次数。另一个常见问题主从切换后旧主库重新上线变成从库但它含有新主库没有的旧数据。Redis 会判断主从复制进度如果冲突通常以新主库为准。但如果你把 Redis 的 AOF 文件到处拷贝恢复有可能引入旧数据覆盖新数据的问题。我在做容灾演练时都会先强制SLAVEOF重新全量同步再开放对旧库只读避免脑裂后数据打架。另外注意主从复制是异步的如果主库刚写成功就宕机这条数据可能没复制到从库。这种场景下分布式锁不要只依赖 Redis 主从可以考虑 RedLock 或直接引入强一致组件比如 etcd / ZooKeeper。但 RedLock 本身也有争议这里不展开我只想提醒一句锁本身就是一秒级或毫秒级的需求不要过度设计除非业务真的无法接受锁丢失。6.4 常见问题速查表问题现象排查方向常用解决措施连不上 Redis端口不通防火墙、bind 配置、网络策略检查telnet ip port放行安全组/入站规则命令超时ping 通但不响应慢查询、大 key、CPU 负载、网络延迟查SLOWLOG恢复大 key调大客户端超时内存占用持续增长key 堆积、无 TTL、大 key--bigkeys扫描设置合理过期拆分大 key缓存穿透导致数据库被打查询不存在 key 的恶意流量空值缓存 布隆过滤器 接口限流缓存雪崩大量 key 同时过期过期时间加随机数集群高可用降级开关主从切换后数据不一致异步复制、脑裂确保配置min-replicas-to-write强制全量重同步使用KEYS阻塞实例误用高频命令用SCAN替换设置rename-command KEYS 图形工具误删 key权限过大ACL 只读账号删除前备份慢查询频率高命令设计不合理分布式批量操作pipeline拆分大数据结构7. 一点私人体会我在服务器上折腾 Redis 这么久最大的感受是它很容易上手但极难“驾驭好”。很多人装上、用起来觉得一切正常直到线上遇到一次内存雪崩或者锁失效才会意识到自己之前忽视了多少小细节。比如maxmemory从来不做规划比如持久化策略随便选比如可视化工具一把梭这些都是我踩过的坑。另外一个经验是Redis 的版本升级别太保守。我从 3.x 一路用下来4.0 的异步删除、5.0 的 Stream、6.0 的 SSL 与 ACL、7.0 的 Function这些新特性实实在在地解决了很多老版本“能跑但不好用”的问题。只要你有经过测试的升级流程尽量保持较新的稳定版。最后再分享一个小技巧在服务器上给 Redis 留一个“逃生舱”。我会准备一个独立的脚本紧急时可以直接redis-cli shutdown nosave快速重启并同时保留最近的 RDB 文件。真遇到内存卡死或锁问题快速恢复永远比慢慢分析更重要。等你恢复完业务再回头慢慢看日志。这个思路适用于任何线上中间件Redis 尤甚。服务器是脆弱的但服务不能跟着脆弱合理的设计和谨慎的运维能让你睡个好觉。
返回列表