Redis Sentinel 高可用方案在WMS仓储管理系统的应用

Redis Sentinel 高可用方案在WMS仓储管理系统的应用

一、仓储场景的特殊挑战

在WMS(Warehouse Management System)系统中,Redis承载着高频且关键的业务数据:

业务模块 Redis用途 可用性要求
库存实时缓存 SKU库存量、库位占用状态 99.99% - 断货/超卖直接损失
波次/任务调度 拣货任务队列、锁机制 高 - 任务丢失导致作业停滞
设备指令通道 AGV/堆垛机指令下发与状态回传 极高 - 设备碰撞风险
会话与权限 仓内PDA/RF枪登录状态 中 - 可快速重建

核心痛点:传统单节点Redis在仓库24小时作业模式下,任何故障都可能导致:

  • 库存数据不一致 → 账面与实物不符
  • 任务队列中断 → 产线停线
  • 设备失控 → 安全事故

二、Sentinel架构选型决策

2.1 为什么不是Cluster?

在WMS场景中,数据特征决定了架构选择:

复制代码
WMS数据特征:
├── 数据规模:单个仓库SKU通常在10万级,非海量(<10GB)
├── 访问模式:高并发读写,但无大规模分片需求  
├── 数据结构:大量Hash(库存)、List(任务队列)、Pub/Sub(设备通信)
└── 一致性要求:强一致性优先于分区容错(AP→CP倾向)

Sentinel vs Cluster 决策矩阵

维度 Sentinel Cluster
数据规模 <100GB,单节点可承载 >100GB必须分片
数据结构复杂度 支持全部Redis特性 多键操作受限(跨slot)
故障转移速度 秒级(典型3-10s) 秒级,但涉及slot迁移更复杂
运维复杂度 低,类似MySQL MHA 高,需处理reshard/rebalance
WMS适配度 首选 数据量未达阈值

2.2 最小高可用拓扑

复制代码
┌─────────────────────────────────────────┐
│           仓库数据中心                   │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ Sentinel│  │ Sentinel│  │ Sentinel│  │  奇数节点,防止脑裂
│  │   26379 │  │   26380 │  │   26381 │  │
│  └────┬────┘  └────┬────┘  └────┬────┘  │
│       │              │              │     │
│       └──────────────┼──────────────┘     │
│                      │                     │
│              ┌───────┴───────┐             │
│              ▼               ▼             │
│        ┌─────────┐     ┌─────────┐        │
│        │  Master │◄────►│  Slave │        │
│        │  6379   │  同步  │  6380   │        │
│        └────┬────┘     └────┬────┘        │
│             │               │              │
│        ┌────┴────┐     ┌────┴────┐        │
│        │  Slave  │     │  Slave  │        │  级联复制,减少主库压力
│        │  6381   │     │  6382   │        │
│        └─────────┘     └─────────┘        │
└─────────────────────────────────────────┘

三、WMS场景化配置详解

