百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Redis缓存雪崩的5种应对措施

mhr18 2025-08-05 19:43 3 浏览 0 评论

在高并发系统中,Redis作为核心缓存组件,通常扮演着重要的"守门员"角色,有效地保护后端数据库免受流量冲击。然而,当大量缓存同时失效时,会导致请求如洪水般直接涌向数据库,造成数据库瞬间压力剧增甚至宕机,这种现象被形象地称为"缓存雪崩"。

缓存雪崩主要有两种触发场景:一是大量缓存同时到期失效;二是Redis服务器宕机。无论哪种情况,后果都是请求穿透缓存层直达数据库,使系统面临崩溃风险。对于依赖缓存的高并发系统来说,缓存雪崩不仅会导致响应延迟,还可能引发连锁反应,造成整个系统的不可用。

1. 缓存过期时间随机化策略

原理

缓存雪崩最常见的诱因是大批缓存在同一时间点集中过期。通过为缓存设置随机化的过期时间,可以有效避免这种集中失效的情况,将缓存失效的压力分散到不同的时间点。

实现方法

核心思路是在基础过期时间上增加一个随机值,确保即使是同一批缓存,也会在不同时间点失效。

public class RandomExpiryTimeCache {
    private RedisTemplate<String, Object> redisTemplate;
    private Random random = new Random();
    
    public RandomExpiryTimeCache(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 设置缓存值与随机过期时间
     * @param key 缓存键
     * @param value 缓存值
     * @param baseTimeSeconds 基础过期时间(秒)
     * @param randomRangeSeconds 随机时间范围(秒)
     */
    public void setWithRandomExpiry(String key, Object value, long baseTimeSeconds, long randomRangeSeconds) {
        // 生成随机增量时间
        long randomSeconds = random.nextInt((int) randomRangeSeconds);
        // 计算最终过期时间
        long finalExpiry = baseTimeSeconds + randomSeconds;
        
        redisTemplate.opsForValue().set(key, value, finalExpiry, TimeUnit.SECONDS);
        
        log.debug("Set cache key: {} with expiry time: {}", key, finalExpiry);
    }
    
    /**
     * 批量设置带随机过期时间的缓存
     */
    public void setBatchWithRandomExpiry(Map<String, Object> keyValueMap, long baseTimeSeconds, long randomRangeSeconds) {
        keyValueMap.forEach((key, value) -> setWithRandomExpiry(key, value, baseTimeSeconds, randomRangeSeconds));
    }
}

实际应用示例

@Service
public class ProductCacheService {
    @Autowired
    private RandomExpiryTimeCache randomCache;
    
    @Autowired
    private ProductRepository productRepository;
    
    /**
     * 获取商品详情,使用随机过期时间缓存
     */
    public Product getProductDetail(String productId) {
        String cacheKey = "product:detail:" + productId;
        Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
        
        if (product == null) {
            // 缓存未命中,从数据库加载
            product = productRepository.findById(productId).orElse(null);
            
            if (product != null) {
                // 设置缓存,基础过期时间30分钟,随机范围10分钟
                randomCache.setWithRandomExpiry(cacheKey, product, 30 * 60, 10 * 60);
            }
        }
        
        return product;
    }
    
    /**
     * 缓存首页商品列表,使用随机过期时间
     */
    public void cacheHomePageProducts(List<Product> products) {
        String cacheKey = "products:homepage";
        // 基础过期时间1小时,随机范围20分钟
        randomCache.setWithRandomExpiry(cacheKey, products, 60 * 60, 20 * 60);
    }
}

优缺点分析

优点

  • 实现简单,无需额外基础设施
  • 有效分散缓存过期的时间点,降低瞬时数据库压力
  • 对现有代码改动较小,易于集成
  • 无需额外的运维成本

缺点

  • 无法应对Redis服务器整体宕机的情况
  • 仅能缓解而非完全解决雪崩问题
  • 随机过期可能导致热点数据过早失效
  • 不同业务模块的过期策略需要分别设计

适用场景

