规则持久化:Nacos、Apollo、Redis三种方案

三种方案对比 方案 实时性 高可用 运维成本 适用场景 Nacos 秒级 高 低 推荐,微服务首选 Apollo 秒级 高 中 已使用Apollo的项目 Redis 秒级 高 低 轻量级方案 文件 分钟级 低 高 不推荐生产使用 方案1:Nacos(推荐) 配置 spring: cloud: sentinel: datasource: flow: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-flow-rules groupId: SENTINEL_GROUP rule-type: flow namespace: dev Nacos中配置规则 [ { "resource": "orderCreate", "limitApp": "default", "grade": 1, "count": 1000, "strategy": 0, "controlBehavior": 0 } ] 优点 与Spring Cloud Alibaba深度集成 支持配置热更新 提供Web控制台 支持灰度发布 支持多环境(namespace) 方案2:Apollo 添加依赖 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-apollo</artifactId> <version>1.8.6</version> </dependency> 配置 spring: cloud: sentinel: datasource: flow: apollo: namespace-name: application flow-rules-key: sentinel.flow.rules default-flow-rule-value: "[]" rule-type: flow Apollo中配置 Namespace: application Key: sentinel.flow.rules Value: ...

2025-11-20 · maneng

Redis第一性原理:为什么我们需要缓存?

