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