  • 大量同类型数据需要缓存的场景,如商品列表、文章列表等
  • 系统初始化或重启后需要预加载大量缓存的情况
  • 数据更新频率较低,过期时间可预测的业务
  • 作为防雪崩的第一道防线,与其他策略配合使用

2. 缓存预热与定时更新

原理

缓存预热是指系统启动时,提前将热点数据加载到缓存中,而不是等待用户请求触发缓存。这样可以避免系统冷启动或重启后,大量请求直接击穿到数据库。配合定时更新机制,可以在缓存即将过期前主动刷新,避免过期导致的缓存缺失。

实现方法

通过系统启动钩子和定时任务实现缓存预热与定时更新:

@Component
public class CacheWarmUpService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private CategoryRepository categoryRepository;
    
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
    
    /**
     * 系统启动时执行缓存预热
     */
    @PostConstruct
    public void warmUpCacheOnStartup() {
        log.info("Starting cache warm-up process...");
        
        CompletableFuture.runAsync(this::warmUpHotProducts);
        CompletableFuture.runAsync(this::warmUpCategories);
        CompletableFuture.runAsync(this::warmUpHomePageData);
        
        log.info("Cache warm-up tasks submitted");
    }
    
    /**
     * 预热热门商品数据
     */
    private void warmUpHotProducts() {
        try {
            log.info("Warming up hot products cache");
            List<Product> hotProducts = productRepository.findTop100ByOrderByViewCountDesc();
            
            // 批量设置缓存,基础TTL 2小时,随机范围30分钟
            Map<String, Object> productCacheMap = new HashMap<>();
            hotProducts.forEach(product -> {
                String key = "product:detail:" + product.getId();
                productCacheMap.put(key, product);
            });
            
            redisTemplate.opsForValue().multiSet(productCacheMap);
            
            // 设置过期时间
            productCacheMap.keySet().forEach(key -> {
                int randomSeconds = 7200 + new Random().nextInt(1800);
                redisTemplate.expire(key, randomSeconds, TimeUnit.SECONDS);
            });
            
            // 安排定时刷新,在过期前30分钟刷新
            scheduleRefresh("hotProducts", this::warmUpHotProducts, 90, TimeUnit.MINUTES);
            
            log.info("Successfully warmed up {} hot products", hotProducts.size());
        } catch (Exception e) {
            log.error("Failed to warm up hot products cache", e);
        }
    }
    
    /**
     * 预热分类数据
     */
    private void warmUpCategories() {
        // 类似实现...
    }
    
    /**
     * 预热首页数据
     */
    private void warmUpHomePageData() {
        // 类似实现...
    }
    
    /**
     * 安排定时刷新任务
     */
    private void scheduleRefresh(String taskName, Runnable task, long delay, TimeUnit timeUnit) {
        scheduler.schedule(() -> {
            log.info("Executing scheduled refresh for: {}", taskName);
            try {
                task.run();
            } catch (Exception e) {
                log.error("Error during scheduled refresh of {}", taskName, e);
                // 发生错误时,安排短期重试
                scheduler.schedule(task, 5, TimeUnit.MINUTES);
            }
        }, delay, timeUnit);
    }
    
    /**
     * 应用关闭时清理资源
     */
    @PreDestroy
    public void shutdown() {
        scheduler.shutdown();
    }
}

优缺点分析

优点

  • 有效避免系统冷启动引发的缓存雪崩
  • 减少用户请求触发的缓存加载,提高响应速度
  • 可以根据业务重要性分级预热,合理分配资源
  • 通过定时更新延长热点数据缓存生命周期

缺点

  • 预热过程可能占用系统资源,影响启动速度
  • 需要识别哪些是真正的热点数据
  • 定时任务可能引入额外的系统复杂度
  • 预热的数据量过大可能会增加Redis内存压力

适用场景

  • 系统重启频率较低,启动时间不敏感的场景
  • 有明确热点数据且变化不频繁的业务
  • 对响应速度要求极高的核心接口
  • 可预测的高流量活动前的系统准备

3. 互斥锁与分布式锁防击穿

原理

