ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Redis主从复制原理与Docker实战:全量/增量同步及故障排查

Redis主从复制原理与Docker实战:全量/增量同步及故障排查 1. 主从复制到底在解决什么问题先说结论Redis 主从复制replication本质上是把一台实例上的写命令流按顺序、可重放地同步到一台或多台实例上让多份内存数据保持一致。它不是什么黑魔法也不依赖共享存储就是一套快照 命令流的组合拳。第一次接触这个概念的人容易把它和大数据领域的副本机制混在一起其实 Redis 这套设计非常土味、非常直接主库把 RDB 文件丢给从库之后每一条写命令都转发一遍就这样。这套机制撑起了三件事一是数据冗余主库那台机器磁盘挂了、内存炸了从库还有一份完整数据二是读写分离把读流量分到从库上主库专心写QPS 能拉高好几倍三是高可用地基没有复制就没有哨兵Sentinel和 Cluster 的故障自动切换所有主挂了自动顶上去的承诺都是空话。所以别看主从复制自己不带自动故障转移它却是整个 Redis 高可用体系里最底层的那块砖。这篇文章面向的读者很具体你可能是刚学完redis 数据类型、装完redis desktop manager、跑通了一个单机实例接下来想搞清楚为什么生产环境非要搞两台以上也可能是被redis面试题里的说说主从复制的原理问懵了只会背全量同步、增量同步六个字。我会从协议交互细节讲到 Docker 里搭三节点的完整操作再把你线上大概率会遇到的坑摊开讲。看完你至少能做到两件事自己能搭一套能用的主从以及看到master_link_status:down时知道往哪个方向查。1.1 单机 Redis 的三道坎单机 Redis 的问题不在于性能不够而在于它把所有的赌注都押在一台机器和一个进程上。第一道坎是容量Redis 是内存数据库单机内存受物理内存限制一台 64G 的机器你撑死给它 40G 左右的数据量再往上 fork 和 RDB 就会很难受。第二道坎是吞吐单线程模型下所有命令排队执行读多写少的场景里大量 CPU 时间浪费在无意义的读操作上。第三道坎最要命是可用性进程 OOM 被杀、机器断电、内核 panic任何一个环节出问题服务就直接不可用而且数据可能只剩上次 RDB 或 AOF 落盘的快照。主从复制对这三道坎的回应是分别的容量问题它解决不了每台从库都是全量副本不是分片吞吐问题它能解决一大半读请求分散到 N 台从库理论读能力乘以 N可用性问题它提供了有人能接班的前提但接班的动作得靠哨兵或者你自己写脚本。我见过不少团队在项目初期直接上 Cluster结果发现数据量才 5G、QPS 才两千白白背上了分片带来的运维复杂度。这种场景老老实实一主两从加哨兵省事得多。顺带说一句很多人会搞混的主从不是备份。从库上执行的FLUSHALL会被原样同步到所有从库主库误删数据从库跟着一起没。真正的备份是定期把 RDB 文件拷到另一台不参与复制的机器或对象存储上这件事别偷懒。1.2 复制、哨兵、集群的分工边界刚上手的人最容易把这三个概念搅成一锅粥。我习惯这样给同事解释复制Replication负责数据有几份只管同步不管谁死谁活。哨兵Sentinel负责谁挂了谁顶上它自己也是一组进程监控主从节点主库失联后选一个从库提升为新主然后通知其他从库改跟随。集群Cluster负责数据怎么分片把 16384 个哈希槽分给多个主节点每个主节点下面还能挂从库做冗余。三者的关系是层层叠加的Cluster 内部也用复制来实现主从冗余Sentinel 完全建立在复制之上。所以你把复制原理吃透后面两个都是顺藤摸瓜。提示不要用 Sentinel 去管理 Cluster 节点也不要在 Cluster 模式下用REPLICAOF手动改拓扑Cluster 的槽位和复制关系由集群自己维护手动干预极容易搞出数据不一致。还有一点值得提前说主从复制是异步的。主库执行完写命令、返回客户端成功然后才把命令发给从库。这意味着主库在返回成功到命令真正到达从库之间存在一个时间窗口如果此时主库宕机且没有做持久化这条数据就永远丢了。这不是 Redis 的缺陷是异步复制的固有代价理解了这一点你才能看懂后面讲的WAIT命令和min-replicas-to-write参数到底在防什么。2. 复制链路是怎么建立的从 REPLICAOF 到 PSYNC讨论协议之前先约定叫法。Redis 5.0 之前命令叫SLAVEOF节点叫 slave5.0 之后官方全面改用REPLICAOF和 replica老命令还保留兼容。但你在INFO replication的输出里看到的字段名依然是slave0:、slave1:这是历史包袱别以为是版本装错了。下面我统一用从库/replica。整条链路建立起来分两大阶段握手建连和数据同步。数据同步里又分全量同步full resynchronization和增量同步partial resynchronization具体走哪条路由从库断线前记住的 offset 决定。2.1 建连与握手PING、AUTH、REPLCONF从库在配置里写了replicaof 192.168.1.10 6379之后或者运行时执行了这条命令会立刻做这几件事顺序很固定保存主库的 IP 和端口建立 TCP 连接。发送PING期待主库回PONG。如果超时或者回的是别的说明网络或端口有问题重连。如果主库配了requirepass从库必须配masterauth它会发AUTH password认证。从库发送自己的监听端口REPLCONF listening-port 6380主库记录下来INFO replication里显示的port就是这个值。Redis 4.0 之后还会发REPLCONF capa eof capa psync2告诉主库自己支持无盘传输的 EOF 标记和 PSYNC2 协议。这几个步骤看着很啰嗦但每一步都有存在的理由。REPLCONF listening-port尤其关键——主库需要通过这个端口去连从库如果你在 Docker 或者 NAT 环境里没把端口映射对主库记下来的是容器内端口外部根本连不上后面做故障转移时哨兵会一脸懵。我踩过这个坑容器里从库上报listening-port 6379主库和哨兵都按 6379 去连结果连到的是主库自己。握手完成后进入真正的同步协商从库发PSYNC replid offset第一次连接时是PSYNC ? -1意思是我没有历史给我全量。主库收到后有两种回复FULLRESYNC replid offset走全量同步replid是主库当前复制流的 ID40 位十六进制字符串offset是主库当前的复制偏移量。CONTINUE replid走增量同步只需要把断线期间丢失的那部分命令补发。2.2 全量同步RDB 快照与复制缓冲区全量同步是整个流程里最重的环节也是最容易出问题的地方。主库收到PSYNC ? -1之后的动作是执行BGSAVE或者BGREWRITEAOF如果 AOF 正在重写会等它结束生成 RDB 快照文件。同时开启一个复制缓冲区replication buffer把 BGSAVE 期间新进来的所有写命令都追加进去。RDB 生成完毕后把文件通过网络发给从库。从库收到后先落盘再清空自己的旧数据然后载入 RDB。载入完成后主库把复制缓冲区里堆积的命令发过去从库执行至此双方数据追平进入稳定的命令传播阶段。第二步是很多人忽略的细节BGSAVE 可能要跑几秒甚至几十秒这期间主库不能停止服务新写入的命令必须先缓存起来否则从库拿到的就是一份过期快照。这部分缓存占用的就是复制缓冲区内存如果从库接收 RDB 的速度太慢缓冲区会一直涨涨到超过client-output-buffer-limit replica的限制主库会强制断开这个从库然后从库重连、再来一次全量同步形成恶性循环。RDB 的传输方式在 Redis 7.x 里默认变成了无盘模式repl-diskless-sync yes。有盘模式是主库先把 RDB 写到磁盘再读文件发送无盘模式是 BGSAVE 的子进程直接把 RDB 字节流写进 socket省掉一次磁盘 IO。无盘模式对大内存实例特别友好因为省下了几 GB 的临时磁盘文件但也有代价它会让主库多开一个 socket 连接repl-diskless-sync-delay默认 5 秒是为了等更多从库一起来好让一次 RDB 喂给多个从库——这个延迟在小规模场景下会让人误以为从库怎么半天不同步。从库载入 RDB 期间是阻塞的这期间的读请求会被挂起。如果你用的是repl-diskless-load swapdb从库会先把数据加载到一块临时空间加载完再原子替换减少阻塞时间但会额外占用一份内存。生产环境里从库的规格不要比主库小太多否则加载 RDB 那几分钟你的读流量会全部超时。2.3 增量同步replid 与 offset 的账本增量同步是 Redis 2.8 引入 PSYNC 之后才有的能力在这之前断线重连只能全量非常痛苦。它的核心是两个变量replid和offset。replid主库的复制流标识。主库每次重启或者被提升为新主都会重新生成一个 replid从库看到 replid 变了就知道换人了账本作废。offset复制流的字节偏移量。主库每传播 N 个字节的命令offset 就加 N从库每收到 N 字节自己的 offset 也加 N。两边一对比就知道差多少数据。光有两个变量还不够主库还得把最近发出去的这些字节存着这就是复制积压缓冲区repl backlog由repl-backlog-size控制默认只有 1MB注意单位是 MB 不是 GB。从库断线重连时发PSYNC replid offset主库检查replid 对得上并且 offset 之差小于 backlog 里现存的数据量回CONTINUE只补发差值部分。其他情况一律FULLRESYNC全量重来。所以repl-backlog-size这个参数直接决定了从库短暂抖动一下需不需要全量重同步。默认 1MB 在高写入场景下几乎等于没有网络抖两秒就撑爆了。这也是后面第 3 章我要专门算一遍的原因。Redis 4.0 把协议升级到了 PSYNC2解决了主从切换后无法增量同步的老问题。老版本里主库一重启replid 变了所有从库必须全量PSYNC2 引入了replid2和second_replid_offset让从库在新主库上仍有机会走增量。这个改进在哨兵故障转移时价值巨大因为转移过程中最怕的就是新主刚上来所有从库排队全量同步把新主压垮。2.4 心跳、超时与延迟判定数据同步完之后链路不会闲着双方一直在对暗号靠的就是心跳主库每隔repl-ping-replica-period默认 10 秒给从库发一个PING确认连接还活着。从库每秒给主库发一次REPLCONF ACK offset汇报自己处理到哪儿了。主库如果repl-timeout默认 60 秒内没收到从库的 ACK就判定从库下线断开连接从库同理超时没收到主库数据就认为主库挂了master_link_status变成down。这里有个很隐蔽的坑repl-timeout管的东西不止心跳还包括 RDB 传输、命令传播等多个阶段的超时。如果你的实例内存很大、RDB 有几 GB在慢网络下传 60 秒根本传不完主库会误判超时然后反复断开重传永远同步不上。这种情况不是加大repl-timeout就完事的更该做的是排查网络带宽、考虑换无盘模式或者干脆在从库本地做一份 RDB 冷启动。INFO replication里的lag字段是从库视角看的延迟含义是距离上次收到主库数据过了几秒正常值应该是 0 或 1。如果它持续大于 3说明链路有压力。更精确的延迟要用 offset 差值来算master_repl_offset - slave_repl_offset这个差值乘以平均命令字节数大致就是落后的数据量。做监控的时候我一般两个都采lag 反映的是卡不卡offset 差反映的是差多少。3. Docker 三节点主从实操从零到跑通纸上谈兵没意思这一章我们把一主两从在本地 Docker 里完整搭一遍。选 Docker 不是因为它是唯一方案而是因为它能让你在五分钟内拥有一套可反复销毁重建的环境练习故障场景特别方便。docker安装redis主从这个关键词能搜出一堆教程但大多数只告诉你跑起来不告诉你为什么这么配也不告诉你哪些参数上线前必须改。3.1 环境准备与配置文件先拉镜像用 7.2 这个版本稳定且默认参数比较现代docker pull redis:7.2然后准备三个配置文件放在./conf下分别是master.conf、replica1.conf、replica2.conf。为什么不直接用docker run redis-server --replicaof ...这种命令行传参因为生产里你要配的东西远不止一个replicaof用配置文件能让你把参数固化下来也方便版本管理。master.confport 6379 bind 0.0.0.0 protected-mode no appendonly yes appendfsync everysec requirepass redis123456 masterauth redis123456 repl-backlog-size 64mb repl-backlog-ttl 7200 min-replicas-to-write 1 min-replicas-max-lag 10replica1.confport 6380 bind 0.0.0.0 protected-mode no appendonly yes appendfsync everysec requirepass redis123456 masterauth redis123456 replicaof redis-master 6379 replica-read-only yes replica-serve-stale-data yes replica-priority 100 repl-backlog-size 64mb几个配置项值得单独解释。masterauth从库必须配不然主库有密码时握手直接失败报NOAUTH Authentication required这个错误在日志里一点都不明显很多人查半天以为是网络问题。protected-mode no只在容器这种受控网络里开物理机上千万别这么干。replica-read-only yes是默认值但显式写出来提醒自己从库默认拒绝写命令这是保护机制不是安全边界有CONFIG SET权限的人照样能关掉它。min-replicas-to-write 1和min-replicas-max-lag 10这两个参数是主库侧的保险丝意思是至少有 1 个从库、且它的延迟不超过 10 秒我才接受写请求否则直接报错。它的作用是避免主库网络分区变成孤岛之后还在傻傻接写等网络恢复把脏数据同步下去。注意这两个参数一开从库全部掉线时主库会直接拒写业务会报错。上线前一定要和业务方确认这个行为可接受或者把它当成降级开关通过配置中心动态调整。3.2 启动编排与状态验证用 docker-compose 一把梭docker-compose.yml内容如下version: 3.8 services: redis-master: image: redis:7.2 container_name: redis-master ports: - 6379:6379 volumes: - ./conf/master.conf:/usr/local/etc/redis/redis.conf - ./data/master:/data command: [redis-server, /usr/local/etc/redis/redis.conf] redis-replica1: image: redis:7.2 container_name: redis-replica1 ports: - 6380:6380 volumes: - ./conf/replica1.conf:/usr/local/etc/redis/redis.conf - ./data/replica1:/data depends_on: - redis-master command: [redis-server, /usr/local/etc/redis/redis.conf] redis-replica2: image: redis:7.2 container_name: redis-replica2 ports: - 6381:6381 volumes: - ./conf/replica2.conf:/usr/local/etc/redis/redis.conf - ./data/replica2:/data depends_on: - redis-master command: [redis-server, /usr/local/etc/redis/redis.conf]replica2.conf把 port 换成 6381、replicaof指向同一个主库即可内容我就不重复贴了。启动docker compose up -d docker compose ps三个容器都 Up 之后开始验证。先看主库docker exec -it redis-master redis-cli -a redis123456 info replication期望看到role:master connected_slaves:2 slave0:ip172.20.0.3,port6380,stateonline,offset1234,lag0 slave1:ip172.20.0.4,port6381,stateonline,offset1234,lag0 master_failover_state:no-failover master_replid:8f3a1c... master_repl_offset:1234stateonline是关键如果是statewait_bgsave说明 RDB 还在生成等几秒再刷如果connected_slaves:0八成是masterauth没配对或者网络不通。再看从库docker exec -it redis-replica1 redis-cli -a redis123456 info replicationrole:slave、master_link_status:up、master_last_io_seconds_ago:0这三项对了就说明链路健康。写条数据验证同步docker exec -it redis-master redis-cli -a redis123456 set hello from-master docker exec -it redis-replica1 redis-cli -a redis123456 get hello docker exec -it redis-replica2 redis-cli -a redis123456 get hello两个从库都能读到from-master最基础的主从就通了。这时候你再试试从库写入docker exec -it redis-replica1 redis-cli -a redis123456 set test 1 # (error) READONLY You cant write against a read only replica.这个报错是对的说明replica-read-only生效了。3.3 断线重连与手动切换演练搭完不练等于白搭我建议至少做三个演练。第一个是验证增量同步。先把某个从库的网络断掉模拟短暂抖动docker network disconnect redis_default redis-replica1 docker exec -it redis-master redis-cli -a redis123456 set k1 v1 docker exec -it redis-master redis-cli -a redis123456 set k2 v2 docker network connect redis_default redis-replica1Docker 的 bridge 网络名一般是目录名_default用docker network ls查一下。重连后立刻看从库的INFO replication重点看sync_full和sync_partial_ok两个计数器docker exec -it redis-replica1 redis-cli -a redis123456 info stats | grep sync # sync_full:0 # sync_partial_ok:1sync_full:0且sync_partial_ok:1说明走的是增量同步backlog 起作用了。如果你看到sync_full涨了那就是断线时间太长或者 backlog 太小差值已经超出缓冲区范围。第二个演练是手动故障转移。假设主库真的挂了我们手动把 replica1 提升为新主docker stop redis-master docker exec -it redis-replica1 redis-cli -a redis123456 replicaof no one docker exec -it redis-replica1 redis-cli -a redis123456 info replication # role:master然后让 replica2 改跟新主docker exec -it redis-replica2 redis-cli -a redis123456 replicaof redis-replica1 6380注意这一步replicaof运行时执行不会落盘到配置文件容器重启就丢了。生产里做这类变更必须同步改配置文件。第三个演练是观察全量同步的开销。删掉一个从库的 data 目录再重启让它从头全量同步同时盯着主库的INFO stats里的rdb_bgsave_in_progress和latest_fork_usec。你会发现 fork 那一瞬间主库是阻塞的latest_fork_usec会飙到几百微秒甚至毫秒级内存越大越明显。这就是为什么我前面强调从库重建要错峰别三个从库同时重启。3.4 关键参数的量级估算前面反复提到repl-backlog-size默认 1MB 太小现在来算一遍该怎么定。逻辑很简单backlog 容量 主库写入速率 × 可容忍的最长断线时间举个数主库 QPS 5000其中写命令占 40% 也就是 2000 条/秒平均每条命令含命令名、参数、协议头约 200 字节那么写入速率约 400 KB/s。假设你希望从库断线 60 秒内还能增量恢复需要的容量是 400 KB/s × 60 s 24 MB。考虑到协议开销的波动和突发流量取 2 到 3 倍余量配 64MB 比较稳。这不是拍脑袋是按峰值估算再留冗余。如果你不确定写入速率可以用这个命令粗算redis-cli -a redis123456 info stats | grep instantaneous_input_kbpsinstantaneous_input_kbps是最近一次采样周期内的入站速率采样周期由INFO调用间隔决定。它只反映瞬时值做容量规划时要抓业务高峰期的数据多采几次取最大值。其他几个参数的经验值我整理成表方便你直接抄参数默认值生产建议说明repl-backlog-size1mb64mb 起按写入速率和容忍断线时间算高写入场景可到 256mbrepl-backlog-ttl36007200从库全断后 backlog 保留多久太短会导致重连必须全量repl-timeout6060-120大实例慢网络适当加大但要和网络抖动情况匹配repl-ping-replica-period1010改小会增加心跳开销一般不动repl-diskless-sync-delay50-5从库少且启动分散时设 0避免等待client-output-buffer-limit replica256mb/64mb/60512mb/128mb/60全量同步期间从库太慢会被踢大实例要放宽最后一行是很多线上事故的根因。默认硬限制 256MB一个 5000 万 key、写入又猛的主库在给慢从库传 RDB 时复制缓冲区几秒钟就能顶到 256MB然后主库主动断链从库重连再全量无限循环。加大这个限制是权宜之计根子上要看从库为什么慢——是磁盘 IO 不行还是网络带宽不够。4. 踩坑实录常见故障与排查手册这一章是我这些年攒下来的问题清单按症状 → 排查方向 → 解决的顺序写你可以当成速查表用。4.1 反复全量同步的定位思路最常见的症状是从库看起来连上了但过一会儿又断INFO stats里sync_full一路涨主库的 RDB 生成日志刷个不停。这个问题的排查顺序应该是第一步确认是网络抖动还是容量不足。看从库日志里断连的时间间隔如果规律性地每隔几十秒断一次多半是repl-timeout或者缓冲区溢出如果是随机的更可能是网络。第二步量 offset 差值。用master_repl_offset - slave_repl_offset算落后字节数和repl-backlog-size一比就知道有没有超。超了就把 backlog 调大。第三步看主库是否踢过从库。主库日志里搜Client ... scheduled to be closed ASAP for overcoming of output buffer limits有这句就是输出缓冲区超限。第四步看从库加载 RDB 的耗时。从库日志会有MASTER - REPLICA sync: Loading DB in memory和Done loading RDB两条中间的时间差如果超过repl-timeout的一半建议用无盘加载repl-diskless-load swapdb或者提升从库规格。还有一个容易漏的点主库是不是在频繁做 RDB。如果你既有save策略又有从库全量同步两个 BGSAVE 撞一起fork 两次主库会非常卡。正确做法是把从库同步触发的 RDB 和定时 RDB 错开或者干脆在主库上关掉定时 RDB让从库承担持久化职责。4.2 典型故障速查表我把高频问题整理成一张表遇到问题先对号入座现象可能原因处理方式master_link_status:down网络不通、端口映射错、masterauth缺失用telnet测端口检查两侧密码配置NOAUTH Authentication required主库有requirepass从库没配masterauth从库补上masterauth并重启从库读不到数据从库在replica-serve-stale-data no下与主库失联恢复链路或临时改配置允许读旧数据READONLY You cant write against a read only replica应用误连从库做写操作检查客户端路由配置写请求走主库sync_full持续增长backlog 太小、从库处理慢、网络抖动加大repl-backlog-size排查从库性能lag持续大于 5从库负载高、网络带宽不足、主库写入过猛排查从库慢查询、加带宽、考虑级联复制主库 fork 时间过长内存大、页表大、透明大页未关关闭 THP控制单实例内存不超过 16-20GB从库内存暴涨repl-diskless-load swapdb造成双份数据关掉该选项或扩容主从数据不一致从库曾被写入、主库误操作被同步用redis-cli --bigkeys等工具比对重建从库关于透明大页THP这是 Linux 上的一个老坑。THP 会把内存页从 4KB 合并成 2MBRedis 的 fork 走的是写时复制页越大复制的代价越高fork 耗时会从毫秒级变成几百毫秒。关掉的方式是echo never /sys/kernel/mm/transparent_hugepage/enabled这条命令我每次部署新机器都要执行一遍写进初始化脚本里别指望运维记得。4.3 生产环境的几条经验除了参数和排错还有些只有踩过才知道的东西。别让从库承担唯一备份。我见过团队把从库当成备份策略结果主库误执行了FLUSHALL一分钟内所有从库全空了。备份必须是独立的、离线的、有版本历史的。从库只能算副本。读写分离不要生搬硬套。写后立即读的场景比如用户下单后马上查订单状态走从库会读到旧数据这类请求必须强制走主库。Lettuce 客户端里有ReadFrom.MASTER、ReadFrom.REPLICA_PREFERRED这类策略Spring Boot 里可以通过LettuceClientConfiguration配置。我的做法是默认全部走主库只对明确能容忍延迟的查询比如排行榜、统计报表开从库读宁可少省点资源也别让业务同学半夜排查数据怎么对不上。级联复制可以救命但不能滥用。一主挂八从的时候主库要为每个从库维护一份输出缓冲全量同步时 fork 一次就要喂八个。这时候可以让两三个从库直接跟主库剩下的跟从库即从库的从库。代价是多一跳延迟而且链路越长越容易断。我的经验是一层足够别搞三层。监控必须采这几项。光看 Redis 进程活着没用要采master_link_status、master_repl_offset差值、sync_full增量、latest_fork_usec、connected_slaves数量。前两个反映健康度后三个反映成本和风险。用 Prometheus redis_exporter 都能拿到配个告警规则offset 差值超过阈值或者sync_full五分钟内涨了两次就报警。升级版本的时候先看协议变化。Redis 6.0 引入了多线程 IO但复制链路还是单线程处理的7.0 把复制缓冲区和 backlog 的内存模型改成了共享结构多个从库共用一份 backlog内存占用大幅下降。升级前值得读一遍 release notes 里 Replication 那一段能省掉很多怎么升级完内存掉了的困惑。5. 数据一致性与延迟应用该怎么写原理和运维讲完了最后聊聊应用侧。毕竟主从复制最终是要服务于业务代码的代码写不对前面配得再漂亮也白搭。5.1 复制延迟的度量与监控延迟不是有或没有的问题是多少的问题。测量延迟有两个维度字节维度就是master_repl_offset - slave_repl_offset。这个值最直观但要注意它只反映还没处理的字节数不等于业务上感知的数据条数差异。要换算成条数得再除以平均命令大小。时间维度从库上的master_last_io_seconds_ago和lag字段。前者的含义是距离上次收到主库数据过了几秒后者是主库视角的最后一次 ACK 距今秒数。正常应该都是 0 到 1。想精确测端到端延迟可以这么干在主库上写一个带时间戳的 key然后在从库上轮询读读到之后用当前时间减时间戳。这个方法我用过很多次比看 INFO 的计数器更贴近真实业务感受因为它把命令传播、从库排队、网络往返全算进去了。# 主库 redis-cli -h master -a pass set delay:probe $(date %s%3N) # 从库 redis-cli -h replica -a pass get delay:probe监控的告警阈值我一般设成这样slave_repl_offset差值超过 5MB 持续 30 秒报警master_link_status变成 down 立即报警sync_full十分钟内增长超过 1 次报警。这三个覆盖了绝大多数异常。5.2 读写分离的坑与兜底策略最后说说读写分离真正落地时的问题。理论上写走主、读走从很简单实际上一堆细节连接池要分开配。主库的连接池和从库的连接池不能共用因为主库要为每个从库开一个复制连接从库还要处理读请求混在一起容易互相挤占。Spring Boot 里常见做法是配两个LettuceConnectionFactory用Primary标记主库的那个再配一个只读的供特定场景注入。事务里的读必须走主库。Redis 事务MULTI/EXEC和 Lua 脚本里的读操作如果被路由到从库可能读到主库还没同步过来的中间状态导致逻辑错误。这类操作的封装层一定要硬编码走主库。延迟敏感的业务要有降级。从库挂了的时候如果读请求全部失败用户体验会很差。可以做一层兜底从库读超时自动回落到主库读代价是主库压力上升但至少服务不中断。这个逻辑别写死在客户端库里放在业务层的 Repository 里更好控制。别用从库做限流和计数。分布式限流的计数器、秒杀库存这类强一致需求的场景必须走主库。从库的延迟意味着你的计数器可能不准导致超卖或者限流失效。用 Lua 脚本做原子操作的场景尤其要注意脚本本身会在主库执行但如果你读的数据是从库的就是两回事了。从库数量不是越多越好。每加一个从库主库就多一份输出缓冲、多一次网络分发。三个从库和八个从库的差别在写入高峰期会非常明显。我的经验是单主挂三个从库是舒服的区间再多就上级联或者考虑 Cluster 分片。提示从库做只读查询时记得避开KEYS、FLUSHALL这类危险命令。生产环境用rename-command把它们改掉或者禁用比事后追责有用得多。这套东西我在几个日活百万级的项目里跑过从最早的一主一从手动切换到现在一主三从加哨兵自动切中间踩的坑基本都在上面了。主从复制本身不难难的是把参数调到和自己的业务量匹配把监控做到能提前发现问题把应用侧的路由规则写对。这三件事做好了Redis 的可用性会有质的提升。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表