3.1 Redis节点配置(redis.conf

ini 复制代码
# ============================================
# WMS生产环境 - Master节点配置
# ============================================

# 1. 基础配置
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile /var/log/redis/redis_6379.log
dir /data/redis/6379

# 2. 持久化策略(WMS必须开启,防止故障丢数据)
# RDB:用于快速全量恢复(每日凌晨低峰期生成)
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb

# AOF:用于实时持久化(每秒刷盘,平衡性能与安全)
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec  # WMS场景:everysec是性能与安全的平衡点
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 3. 复制配置(Slave节点需配置replicaof)
# replicaof 192.168.1.10 6379

# 4. 高可用相关
# 主库必须配置密码,Sentinel需要认证
requirepass WmsProd_2024_Secure
masterauth WmsProd_2024_Secure

# 客户端输出缓冲区(防止Slave同步时阻塞)
client-output-buffer-limit slave 256mb 64mb 60

# 5. WMS业务优化
maxmemory 8gb
maxmemory-policy allkeys-lru  # 库存数据有访问热度差异,LRU合理
maxmemory-samples 10

# 6. 慢查询监控(WMS核心指标)
slowlog-log-slower-than 10000  # 10ms,WMS任务响应敏感
slowlog-max-len 128

# 7. 安全加固
rename-command FLUSHALL "WMS_FLUSHALL_9x7d"  # 防止误操作
rename-command FLUSHDB "WMS_FLUSHDB_3k2m"

3.2 Sentinel节点配置(sentinel.conf

ini 复制代码
# ============================================
# WMS生产环境 - Sentinel配置(3节点相同,仅需调整端口)
# ============================================

port 26379
daemonize yes
logfile /var/log/redis/sentinel_26379.log
dir /tmp

# 监控主库:mymaster是WMS业务统一的连接标识
# 格式:sentinel monitor <master-name> <ip> <port> <quorum>
# quorum=2:3个Sentinel中至少2个同意才进行故障转移
sentinel monitor mymaster 192.168.1.10 6379 2

# 认证(必须与Redis主库密码一致)
sentinel auth-pass mymaster WmsProd_2024_Secure

# 故障判定阈值(WMS场景调优)
# down-after-milliseconds:判定主库下线时间
# WMS建议:5秒,太短易误判网络抖动,太长影响故障恢复
sentinel down-after-milliseconds mymaster 5000

# 故障转移超时
# 180s:包含从库晋升、配置传播、旧主库重启加入等全流程
sentinel failover-timeout mymaster 180000

# 并行同步从库数:1(WMS从库通常2-4个,串行减少主库瞬时压力)
sentinel parallel-syncs mymaster 1

# 通知脚本(对接WMS告警系统)
sentinel notification-script mymaster /opt/wms/alert/notify.sh

# 客户端重配置脚本(可选,用于DNS刷新或配置中心推送)
sentinel client-reconfig-script mymaster /opt/wms/alert/reconfig.sh

# 安全加固
requirepass Sentinel_Wms_2024

四、WMS业务层接入方案

4.1 连接层设计:Sentinel-aware客户端

Java(Spring Boot + Lettuce)配置示例

java 复制代码
@Configuration
public class WmsRedisConfig {
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // 1. 配置Sentinel节点(无需知道当前谁是主库)
        RedisSentinelConfiguration sentinelConfig = 
            new RedisSentinelConfiguration()
                .master("mymaster")  // 必须与sentinel.conf中的master-name一致
                .sentinel("192.168.1.20", 26379)
                .sentinel("192.168.1.21", 26380)
                .sentinel("192.168.1.22", 26381);
        
        // 2. 认证配置
        sentinelConfig.setPassword(RedisPassword.of("WmsProd_2024_Secure"));
        sentinelConfig.setSentinelPassword(RedisPassword.of("Sentinel_Wms_2024"));
        
        // 3. Lettuce客户端优化(WMS高并发场景)
        ClientOptions clientOptions = ClientOptions.builder()
            // 自动拓扑刷新:Sentinel切换主库后,客户端自动感知
            .autoReconnect(true)
            .pingBeforeActivateConnection(true)
            .build();
            
        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
            .clientOptions(clientOptions)
            // 读取从库:适合库存查询类操作,减轻主库压力
            .readFrom(ReadFrom.REPLICA_PREFERRED)  
            .build();
            
        return new LettuceConnectionFactory(sentinelConfig, clientConfig);
    }
    
    // 4. 序列化配置(WMS大量使用Hash存储库存对象)
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用String序列化key,JSON序列化value
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer jsonSerializer = 
            new GenericJackson2JsonRedisSerializer();
            
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        return template;
    }
}

4.2 库存操作模式:乐观锁防超卖

