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 的仓储系统。

相关推荐
小羊在睡觉3 小时前
Reids缓存穿透、击穿、雪崩
redis·缓存·go
ward RINL6 小时前
Redis 安装及配置教程(Windows)【安装】
数据库·windows·redis
Nontee7 小时前
Redis高可用架构解析
数据库·redis·架构
m0_612535998 小时前
redis入门到精通
数据库·redis·缓存
刘~浪地球9 小时前
Redis 从入门到精通(三):键操作命令详解
数据库·redis·缓存
刘~浪地球11 小时前
Redis 从入门到精通(四):字符串操作详解
数据库·redis·缓存
xhuiting11 小时前
Redis专题(二)
redis·缓存
一叶飘零_sweeeet12 小时前
Redis 不止缓存!从零到一吃透 Redis 向量数据库
redis·向量数据库
softshow102614 小时前
SpringCloud Redis与分布式
redis·分布式·spring cloud