一、引子:商品详情接口的性能进化之路 想象你正在开发一个电商平台的商品详情页面,每次用户访问都需要查询商品基本信息、品牌信息、类目信息、库存信息和商品图片。让我们看看三种不同的实现方式,以及它们各自的性能表现。 1.1 场景A:无缓存(直接查数据库) 这是最直接的实现方式:每次请求都查询数据库。 @Service public class ProductService { @Autowired private ProductRepository productRepository; @Autowired private BrandRepository brandRepository; @Autowired private CategoryRepository categoryRepository; @Autowired private InventoryRepository inventoryRepository; @Autowired private ProductImageRepository productImageRepository; /** * 查询商品详情(每次请求都查数据库) * 平均耗时:100ms * QPS上限:500 */ public ProductDetailVO getProductDetail(Long productId) { // 1. 查询商品基本信息(20ms) Product product = productRepository.findById(productId); if (product == null) { throw new ProductNotFoundException("商品不存在:" + productId); } // 2. 查询品牌信息(20ms) Brand brand = brandRepository.findById(product.getBrandId()); // 3. 查询类目信息(20ms) Category category = categoryRepository.findById(product.getCategoryId()); // 4. 查询库存信息(20ms) Inventory inventory = inventoryRepository.findByProductId(productId); // 5. 查询商品图片(20ms,可能有N+1查询问题) List<ProductImage> images = productImageRepository.findByProductId(productId); // 6. 组装返回对象 ProductDetailVO vo = new ProductDetailVO(); vo.setProductId(product.getId()); vo.setProductName(product.getName()); vo.setPrice(product.getPrice()); vo.setBrandName(brand.getName()); vo.setCategoryName(category.getName()); vo.setStock(inventory.getStock()); vo.setImages(images); return vo; } } 性能数据(压测工具:JMeter,1000并发): ...

2025-11-03 · maneng

从HashMap到Redis:分布式缓存的演进

一、引子:一个用户会话缓存的演进之路 假设你正在开发一个电商网站的用户会话管理功能。每次用户请求都需要验证身份,最初的实现是每次都查询数据库,但随着用户量增长,数据库压力越来越大。让我们看看这个功能如何一步步演进,从最简单的HashMap到最终的Redis分布式缓存。 1.1 场景0:无缓存(每次查数据库) 最直接的实现:每次请求都查询数据库验证用户身份。 @RestController public class UserController { @Autowired private UserRepository userRepository; /** * 获取用户信息(每次查数据库) * 问题:数据库压力大,响应慢 */ @GetMapping("/api/user/info") public UserVO getUserInfo(@RequestHeader("token") String token) { // 1. 根据token查询用户ID(查数据库) Long userId = tokenRepository.findUserIdByToken(token); if (userId == null) { throw new UnauthorizedException("未登录"); } // 2. 查询用户详细信息(查数据库) User user = userRepository.findById(userId); if (user == null) { throw new UserNotFoundException("用户不存在"); } return convertToVO(user); } } 性能数据: 指标 数值 说明 平均响应时间 50ms 2次SQL查询 QPS上限 1000 数据库连接池限制 数据库压力 100% 每次请求都查库 问题: ...

2025-11-03 · maneng

Redis五大数据结构:从场景到实现

一、引子:为什么Redis需要五大数据结构? 很多人的疑问:Memcached只有String一种数据结构,Redis为什么需要五种? 核心答案:不同的业务场景需要不同的数据结构。 1.1 如果只有String会怎样? 假设我们要实现一个排行榜功能,只有String的话: // ❌ 方案1:用String存储整个排行榜(JSON序列化) // 问题:每次更新一个用户分数,需要序列化/反序列化整个排行榜 public void updateScore(Long userId, int score) { // 1. 读取整个排行榜(反序列化) String json = redisTemplate.opsForValue().get("rank:list"); List<User> rankList = JSON.parseArray(json, User.class); // 10000个用户 // 2. 更新一个用户的分数 for (User user : rankList) { if (user.getId().equals(userId)) { user.setScore(score); break; } } // 3. 重新排序 rankList.sort((a, b) -> b.getScore() - a.getScore()); // 4. 写入Redis(序列化) String newJson = JSON.toJSONString(rankList); redisTemplate.opsForValue().set("rank:list", newJson); } // 性能问题: // - 读取:反序列化10000个用户,耗时100ms // - 排序:O(NlogN) = 10000*log(10000) ≈ 130000次比较 // - 写入:序列化10000个用户,耗时100ms // 总耗时:200ms+(单次更新) // ✅ 方案2:使用Redis ZSet(有序集合) // 优势:Redis内部维护排序,O(logN)复杂度 public void updateScore(Long userId, int score) { redisTemplate.opsForZSet().add("rank:zset", userId.toString(), score); } // 性能提升: // - 写入:O(logN) = log(10000) ≈ 13次比较 // - 总耗时:1ms // 性能提升:200倍 核心洞察: ...

2025-11-03 · maneng

Redis高可用架构:主从复制、哨兵、集群

一、引子:单机Redis的三大困境 假设你正在运行一个电商网站,使用单机Redis作为缓存。随着业务增长,你会遇到三个核心问题: 1.1 问题1:单点故障(可用性) 场景:双11大促,凌晨0点 00:00:00 - Redis单机宕机(内存不足OOM) 00:00:01 - 所有请求打到数据库 00:00:02 - 数据库连接池耗尽(1000个请求/秒) 00:00:03 - 数据库CPU 100% 00:00:05 - 网站503错误,用户无法下单 00:00:10 - 运维手动重启Redis 00:00:15 - Redis启动成功,但缓存为空 00:00:20 - 缓存预热中(需要10分钟) 00:10:00 - 系统恢复正常 损失: - 10分钟宕机时间 - 订单损失:10分钟 × 1000单/分钟 = 1万单 - GMV损失:1万单 × 200元/单 = 200万元 核心问题:单点故障导致系统不可用,无容灾能力。 可用性计算: 假设:Redis每月宕机1次,每次10分钟 可用性 = (30天 × 24小时 × 60分钟 - 10分钟) / (30天 × 24小时 × 60分钟) = (43200 - 10) / 43200 = 99.977% 看起来还不错?但实际上: - 1年12次宕机,累计120分钟 = 2小时 - 如果恰好在大促期间宕机,损失巨大 - 高可用系统要求:99.99%(年宕机时间 < 52分钟) 1.2 问题2:容量瓶颈(可扩展性) 场景:业务增长,数据量暴增 第1个月: - 商品数量:10万 - 缓存数据量:2GB - 单机Redis:4GB内存(够用) 第6个月: - 商品数量:100万 - 缓存数据量:20GB - 单机Redis:4GB内存(不够用!) 解决方案1:垂直扩展(加内存) - 4GB → 16GB(成本翻倍,但有上限) - 最大:512GB(成本极高,且有物理上限) 解决方案2:水平扩展(加机器) - 需要Redis集群(数据分片) 核心问题:单机内存有限,垂直扩展有上限,需要水平扩展。 ...

2025-11-03 · maneng

Redis持久化:RDB与AOF的权衡

一、引子:Redis宕机后数据去哪了? Redis是内存数据库,数据存储在内存中。那么问题来了:Redis宕机后,数据还在吗? 1.1 场景:双11大促数据丢失事故 场景:电商网站,双11大促 00:00:00 - 大促开始,流量暴增 00:00:01 - Redis缓存:10万个商品详情、50万个用户Session 00:05:00 - 服务器断电(机房故障) 00:05:01 - Redis进程被杀,内存数据全部丢失 00:10:00 - 服务器恢复,Redis重启 00:10:01 - Redis启动成功,但数据为空! 问题: 1. 所有商品详情缓存丢失 → 10万次数据库查询 2. 所有用户Session丢失 → 50万用户被强制登出 3. 缓存预热需要10分钟 → 数据库压力巨大 4. 用户体验极差 → 大量用户流失 损失: - 用户流失:50万用户 × 10% = 5万用户 - 订单损失:5万用户 × 20% = 1万单 - GMV损失:1万单 × 200元 = 200万元 核心问题:Redis是内存数据库,断电后数据丢失,需要持久化。 1.2 持久化的本质 持久化:将内存中的数据保存到磁盘,重启后可以恢复。 为什么需要持久化? 原因1:数据安全 - Redis宕机、服务器断电、进程被杀 - 内存数据丢失 - 持久化后可以恢复 原因2:快速恢复 - 没有持久化:需要从数据库重新加载(10分钟+) - 有持久化:直接加载持久化文件(1分钟) 原因3:数据备份 - 定期备份持久化文件 - 灾难恢复(机房火灾、磁盘损坏) Redis的两种持久化方式: ...

2025-11-03 · maneng

Redis实战:分布式锁、消息队列、缓存设计

一、分布式锁:从SETNX到Redlock 1.1 为什么需要分布式锁? 场景:秒杀系统的超卖问题 // ❌ 错误:单机锁无法解决分布式超卖 @Service public class SeckillService { @Autowired private ProductRepository productRepository; /** * 秒杀下单(单机锁) */ public synchronized Order seckill(Long productId, Long userId) { // 1. 检查库存 Integer stock = productRepository.getStock(productId); if (stock <= 0) { throw new SoldOutException("商品已售罄"); } // 2. 扣减库存 productRepository.decrementStock(productId); // 3. 创建订单 return orderService.createOrder(productId, userId); } } // 问题: // 假设有3台服务器(Server A、B、C) // T1: Server A:检查库存=1(通过) // T2: Server B:检查库存=1(通过) // T3: Server A:扣减库存=0,创建订单1 // T4: Server B:扣减库存=-1(超卖!),创建订单2 // synchronized只能锁住单机JVM内的线程 // 无法锁住分布式环境的多个进程 核心问题:分布式环境下,单机锁无效,需要分布式锁。 ...

2025-11-03 · maneng

Redis第一性原理:为什么我们需要缓存?

一、引子:商品详情接口的性能进化之路 想象你正在开发一个电商平台的商品详情页面,每次用户访问都需要查询商品基本信息、品牌信息、类目信息、库存信息和商品图片。让我们看看三种不同的实现方式,以及它们各自的性能表现。 1.1 场景A:无缓存(直接查数据库) 这是最直接的实现方式:每次请求都查询数据库。 @Service public class ProductService { @Autowired private ProductRepository productRepository; @Autowired private BrandRepository brandRepository; @Autowired private CategoryRepository categoryRepository; @Autowired private InventoryRepository inventoryRepository; @Autowired private ProductImageRepository productImageRepository; /** * 查询商品详情(每次请求都查数据库) * 平均耗时:100ms * QPS上限:500 */ public ProductDetailVO getProductDetail(Long productId) { // 1. 查询商品基本信息(20ms) Product product = productRepository.findById(productId); if (product == null) { throw new ProductNotFoundException("商品不存在:" + productId); } // 2. 查询品牌信息(20ms) Brand brand = brandRepository.findById(product.getBrandId()); // 3. 查询类目信息(20ms) Category category = categoryRepository.findById(product.getCategoryId()); // 4. 查询库存信息(20ms) Inventory inventory = inventoryRepository.findByProductId(productId); // 5. 查询商品图片(20ms,可能有N+1查询问题) List<ProductImage> images = productImageRepository.findByProductId(productId); // 6. 组装返回对象 ProductDetailVO vo = new ProductDetailVO(); vo.setProductId(product.getId()); vo.setProductName(product.getName()); vo.setPrice(product.getPrice()); vo.setBrandName(brand.getName()); vo.setCategoryName(category.getName()); vo.setStock(inventory.getStock()); vo.setImages(images); return vo; } } 性能数据(压测工具:JMeter,1000并发): ...

2025-11-03 · maneng

从HashMap到Redis:分布式缓存的演进

一、引子:一个用户会话缓存的演进之路 假设你正在开发一个电商网站的用户会话管理功能。每次用户请求都需要验证身份,最初的实现是每次都查询数据库,但随着用户量增长,数据库压力越来越大。让我们看看这个功能如何一步步演进,从最简单的HashMap到最终的Redis分布式缓存。 1.1 场景0:无缓存(每次查数据库) 最直接的实现:每次请求都查询数据库验证用户身份。 @RestController public class UserController { @Autowired private UserRepository userRepository; /** * 获取用户信息(每次查数据库) * 问题:数据库压力大,响应慢 */ @GetMapping("/api/user/info") public UserVO getUserInfo(@RequestHeader("token") String token) { // 1. 根据token查询用户ID(查数据库) Long userId = tokenRepository.findUserIdByToken(token); if (userId == null) { throw new UnauthorizedException("未登录"); } // 2. 查询用户详细信息(查数据库) User user = userRepository.findById(userId); if (user == null) { throw new UserNotFoundException("用户不存在"); } return convertToVO(user); } } 性能数据: 指标 数值 说明 平均响应时间 50ms 2次SQL查询 QPS上限 1000 数据库连接池限制 数据库压力 100% 每次请求都查库 问题: ...

2025-11-03 · maneng

Redis五大数据结构:从场景到实现

一、引子:为什么Redis需要五大数据结构? 很多人的疑问:Memcached只有String一种数据结构,Redis为什么需要五种? 核心答案:不同的业务场景需要不同的数据结构。 1.1 如果只有String会怎样? 假设我们要实现一个排行榜功能,只有String的话: // ❌ 方案1:用String存储整个排行榜(JSON序列化) // 问题:每次更新一个用户分数,需要序列化/反序列化整个排行榜 public void updateScore(Long userId, int score) { // 1. 读取整个排行榜(反序列化) String json = redisTemplate.opsForValue().get("rank:list"); List<User> rankList = JSON.parseArray(json, User.class); // 10000个用户 // 2. 更新一个用户的分数 for (User user : rankList) { if (user.getId().equals(userId)) { user.setScore(score); break; } } // 3. 重新排序 rankList.sort((a, b) -> b.getScore() - a.getScore()); // 4. 写入Redis(序列化) String newJson = JSON.toJSONString(rankList); redisTemplate.opsForValue().set("rank:list", newJson); } // 性能问题: // - 读取:反序列化10000个用户,耗时100ms // - 排序:O(NlogN) = 10000*log(10000) ≈ 130000次比较 // - 写入:序列化10000个用户,耗时100ms // 总耗时:200ms+(单次更新) // ✅ 方案2:使用Redis ZSet(有序集合) // 优势:Redis内部维护排序,O(logN)复杂度 public void updateScore(Long userId, int score) { redisTemplate.opsForZSet().add("rank:zset", userId.toString(), score); } // 性能提升: // - 写入:O(logN) = log(10000) ≈ 13次比较 // - 总耗时:1ms // 性能提升:200倍 核心洞察: ...

2025-11-03 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...