当缓存失效时,如果有大量并发请求同时发现缓存缺失并尝试重建缓存,就会造成数据库瞬间压力激增。通过互斥锁机制,可以确保只有一个请求线程去查询数据库和重建缓存,其他线程等待或返回旧值,从而保护数据库。

实现方法

使用Redis实现分布式锁,防止缓存击穿:

@Service
public class MutexCacheService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductRepository productRepository;
    
    // 锁的默认过期时间
    private static final long LOCK_EXPIRY_MS = 3000;
    
    /**
     * 使用互斥锁方式获取商品数据
     */
    public Product getProductWithMutex(String productId) {
        String cacheKey = "product:detail:" + productId;
        String lockKey = "lock:product:detail:" + productId;
        
        // 尝试从缓存获取
        Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
        
        // 缓存命中,直接返回
        if (product != null) {
            return product;
        }
        
        // 定义最大重试次数和等待时间
        int maxRetries = 3;
        long retryIntervalMs = 50;
        
        // 重试获取锁
        for (int i = 0; i <= maxRetries; i++) {
            boolean locked = false;
            try {
                // 尝试获取锁
                locked = tryLock(lockKey, LOCK_EXPIRY_MS);
                
                if (locked) {
                    // 双重检查
                    product = (Product) redisTemplate.opsForValue().get(cacheKey);
                    if (product != null) {
                        return product;
                    }
                    
                    // 从数据库加载
                    product = productRepository.findById(productId).orElse(null);
                    
                    if (product != null) {
                        // 设置缓存
                        int expiry = 3600 + new Random().nextInt(300);
                        redisTemplate.opsForValue().set(cacheKey, product, expiry, TimeUnit.SECONDS);
                    } else {
                        // 设置空值缓存
                        redisTemplate.opsForValue().set(cacheKey, new EmptyProduct(), 60, TimeUnit.SECONDS);
                    }
                    
                    return product;
                } else if (i < maxRetries) {
                    // 使用随机退避策略,避免所有线程同时重试
                    long backoffTime = retryIntervalMs * (1L << i) + new Random().nextInt(50);
                    Thread.sleep(Math.min(backoffTime, 1000)); // 最大等待1秒
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("Interrupted while waiting for mutex lock", e);
                break; // 中断时退出循环
            } catch (Exception e) {
                log.error("Error getting product with mutex", e);
                break; // 发生异常时退出循环
            } finally {
                if (locked) {
                    unlock(lockKey);
                }
            }
        }
        
        // 达到最大重试次数仍未获取到锁,返回可能旧的缓存值或默认值
        product = (Product) redisTemplate.opsForValue().get(cacheKey);
        return product != null ? product : getDefaultProduct(productId);
    }

    // 提供默认值或降级策略
    private Product getDefaultProduct(String productId) {
        log.warn("Failed to get product after max retries: {}", productId);
        // 返回基础信息或空对象
        return new BasicProduct(productId);
    }
    
    /**
     * 尝试获取分布式锁
     */
    private boolean tryLock(String key, long expiryTimeMs) {
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, "locked", expiryTimeMs, TimeUnit.MILLISECONDS);
        return Boolean.TRUE.equals(result);
    }
    
    /**
     * 释放分布式锁
     */
    private void unlock(String key) {
        stringRedisTemplate.delete(key);
    }
}

实际业务场景应用

@RestController
@RequestMapping("/api/products")
public class ProductController {
    @Autowired
    private MutexCacheService mutexCacheService;
    
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable("id") String id) {
        // 使用互斥锁方式获取商品
        Product product = mutexCacheService.getProductWithMutex(id);
        
        if (product instanceof EmptyProduct) {
            return ResponseEntity.notFound().build();
        }
        
        return ResponseEntity.ok(product);
    }
}

优缺点分析

优点

  • 有效防止缓存击穿,保护数据库
  • 适用于读多写少的高并发场景
  • 保证数据一致性,避免多次重复计算
  • 可与其他防雪崩策略结合使用