java 复制代码
@Service
public class InventoryService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String STOCK_KEY = "wms:stock:%s";  // %s=SKU
    private static final String LOCK_KEY = "wms:lock:stock:%s";
    
    /**
     * WMS核心:扣减库存(拣货出库)
     * 使用Redis事务(WATCH-MULTI-EXEC)实现乐观锁
     */
    public boolean deductStock(String sku, int quantity) {
        String stockKey = String.format(STOCK_KEY, sku);
        
        return redisTemplate.execute(new SessionCallback<Boolean>() {
            @Override
            public Boolean execute(RedisOperations operations) {
                // 1. WATCH监控库存键,如果被其他客户端修改,事务将失败
                operations.watch(stockKey);
                
                // 2. 获取当前库存
                String stockStr = (String) operations.opsForValue().get(stockKey);
                int currentStock = stockStr == null ? 0 : Integer.parseInt(stockStr);
                
                if (currentStock < quantity) {
                    operations.unwatch();
                    return false;  // 库存不足
                }
                
                // 3. 开启事务
                operations.multi();
                operations.opsForValue().decrement(stockKey, quantity);
                
                // 4. 执行事务(返回空表示WATCH的键被修改,需重试)
                List<Object> results = operations.exec();
                
                // 5. 事务失败时,Sentinel已切换主库,连接会自动重定向到新主库
                // 业务层只需简单重试即可
                return results != null && !results.isEmpty();
            }
        });
    }
    
    /**
     * 分布式锁:波次任务分配(防止同一任务被多个PDA领取)
     */
    public boolean acquireTaskLock(String taskId, String pdaId, long expireSeconds) {
        String lockKey = String.format(LOCK_KEY, taskId);
        
        // Redis 2.6+ 的 SET key value EX seconds NX 原子操作
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, pdaId, expireSeconds, TimeUnit.SECONDS);
            
        return Boolean.TRUE.equals(success);
    }
}

4.3 故障转移感知与降级

java 复制代码
@Component
public class RedisHealthIndicator implements HealthIndicator {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public Health health() {
        try {
            // 通过INFO replication判断当前节点角色
            Properties info = redisTemplate.execute(
                (RedisCallback<Properties>) conn -> conn.info("replication")
            );
            
            String role = info.getProperty("role");
            String masterHost = info.getProperty("master_host");
            
            // WMS监控:记录当前连接的Redis节点信息
            return Health.up()
                .withDetail("role", role)
                .withDetail("master_host", masterHost)
                .withDetail("connected_slaves", info.getProperty("connected_slaves"))
                .build();
                
        } catch (Exception e) {
            // Sentinel故障转移期间,短暂连接失败属正常
            // 业务层应启用本地缓存降级或队列缓冲
            return Health.down()
                .withDetail("error", e.getMessage())
                .withDetail("action", "启用DB直接查询降级")
                .build();
        }
    }
}

五、WMS专项运维方案

5.1 监控指标体系

yaml 复制代码
# Prometheus + Grafana 监控配置
groups:
  - name: wms_redis_sentinel
    rules:
      # 1. 核心:主库可用性(WMS生命线)
      - alert: RedisMasterDown
        expr: redis_up{role="master"} == 0
        for: 0s  # 立即告警
        labels:
          severity: critical
          team: wms-ops
        annotations:
          summary: "WMS Redis主库宕机"
          
      # 2. 复制延迟(影响库存一致性)
      - alert: RedisReplicationLag
        expr: redis_master_last_io_seconds_ago > 10
        for: 30s
        labels:
          severity: warning
        annotations:
          summary: "从库复制延迟超过10秒"
          
      # 3. Sentinel判定异常
      - alert: SentinelMasterSwitch
        expr: changes(sentinel_master_status{master_name="mymaster"})[5m] > 0
        labels:
          severity: warning
        annotations:
          summary: "Sentinel发生主从切换"
          
      # 4. WMS业务指标:库存操作延迟
      - alert: WmsStockOpSlow
        expr: histogram_quantile(0.99, 
              rate(wms_stock_operation_duration_seconds_bucket[5m])) > 0.1
        labels:
          severity: warning
        annotations:
          summary: "99分位库存操作延迟>100ms"

5.2 故障演练:模拟主库宕机

bash 复制代码
#!/bin/bash
# WMS Redis Sentinel 故障演练脚本

MASTER_IP="192.168.1.10"
MASTER_PORT=6379

echo "=== 演练开始:模拟主库故障 ==="

# 1. 记录当前拓扑
echo "当前Sentinel监控状态:"
redis-cli -h 192.168.1.20 -p 26379 -a Sentinel_Wms_2024 \
    SENTINEL master mymaster

# 2. 制造故障:关闭主库进程
echo "停止主库Redis进程..."
ssh $MASTER_IP "systemctl stop redis@6379"

# 3. 观察Sentinel自动切换(通常3-10秒)
echo "等待Sentinel故障转移..."
sleep 10

# 4. 验证新主库
NEW_MASTER=$(redis-cli -h 192.168.1.20 -p 26379 -a Sentinel_Wms_2024 \
    SENTINEL get-master-addr-by-name mymaster)
