分布式部署指南
版本: v2.2.0
更新日期: 2025-11-25
📚 目录
概述
monSQLize 支持单实例和多实例部署。在单实例环境下,所有缓存和事务机制都能完美工作。但在多实例(分布式)环境下,需要额外配置才能保证数据一致性。
为什么需要分布式支持?
在多实例部署中,每个实例都有独立的本地缓存和锁管理器。如果不做特殊处理,会导致:
- ❌ 缓存不一致:实例 A 更新数据后,实例 B 的本地缓存仍是旧数据
- ❌ 事务隔离性失效:实例 A 事务期间,实例 B 可能读到中间状态并写入缓存
- ❌ 业务逻辑错误:余额计算、库存扣减等场景可能出错
架构选择
1. 单实例部署(✅ 推荐:小型应用)
特点:
- ✅ 所有功能完整支持
- ✅ 无需 Redis
- ✅ 配置简单
- ⚠️ 无高可用
适用场景:
- 开发/测试环境
- 流量不大的生产环境
- 单体应用
配置示例:
2. 多实例 + 独立本地缓存(🔴 不推荐)
风险:
- 🔴 高风险:缓存不一致
- 🔴 事务隔离性失效
- ❌ 不推荐生产环境
3. 多实例 + Redis + 分布式缓存失效(🟢 推荐)
特点:
- ✅ 高可用
- ✅ 缓存一致性好(毫秒级延迟)
- ✅ 性能优异
- ⚠️ 依赖 Redis
适用场景:
- 生产环境(推荐)
- 高并发场景
- 可容忍短期(毫秒级)数据不一致
配置示例:
4. 多实例 + 分布式事务锁(🟢 推荐:金融/交易)
特点:
- ✅ 强一致性
- ✅ 事务隔离性保障
- ✅ 适合金融/交易场景
- ⚠️ 性能略有下降(Redis 网络请求)
适用场景:
- 金融系统
- 支付/转账
- 库存扣减
- 任何需要强一致性的场景
配置示例:
5. 多实例 + 禁用缓存(🟡 适用:强一致性要求)
特点:
- ✅ 100% 数据一致性
- ✅ 无外部依赖
- ❌ 性能下降(所有请求查数据库)
适用场景:
- 小流量应用
- 强一致性要求
- 无法使用 Redis
配置示例:
分布式环境下的风险
风险 1: 缓存失效不同步
场景:
影响:
- 用户看到不一致的余额
- 业务逻辑可能出错
解决方案:启用分布式缓存失效广播
风险 2: 事务缓存锁不生效
场景:
影响:
- 读到事务中间状态
- 缓存中可能有脏数据
- 事务隔离性失效
解决方案:启用分布式事务锁
解决方案
方案 1: 分布式缓存失效广播(推荐)
原理:使用 Redis Pub/Sub 广播缓存失效消息
工作流程:
配置:
优点:
- ✅ 实时广播,延迟低(毫秒级)
- ✅ 使用现有 Redis 基础设施
- ✅ 实现简单,易于维护
缺点:
- ⚠️ 依赖 Redis
- ⚠️ 极短时间窗口内可能不一致(网络延迟)
方案 2: 分布式事务锁(推荐:强一致性)
原理:使用 Redis 存储事务锁信息,所有实例共享
工作流程:
配置:
优点:
- ✅ 真正的分布式锁
- ✅ 事务隔离性保障
- ✅ 适合金融/交易场景
缺点:
- ⚠️ 性能略有下降(Redis 网络请求)
- ⚠️ 依赖 Redis 可用性
配置指南
完整配置示例
💡 配置说明:
- ✅ 必需项:必须配置,否则功能不工作
- ❌ 可选项:可以不配置,使用默认值
- 一个 Redis 实例:用于缓存、广播、锁三个用途(推荐复用)
配置选项说明
distributed(分布式缓存失效)
⚠️ 重要说明:
redis和redisUrl:都是可选的- 推荐:不配置
redis,自动从cache.remote复用 Redis 实例 - 如需单独配置:使用
redis参数(可复用实例) - 不推荐:使用
redisUrl(会创建新连接)
- 推荐:不配置
instanceId:可选,但强烈建议手动设置- 默认值格式:
instance-${timestamp}-${random}(如instance-1732521234567-a2b3c4d5e) - 每个实例的
instanceId必须不同,否则会导致缓存失效广播失败 - 推荐使用环境变量:
process.env.INSTANCE_ID或process.env.HOSTNAME
- 默认值格式:
transaction.distributedLock(分布式事务锁)
技术实现细节
分布式缓存失效的完整流程
1. 初始化阶段(connect() 时)
2. 写操作阶段(updateOne/deleteOne 等)
3. MultiLevelCache.delPattern() 触发广播
4. DistributedCacheInvalidator 广播消息
5. 其他实例接收消息并失效缓存
完整调用链
关键点说明
-
延迟注入 publish 回调:
- 缓存在
constructor中创建 DistributedCacheInvalidator在connect()中创建- 使用
setPublish()方法动态注入回调
- 缓存在
-
实例 ID 隔离:
- 每个实例有唯一的
instanceId - 接收消息时检查
instanceId,忽略自己发送的消息 - 避免重复失效本地缓存
- 每个实例有唯一的
-
只失效本地缓存:
- 接收到广播消息后,只失效本地缓存
- 不影响 Redis 缓存(已经失效)
- 减少不必要的 Redis 操作
-
错误处理:
- 广播失败不影响写操作成功
- 使用
catch()捕获所有异常 - 记录错误日志但不抛出
最佳实践
1. 环境检测
自动检测是否在分布式环境:
2. 根据业务选择方案
3. 监控和日志
启用日志查看分布式组件状态:
4. 错误处理
分布式组件失败时的降级策略:
性能考虑
1. 分布式缓存失效性能
- 延迟:~1-5ms(Redis Pub/Sub)
- 带宽:每次失效 ~100 bytes
- 影响:对总体性能影响可忽略
2. 分布式事务锁性能
- 延迟:~2-10ms(Redis SET/DEL)
- 吞吐量:略有下降(~10-20%)
- 推荐:仅在需要强一致性时启用
3. 缓存策略对比
故障排查
问题 1: 缓存失效广播不生效
症状:实例 A 更新后,实例 B 仍读到旧数据
排查步骤:
-
检查 Redis 连接是否正常
-
检查 Pub/Sub 订阅
-
查看日志
-
检查实例 ID
问题 2: 分布式事务锁不生效
症状:事务期间其他实例仍写入缓存
排查步骤:
-
确认配置正确
-
检查锁是否创建
-
查看事务日志
问题 3: Redis 连接失败
症状:应用启动报错或缓存不工作
解决方案:
总结
推荐配置
快速开始
最简单的分布式配置(推荐):
⚠️ 重要:
instanceId是可选的,默认自动生成(格式:instance-${timestamp}-${random})- 但强烈建议手动设置,便于调试和日志追踪
- 每个实例的
instanceId必须不同 - 建议使用环境变量:
instanceId: process.env.INSTANCE_ID redis参数可省略,会自动从remote复用
相关文档
更新日期: 2025-11-25
维护者: monSQLize Team