缺点

  • 增加了请求链路的复杂度
  • 可能引入额外的延迟,尤其在锁竞争激烈时
  • 分布式锁实现需要考虑锁超时、死锁等问题
  • 锁的粒度选择需要权衡,过粗会限制并发,过细会增加复杂度

适用场景

  • 高并发且缓存重建成本高的场景
  • 热点数据被频繁访问的业务
  • 需要避免重复计算的复杂查询
  • 作为缓存雪崩最后一道防线

4. 多级缓存架构

原理

多级缓存通过在不同层次设置缓存,形成缓存梯队,降低单一缓存层失效带来的冲击。典型的多级缓存包括:本地缓存(如Caffeine、Guava Cache)、分布式缓存(如Redis)和持久层缓存(如数据库查询缓存)。当Redis缓存失效或宕机时,请求可以降级到本地缓存,避免直接冲击数据库。

实现方法

@Service
public class MultiLevelCacheService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductRepository productRepository;
    
    // 本地缓存配置
    private LoadingCache<String, Optional<Product>> localCache = CacheBuilder.newBuilder()
            .maximumSize(10000)  // 最多缓存10000个商品
            .expireAfterWrite(5, TimeUnit.MINUTES)  // 本地缓存5分钟后过期
            .recordStats()  // 记录缓存统计信息
            .build(new CacheLoader<String, Optional<Product>>() {
                @Override
                public Optional<Product> load(String productId) throws Exception {
                    // 本地缓存未命中时,尝试从Redis加载
                    return loadFromRedis(productId);
                }
            });
    
    /**
     * 多级缓存查询商品
     */
    public Product getProduct(String productId) {
        String cacheKey = "product:detail:" + productId;
        
        try {
            // 首先查询本地缓存
            Optional<Product> productOptional = localCache.get(productId);
            
            if (productOptional.isPresent()) {
                log.debug("Product {} found in local cache", productId);
                return productOptional.get();
            } else {
                log.debug("Product {} not found in any cache level", productId);
                return null;
            }
        } catch (ExecutionException e) {
            log.error("Error loading product from cache", e);
            
            // 所有缓存层都失败,直接查询数据库作为最后手段
            try {
                Product product = productRepository.findById(productId).orElse(null);
                
                if (product != null) {
                    // 尝试更新缓存,但不阻塞当前请求
                    CompletableFuture.runAsync(() -> {
                        try {
                            updateCache(cacheKey, product);
                        } catch (Exception ex) {
                            log.error("Failed to update cache asynchronously", ex);
                        }
                    });
                }
                
                return product;
            } catch (Exception dbEx) {
                log.error("Database query failed as last resort", dbEx);
                throw new ServiceException("Failed to fetch product data", dbEx);
            }
        }
    }
    
    /**
     * 从Redis加载数据
     */
    private Optional<Product> loadFromRedis(String productId) {
        String cacheKey = "product:detail:" + productId;
        
        try {
            Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
            
            if (product != null) {
                log.debug("Product {} found in Redis cache", productId);
                return Optional.of(product);
            }
            
            // Redis缓存未命中,查询数据库
            product = productRepository.findById(productId).orElse(null);
            
            if (product != null) {
                // 更新Redis缓存
                updateCache(cacheKey, product);
                return Optional.of(product);
            } else {
                // 设置空值缓存
                redisTemplate.opsForValue().set(cacheKey, new EmptyProduct(), 60, TimeUnit.SECONDS);
                return Optional.empty();
            }
        } catch (Exception e) {
            log.warn("Failed to access Redis cache, falling back to database", e);
            
            // Redis访问失败,直接查询数据库
            Product product = productRepository.findById(productId).orElse(null);
            return Optional.ofNullable(product);
        }
    }
    
    /**
     * 更新缓存
     */
    private void updateCache(String key, Product product) {
        // 更新Redis,设置随机过期时间
        int expiry = 3600 + new Random().nextInt(300);
        redisTemplate.opsForValue().set(key, product, expiry, TimeUnit.SECONDS);
    }
    
    /**
     * 主动刷新所有级别的缓存
     */
    public void refreshCache(String productId) {
        String cacheKey = "product:detail:" + productId;
        
        // 从数据库加载最新数据
        Product product = productRepository.findById(productId).orElse(null);
        
        if (product != null) {
            // 更新Redis缓存
            updateCache(cacheKey, product);
            
            // 更新本地缓存
            localCache.put(productId, Optional.of(product));
            
            log.info("Refreshed all cache levels for product {}", productId);
        } else {
            // 删除各级缓存
            redisTemplate.delete(cacheKey);
            localCache.invalidate(productId);
            
            log.info("Product {} not found, invalidated all cache levels", productId);
        }
    }
    
    /**
     * 获取缓存统计信息
     */
    public Map<String, Object> getCacheStats() {
        CacheStats stats = localCache.stats();
        
        Map<String, Object> result = new HashMap<>();
        result.put("localCacheSize", localCache.size());
        result.put("hitRate", stats.hitRate());
        result.put("missRate", stats.missRate());
        result.put("loadSuccessCount", stats.loadSuccessCount());
        result.put("loadExceptionCount", stats.loadExceptionCount());
        
        return result;
    }
}