echo "新主库地址:$NEW_MASTER"

# 5. 验证WMS业务连续性
echo "验证库存服务可用性..."
curl -s http://wms-api/inventory/health | jq .

# 6. 恢复旧主库(将作为从库加入)
echo "恢复旧主库..."
ssh $MASTER_IP "systemctl start redis@6379"
echo "旧主库已作为从库重新加入集群"

echo "=== 演练完成 ==="

5.3 数据备份与灾难恢复

bash 复制代码
# WMS Redis 备份策略(每日凌晨2点执行)

#!/bin/bash
BACKUP_DIR="/backup/redis/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

# 1. 通过Sentinel获取当前主库地址
MASTER_INFO=$(redis-cli -h sentinel-1 -p 26379 SENTINEL get-master-addr-by-name mymaster)
MASTER_HOST=$(echo $MASTER_INFO | cut -d' ' -f1)
MASTER_PORT=$(echo $MASTER_INFO | cut -d' ' -f2)

# 2. 热备份:BGSAVE触发RDB,然后复制
redis-cli -h $MASTER_HOST -p $MASTER_PORT -a WmsProd_2024_Secure BGSAVE
sleep 5  # 等待后台保存完成
scp $MASTER_HOST:/data/redis/$MASTER_PORT/dump.rdb $BACKUP_DIR/

# 3. AOF文件备份(如果启用)
scp $MASTER_HOST:/data/redis/$MASTER_PORT/appendonly.aof $BACKUP_DIR/

# 4. 上传到异地存储(OSS/S3)
aliyun oss cp $BACKUP_DIR oss://wms-backup/redis/ --recursive

# 5. 保留最近7天本地备份
find /backup/redis -type d -mtime +7 -exec rm -rf {} \;

六、WMS场景优化总结

优化项 方案 效果
读写分离 Lettuce ReadFrom.REPLICA_PREFERRED 主库负载降低40%
连接保活 配置autoReconnect + 拓扑刷新 故障转移无感知
库存防超卖 WATCH事务 + 乐观锁重试 100%避免超卖
任务队列 List + BRPOP阻塞读取 毫秒级任务分发
设备通信 Pub/Sub频道(独立Sentinel组) 与库存数据隔离,避免干扰

七、关键配置检查清单

部署前逐项确认:

  • Sentinel节点数为奇数(3/5/7),且与Redis节点物理隔离
  • down-after-milliseconds根据机房网络质量调整(建议3-10s)
  • 所有节点配置相同密码,且复杂度符合安全规范
  • 禁用危险命令(FLUSHALL/FLUSHDB/CONFIG SET)或重命名
  • AOF开启且appendfsync设置为everysec(WMS平衡模式)
  • 客户端配置自动拓扑刷新(Lettuce/Redisson原生支持)
  • 监控告警覆盖:主库状态、复制延迟、Sentinel切换事件

工程箴言:WMS的高可用不是"不故障",而是"故障时库存不错、任务不乱、设备不停"。Sentinel的秒级切换能力,配合业务层的重试与降级设计,才能构建真正 resilient 的仓储系统。

相关推荐
二宝哥11 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
ShiXZ21317 小时前
Redis docker-compose 部署指南
redis·docker·eureka
青山木20 小时前
Redis 缓存一致性全解:从 Cache Aside 到延迟双删、Canal+MQ 的进阶之路
java·数据库·redis·后端·缓存
YDS82921 小时前
大营销平台 —— 活动SKU库存扣减业务及其一致性处理
java·spring boot·redis·rabbitmq·ddd
爬也要爬着前进1 天前
redis主从搭建
数据库·redis·缓存
用户095367515832 天前
在系统的学习 redis 前的疑惑
redis·go
吴声子夜歌2 天前
Redis 3.x——集群故障转移
java·数据库·redis·集群
明豆2 天前
Redis 分布式缓存生产实战
redis·分布式·缓存
海兰2 天前
【高速缓存】RedisVL 指南:从向量搜索到 AI 应用落地
人工智能·redis
weixin_727535622 天前
双Token认证体系深度拆解:Spring Security + JWT + Redis
redis·spring·wpf