优缺点分析

优点

  • 极大提高系统的容错能力和稳定性
  • 减轻Redis故障时对数据库的冲击
  • 提供更好的读性能,尤其对于热点数据
  • 灵活的降级路径,多层保护

缺点

  • 增加了系统的复杂性
  • 可能引入数据一致性问题
  • 需要额外的内存消耗用于本地缓存
  • 需要处理各级缓存之间的数据同步

适用场景

  • 高并发、高可用性要求的核心系统
  • 对Redis有强依赖的关键业务
  • 读多写少且数据一致性要求不是极高的场景
  • 大型微服务架构,需要减少服务间网络调用

5. 熔断降级与限流保护

原理

熔断降级机制通过监控缓存层的健康状态,在发现异常时快速降级服务,返回兜底数据或简化功能,避免请求继续冲击数据库。限流则是主动控制进入系统的请求速率,防止在缓存失效期间系统被大量请求淹没。

实现方法

结合Spring Cloud Circuit Breaker实现熔断降级和限流

@Service
public class ResilientCacheService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductRepository productRepository;
    
    // 注入熔断器工厂
    @Autowired
    private CircuitBreakerFactory circuitBreakerFactory;
    
    // 注入限流器
    @Autowired
    private RateLimiter productRateLimiter;
    
    /**
     * 带熔断和限流的商品查询
     */
    public Product getProductWithResilience(String productId) {
        // 应用限流
        if (!productRateLimiter.tryAcquire()) {
            log.warn("Rate limit exceeded for product query: {}", productId);
            return getFallbackProduct(productId);
        }
        
        // 创建熔断器
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("redisProductQuery");
        
        // 包装Redis缓存查询
        Function<String, Product> redisQueryWithFallback = id -> {
            try {
                String cacheKey = "product:detail:" + id;
                Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
                
                if (product != null) {
                    return product;
                }
                
                // 缓存未命中时,从数据库加载
                product = loadFromDatabase(id);
                
                if (product != null) {
                    // 异步更新缓存,不阻塞主请求
                    CompletableFuture.runAsync(() -> {
                        int expiry = 3600 + new Random().nextInt(300);
                        redisTemplate.opsForValue().set(cacheKey, product, expiry, TimeUnit.SECONDS);
                    });
                }
                
                return product;
            } catch (Exception e) {
                log.error("Redis query failed", e);
                throw e; // 重新抛出异常以触发熔断器
            }
        };
        
        // 执行带熔断保护的查询
        try {
            return circuitBreaker.run(() -> redisQueryWithFallback.apply(productId), 
                                    throwable -> getFallbackProduct(productId));
        } catch (Exception e) {
            log.error("Circuit breaker execution failed", e);
            return getFallbackProduct(productId);
        }
    }
    
    /**
     * 从数据库加载商品数据
     */
    private Product loadFromDatabase(String productId) {
        try {
            return productRepository.findById(productId).orElse(null);
        } catch (Exception e) {
            log.error("Database query failed", e);
            return null;
        }
    }
    
    /**
     * 降级后的兜底策略 - 返回基础商品信息或缓存的旧数据
     */
    private Product getFallbackProduct(String productId) {
        log.info("Using fallback for product: {}", productId);
        
        // 优先尝试从本地缓存获取旧数据
        Product cachedProduct = getFromLocalCache(productId);
        if (cachedProduct != null) {
            return cachedProduct;
        }
        
        // 如果是重要商品,尝试从数据库获取基本信息
        if (isHighPriorityProduct(productId)) {
            try {
                return productRepository.findBasicInfoById(productId);
            } catch (Exception e) {
                log.error("Even basic info query failed for high priority product", e);
            }
        }
        
        // 最终兜底:构建一个临时对象,包含最少的必要信息
        return buildTemporaryProduct(productId);
    }
    
    // 辅助方法实现...
    
    /**
     * 熔断器状态监控API
     */
    public Map<String, Object> getCircuitBreakerStatus() {
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("redisProductQuery");
        
        Map<String, Object> status = new HashMap<>();
        status.put("state", circuitBreaker.getState().name());
        status.put("failureRate", circuitBreaker.getMetrics().getFailureRate());
        status.put("failureCount", circuitBreaker.getMetrics().getNumberOfFailedCalls());
        status.put("successCount", circuitBreaker.getMetrics().getNumberOfSuccessfulCalls());
        
        return status;
    }
}

熔断器和限流器配置

@Configuration
public class ResilienceConfig {
    
    @Bean
    public CircuitBreakerFactory circuitBreakerFactory() {
        // 使用Resilience4j实现
        Resilience4JCircuitBreakerFactory factory = new Resilience4JCircuitBreakerFactory();
        
        // 自定义熔断器配置
        factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                .circuitBreakerConfig(CircuitBreakerConfig.custom()
                        .slidingWindowSize(10)  // 滑动窗口大小
                        .failureRateThreshold(50)  // 失败率阈值
                        .waitDurationInOpenState(Duration.ofSeconds(10))  // 熔断器打开持续时间
                        .permittedNumberOfCallsInHalfOpenState(5)  // 半开状态允许的调用次数
                        .build())
                .build());
        
        return factory;
    }
    
    @Bean
    public RateLimiter productRateLimiter() {
        // 使用Guava实现基本的限流器
        return RateLimiter.create(1000);  // 每秒允许1000个请求
    }
}

优缺点分析

优点:

  • 提供完善的容错机制,避免级联故障
  • 主动限制流量,防止系统过载
  • 在缓存不可用时提供降级访问路径
  • 能够自动恢复,适应系统动态变化

缺点

  • 配置复杂,需要精心调优参数
  • 降级逻辑需要为不同业务单独设计
  • 可能导致部分功能暂时不可用
  • 添加了额外的代码复杂度

适用场景

  • 对可用性要求极高的核心系统
  • 需要防止故障级联传播的微服务架构
  • 流量波动较大的在线业务
  • 有多级服务依赖的复杂系统

6. 对比分析

策略

复杂度

效果

适用场景

主要优势

过期时间随机化

同类缓存大量集中失效

实现简单,立即见效

缓存预热与定时更新

系统启动和重要数据

主动预防,减少突发压力

互斥锁防击穿

热点数据频繁失效

精准保护,避免重复计算

多级缓存架构

高可用核心系统

多层防护,灵活降级

熔断降级与限流

微服务复杂系统

全面保护,自动恢复

7. 总结

实际应用中,这些策略并非互斥,而是应根据业务特点和系统架构进行组合。完善的缓存雪崩防护体系需要技术手段、架构设计和运维监控的协同配合,才能构建真正健壮的高可用系统。

通过合理实施这些策略,我们不仅能有效应对缓存雪崩问题,还能全面提升系统的稳定性和可靠性,为用户提供更好的服务体验。

相关推荐

IM群聊消息如此复杂,如何保证不丢不重?

群聊是多人社交的基本诉求,不管是QQ群,还是微信群,一个群友在群内发了一条消息:(1)在线的群友能第一时间收到消息(2)离线的群友能在登陆后收到消息群消息的复杂度要远高于单对单消息。群消息的实时性,可...

Python 网络爬虫实战:从零到部署的完整流程

适用人群:初-中级Python开发者、数据分析师、运维/测试自动化工程师工具栈:Python3.11+requests+BeautifulSoup/lxml+pandas+(...

用上Kiro之后,完全没理由为Cursor续费了

替Cursor续费前最后一秒,免费IDEKiro把钱包按死在屏幕前五位数年费的AI编程助手,被一匹黑马零元秒杀。用过Kiro的人,开note第一件事就是删掉Cursor的自动续费,动作快到连...

分布式微服务中的搜索引擎:架构与实战盘点

01、为什么微服务需要分布式搜索?在单体应用时代,我们通常使用单一数据库的全文检索功能(如MySQL的LIKE语句)或简单的搜索引擎(如早期的Lucene)。但随着业务规模扩大,这种架构暴露出诸多问题...

产品列表获取API接口详解

在现代软件开发中,API(应用程序编程接口)是获取产品列表的核心工具,它允许开发者从远程服务器高效地检索数据。本文将逐步介绍如何设计和使用产品列表获取API接口,包括核心概念、实现步骤、代码示例以及最...

企业和个人基于业务知识和代码库增强的大模型生成代码实践

作者:京东零售杨亚龙1.源起李明是今年刚加入某互联网公司的研发新人,满怀期待地开始了他的职业生涯。然而,短短两周后,他的热情就被现实浇了一盆冷水。第一周:当他第一次接手需求时,mentor只是简单...

从零到一:独立运行若依框架系统并进行本地二次开发

####一、环境准备1.**基础环境**:-JDK1.8+(推荐JDK17)-Maven3.6+-MySQL5.7+(推荐8.0)-Redis5.0+-Node.js16...

一文教你高效优化在Spring Boot3中遇到深度分页查询性能难题?

你有没有这样的经历?在使用SpringBoot3开发项目时,深度分页查询操作让程序运行得越来越慢,页面加载时间变得难以忍受,不仅影响用户体验,还可能导致项目进度受阻。明明代码逻辑看起来没问题,可...

JAVA面试|如何优化limit分页

我们来详细通俗地聊聊如何优化LIMIToffset,size分页。核心问题在于OFFSET的值很大时,性能会急剧下降。想象一下数据库的工作方式,你就明白为什么了。一、为什么OFFSET大时慢?假...

MySQL(143)如何优化分页查询?

优化分页查询是提升数据库性能和用户体验的重要手段。特别是在处理大数据集时,分页查询的效率对系统性能有显著影响。以下是优化分页查询的详细步骤和代码示例。一、传统分页查询传统的分页查询使用OFFSET...

Seata概述

什么是SeataSeata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务也是SpringCloudAlibaba提供的组件Seata官方文档https...

Docmost:一款开源的Wiki和文档协作软件

是一款开源的团队协作Wiki与文档管理工具,定位为Confluence和Notion的开源替代品,专注于提供高效、安全且可定制的知识库解决方案。Docmost的核心优势在于开源免...

B端系统管理「字典管理」模块实战指南

字典管理听起来像“后端杂务”,其实是B端系统配置能力的关键支点。本指南将从真实业务场景出发,系统拆解该模块的设计逻辑、关键字段与典型坑位,让你一文读懂如何搭建一个能跑得久、配得稳的字典模块。一、字典管...

Spring Boot 整合 Redis BitMap 实现 签到与统计

要在SpringBoot中实现RedisBitMap来进行签到和统计,您需要按照以下步骤进行操作:添加Redis依赖:在pom.xml文件中添加Redis依赖:<dependen...

周期性清除Spark Streaming流状态的方法

在SparkStreaming程序中,我们经常需要使用有状态的流来统计一些累积性的指标,比如各个商品的PV。简单的代码描述如下,使用mapWithState()算子:valproductPvSt...

取消回复欢迎 发表评论: