6.1.多级缓存架构

目录

一、多级缓存基础与核心概念

  1. 缓存的定义与价值 • 缓存的应用场景(高并发、低延迟、减轻数据库压力) • 多级缓存 vs 单级缓存的优劣对比

  2. 多级缓存核心组件 • 本地缓存(Caffeine、Guava Cache) • 分布式缓存(Redis、Memcached)

  3. 缓存一致性挑战 • 数据一致性模型(强一致、最终一致) • 常见问题:缓存穿透、雪崩、击穿


二、多级缓存架构设计模式

  1. 经典三级缓存模型 • L1:JVM堆内缓存(Caffeine) • L2:堆外缓存(Offheap/Redis) • L3:持久化存储(MySQL/MongoDB)

  2. 读写策略设计 • Cache-Aside(旁路缓存) • Read/Write Through(穿透读写) • Write Behind(异步回写)

  3. 微服务场景下的多级缓存 • 网关层缓存(Nginx/OpenResty) • 服务层缓存(Spring Cache注解集成) • 分布式缓存同步机制


三、本地缓存实战与性能优化

  1. Caffeine深度解析 • 缓存淘汰策略(LRU、LFU、W-TinyLFU) • 过期策略(基于大小、时间、引用)

  2. 堆外缓存应用 • Ehcache堆外缓存配置 • MapDB实现本地持久化缓存

  3. 热点数据发现与预热 • 基于LRU的热点数据统计 • 定时任务预热(Quartz/Spring Scheduler)


四、分布式缓存集成与高可用

  1. Redis多级缓存架构 • 主从复制与哨兵模式 • Cluster集群分片与数据分布

  2. 缓存与数据库同步策略 • 延迟双删(Double Delete) • 基于Binlog的异步同步(Canal+MQ)

  3. 分布式锁保障数据一致性 • Redisson实现分布式锁 • 锁粒度控制与锁续期机制


五、多级缓存实战场景解析

  1. 电商高并发场景 • 商品详情页多级缓存设计(静态化+动态加载) • 库存缓存与预扣减方案

  2. 社交平台热点数据 • 用户Feed流缓存策略(推拉结合) • 实时排行榜(Redis SortedSet)

  3. 金融交易场景 • 资金账户余额缓存(强一致性保障) • 交易流水异步归档


六、缓存问题解决方案

  1. 缓存穿透 • 布隆过滤器(Bloom Filter)实现 • 空值缓存与短过期时间

  2. 缓存雪崩 • 随机过期时间 • 熔断降级与本地容灾缓存

  3. 缓存击穿 • 互斥锁(Mutex Lock) • 逻辑过期时间(Logical Expiration)


七、性能监控与调优

  1. 缓存命中率分析 • 监控指标(Hit Rate、Miss Rate、Load Time) • Prometheus + Grafana可视化

  2. JVM缓存调优 • 堆内缓存GC优化(G1/ZGC) • 堆外缓存内存泄漏排查(NMT工具)

  3. Redis性能调优 • 内存碎片整理(Memory Purge) • Pipeline批处理与Lua脚本优化


八、面试高频题与实战案例

  1. 经典面试题 • Redis如何实现分布式锁?如何处理锁续期? • 如何设计一个支持百万QPS的缓存架构?

  2. 场景设计题 • 设计一个秒杀系统的多级缓存方案 • 如何保证缓存与数据库的最终一致性?

  3. 实战案例分析 • 某电商大促缓存架构优化(TPS从1万到10万) • 社交平台热点数据动态降级策略


一、多级缓存基础与核心概念


1. 缓存的定义与价值

1.1 缓存的应用场景

高并发场景: • 示例:电商秒杀活动,用户瞬时请求量激增,直接访问数据库会导致宕机。 • 缓存作用:将商品库存信息缓存在Redis中,请求优先读取缓存,缓解数据库压力。 • 低延迟需求: • 示例:社交App的Feed流内容,用户期望快速加载。 • 缓存作用:本地缓存(如Caffeine)存储热门帖子,响应时间从100ms降至5ms。 • 减轻数据库压力: • 示例:用户详情页查询频繁,但数据更新频率低。 • 缓存作用:通过“旁路缓存”模式(Cache-Aside),90%的请求命中缓存。

1.2 多级缓存 vs 单级缓存对比
对比维度单级缓存多级缓存
性能单一层级,性能提升有限本地缓存+分布式缓存,响应速度更快
可用性缓存宕机则请求直接压到数据库本地缓存兜底,分布式缓存故障时仍可部分响应
一致性维护一致性管理简单多层级数据同步复杂,需设计同步策略
适用场景低并发、数据量小高并发、数据量大、延迟敏感型业务

2. 多级缓存核心组件

2.1 本地缓存(Caffeine/Guava Cache)

Caffeine核心配置

  Cache<String, User> cache = Caffeine.newBuilder()  .maximumSize(10_000)          // 最大缓存条目数  .expireAfterWrite(10, TimeUnit.MINUTES)  // 写入后10分钟过期  .recordStats()                // 开启统计(命中率监控)  .build();  
​// 使用示例  User user = cache.get("user:123", key -> userDao.findById(123));  

Guava Cache特性: • 优势:轻量级、与Spring良好集成。 • 局限:性能略低于Caffeine,不支持异步加载。

2.2 分布式缓存(Redis/Memcached)

Redis核心能力: • 数据结构丰富:String、Hash、List、SortedSet等。 • 集群模式:主从复制、Cluster分片、Sentinel高可用。 • 生产级配置yaml spring: redis: cluster: nodes: redis-node1:6379,redis-node2:6379,redis-node3:6379 lettuce: pool: max-active: 20 # 连接池最大连接数

Memcached适用场景: • 简单KV存储:无需复杂数据结构,追求极致内存利用率。 • 多线程模型:相比Redis单线程,在多核CPU下吞吐量更高。


3. 缓存一致性挑战

3.1 数据一致性模型

强一致性: • 定义:缓存与数据库数据实时一致(如金融账户余额)。 • 实现成本:高(需同步阻塞写入、分布式锁)。 • 最终一致性: • 定义:允许短暂不一致,但最终数据一致(如商品库存)。 • 实现方式:异步消息(MQ)或定时任务同步。

3.2 常见问题与解决方案

缓存穿透: • 问题:恶意请求不存在的数据(如查询id=-1),绕过缓存击穿数据库。 • 解决方案java // 布隆过滤器(Guava实现) BloomFilter<String> filter = BloomFilter.create( Funnels.stringFunnel(Charset.defaultCharset()), 10000, 0.01); if (!filter.mightContain(key)) { return null; // 直接拦截非法请求 }

缓存雪崩: • 问题:大量缓存同时过期,请求集中访问数据库。 • 解决方案java // 随机过期时间(30分钟±随机10分钟) redisTemplate.opsForValue().set(key, value, 30 + ThreadLocalRandom.current().nextInt(10), TimeUnit.MINUTES);

缓存击穿: • 问题:热点Key过期后,高并发请求击穿缓存直达数据库。 • 解决方案java // Redisson分布式锁 RLock lock = redissonClient.getLock("product_lock:" + productId); try { if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 获取锁成功,重新加载数据到缓存 return loadDataFromDB(); } } finally { lock.unlock(); }


总结与面试要点

面试高频问题: • Q:如何选择本地缓存和分布式缓存? A:本地缓存用于高频读、低一致性要求的场景(如静态配置),分布式缓存用于跨服务共享数据(如用户会话)。 • Q:多级缓存如何保证数据一致性? A:通过“删除缓存”而非更新缓存、结合消息队列异步同步、设置合理过期时间。 • 技术选型建议: • 小型系统:单级缓存(Redis) + 数据库。 • 中大型系统:Caffeine(L1) + Redis(L2) + MySQL(L3)。

通过理解多级缓存的核心概念与挑战,开发者能够设计出高性能、高可用的缓存架构,有效应对高并发场景的复杂性。


二、多级缓存架构设计模式


1. 经典三级缓存模型

1.1 L1:JVM堆内缓存(Caffeine)

核心特性: • 极速访问:数据存储在JVM堆内存中,基于内存寻址,访问速度在纳秒级。 • 适用场景:高频读取、数据量小(如配置信息、用户会话Token)。 • Caffeine实战配置

Cache<String, Product> productCache = Caffeine.newBuilder()  .maximumSize(10_000)                        // 最大缓存条目数  .expireAfterWrite(5, TimeUnit.MINUTES)      // 写入后5分钟过期  .refreshAfterWrite(1, TimeUnit.MINUTES)    // 1分钟后异步刷新  .recordStats()                              // 开启命中率统计  .build(key -> productDao.getById(key));     // 缓存加载逻辑  

性能优化点: • 使用WeakKeysSoftValues减少内存压力。 • 结合异步刷新(refreshAfterWrite)避免缓存过期瞬间的请求风暴。

1.2 L2:堆外缓存(Offheap/Redis)

堆外缓存(Ehcache Offheap): • 优势:突破JVM堆内存限制,存储更大数据量(如百MB级商品列表)。 • 配置示例xml <ehcache> <cache name="productOffheapCache" maxEntriesLocalHeap="0" maxBytesLocalOffHeap="500MB" timeToLiveSeconds="3600"/> </ehcache>分布式缓存(Redis): • 场景:跨服务共享数据(如用户会话、分布式锁)。 • 数据结构优化java // 使用Hash存储用户信息,减少序列化开销 redisTemplate.opsForHash().put("user:123", "name", "Alice"); redisTemplate.opsForHash().put("user:123", "age", "30");

1.3 L3:持久化存储(MySQL/MongoDB)

兜底策略: • 缓存未命中时:查询数据库并回填缓存,避免直接穿透。 • 批量加载优化java public List<Product> batchLoadProducts(List<String> ids) { // 先查缓存 Map<String, Product> cachedProducts = cache.getAllPresent(ids); // 未命中部分查数据库 List<String> missingIds = ids.stream() .filter(id -> !cachedProducts.containsKey(id)) .collect(Collectors.toList()); List<Product> dbProducts = productDao.batchGet(missingIds); cache.putAll(dbProducts.stream() .collect(Collectors.toMap(Product::getId, Function.identity()))); return Stream.concat(cachedProducts.values().stream(), dbProducts.stream()) .collect(Collectors.toList()); }


2. 读写策略设计

2.1 Cache-Aside(旁路缓存)

读流程

  1. 先查询缓存,命中则返回数据。

  2. 未命中则查询数据库,并将结果写入缓存。 • 写流程

  3. 更新数据库。

  4. 删除或更新缓存(推荐删除,避免并发写导致脏数据)。 • 适用场景:读多写少(如用户详情页)。 • 代码示例

@Transactional  
public void updateProduct(Product product) {  productDao.update(product);  // 删除缓存而非更新,避免并发问题  cache.invalidate(product.getId());  
}  
2.2 Read/Write Through(穿透读写)

核心机制:缓存层代理所有数据库操作。 • 读穿透:缓存未命中时,缓存组件自动加载数据库数据。 • 写穿透:写入缓存时,缓存组件同步更新数据库。 • 实现示例(Caffeine + Spring Cache)

  @Cacheable(value = "products", unless = "#result == null")  public Product getProduct(String id) {  return productDao.getById(id);  }  
​@CachePut(value = "products", key = "#product.id")  public Product updateProduct(Product product) {  return productDao.update(product);  }  
2.3 Write Behind(异步回写)

核心逻辑

  1. 数据先写入缓存,立即返回成功。

  2. 异步批量或延迟写入数据库。 • 风险与优化

• **数据丢失风险**:缓存宕机导致未持久化数据丢失,需结合WAL(Write-Ahead Logging)。  
• **批量合并写入**:将多次更新合并为一次数据库操作,减少IO压力。  

应用场景:写密集且容忍最终一致性的场景(如点赞计数)。


3. 微服务场景下的多级缓存

3.1 网关层缓存(Nginx/OpenResty)

静态资源缓存

location /static/ {  proxy_cache static_cache;  proxy_pass http://static_service;  proxy_cache_valid 200 1h;  add_header X-Cache-Status $upstream_cache_status;  
}  

动态API缓存

-- OpenResty Lua脚本  
local cache = ngx.shared.my_cache  
local key = ngx.var.uri .. ngx.var.args  
local value = cache:get(key)  
if value then  ngx.say(value)  return  
end  
-- 未命中则请求后端并缓存  
local resp = ngx.location.capture("/backend" .. ngx.var.request_uri)  
cache:set(key, resp.body, 60)  -- 缓存60秒  
ngx.say(resp.body)  
3.2 服务层缓存(Spring Cache集成)

注解驱动开发

  @Cacheable(value = "users", key = "#userId", sync = true)  public User getUser(String userId) {  return userDao.getById(userId);  }  
​@CacheEvict(value = "users", key = "#userId")  public void updateUser(User user) {  userDao.update(user);  }  

多级缓存配置

spring:  cache:  type: caffeine  caffeine:  spec: maximumSize=10000,expireAfterWrite=5m  redis:  time-to-live: 1h  
3.3 分布式缓存同步机制

缓存失效广播

// 使用Redis Pub/Sub通知其他节点  
redisTemplate.convertAndSend("cache:invalidate", "user:123");  

版本号控制

  // 缓存值携带版本号  public class CacheValue<T> {  private T data;  private long version;  }  // 更新时校验版本号  if (currentVersion == expectedVersion) {  updateDataAndVersion();  }  

总结与设计原则

多级缓存设计原则: • 层级分明:L1追求速度,L2平衡容量与性能,L3保障数据持久化。 • 失效策略:结合TTL、LRU和主动失效,避免脏数据。 • 微服务缓存要点: • 网关层:拦截高频请求,减少下游压力。 • 服务层:通过注解简化开发,结合本地与分布式缓存。 • 同步机制:采用事件驱动或版本控制,确保跨服务缓存一致性。

生产案例:某电商平台通过三级缓存(Caffeine + Redis + MySQL),将商品详情页QPS从5万提升至50万,数据库负载降低80%。


三、本地缓存实战与性能优化


1. Caffeine深度解析

1.1 缓存淘汰策略对比

LRU(Least Recently Used): • 原理:淘汰最久未被访问的数据。 • 缺点:无法应对突发流量,可能淘汰高频访问但近期未用的数据。 • 示例:访问序列A->B->C->A->B,LRU会淘汰C。

LFU(Least Frequently Used): • 原理:淘汰访问频率最低的数据。 • 缺点:长期保留历史热点数据,无法适应访问模式变化。 • 示例:访问序列A->A->A->B->B,LFU会淘汰B。

W-TinyLFU(Caffeine默认策略): • 原理:结合LFU和LRU,通过滑动窗口统计频率,适应动态访问模式。 • 优势:高吞吐、低内存开销,适合高并发场景。 • 配置示例java Cache<String, User> cache = Caffeine.newBuilder() .maximumSize(10_000) .evictionPolicy(EvictionPolicy.W_TinyLFU) .build();

1.2 过期策略配置

基于大小淘汰

Caffeine.newBuilder()  .maximumSize(1000)  // 最多缓存1000个条目  .build();  

基于时间淘汰

  // 写入后5分钟过期  Caffeine.newBuilder()  .expireAfterWrite(5, TimeUnit.MINUTES)  .build();  // 访问后1分钟过期  Caffeine.newBuilder()  .expireAfterAccess(1, TimeUnit.MINUTES)  .build();  

基于引用淘汰

  // 软引用缓存(内存不足时GC回收)  Caffeine.newBuilder()  .softValues()  .build();  // 弱引用缓存(GC时直接回收)  Caffeine.newBuilder()  .weakKeys()  .weakValues()  .build();  

2. 堆外缓存应用

2.1 Ehcache堆外缓存配置

堆外缓存优势: • 突破JVM堆限制:可缓存GB级数据(如大型商品列表)。 • 减少GC压力:数据存储在堆外内存,避免频繁GC停顿。 • 配置示例

<ehcache>  <cache name="productCache"  maxEntriesLocalHeap="1000"  maxBytesLocalOffHeap="2G"  timeToIdleSeconds="300">  <persistence strategy="none"/>  </cache>  
</ehcache>  

Java代码访问

CacheManager cacheManager = CacheManager.create();  
Ehcache productCache = cacheManager.getEhcache("productCache");  
productCache.put(new Element("p123", new Product("手机")));  
Product product = (Product) productCache.get("p123").getObjectValue();  
2.2 MapDB实现本地持久化缓存

核心特性: • 持久化存储:数据落盘,重启后不丢失。 • 支持复杂结构:Map、Set、Queue等数据结构。 • 使用示例

  // 创建或打开数据库  DB db = DBMaker.fileDB("cache.db").make();  ConcurrentMap<String, Product> map = db.hashMap("products").createOrOpen();  // 写入数据  map.put("p123", new Product("笔记本电脑"));  // 读取数据  Product product = map.get("p123");  // 关闭数据库  db.close();  

适用场景: • 本地缓存需要持久化(如离线应用配置)。 • 大数据量且允许较高读取延迟。


3. 热点数据发现与预热

3.1 基于LRU的热点数据统计

实现思路

  1. 在缓存访问时记录Key的访问时间和频率。

  2. 维护一个LRU队列,定期淘汰尾部低频数据。 • 代码示例

  public class HotspotTracker<K> {  private final LinkedHashMap<K, Long> accessLog = new LinkedHashMap<>(1000, 0.75f, true);  public void trackAccess(K key) {  accessLog.put(key, System.currentTimeMillis());  }  public List<K> getHotKeys(int topN) {  return accessLog.entrySet().stream()  .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue()))  .limit(topN)  .map(Map.Entry::getKey)  .collect(Collectors.toList());  }  }  
3.2 定时任务预热

Spring Scheduler预热示例

@Scheduled(fixedRate = 10 * 60 * 1000) // 每10分钟执行一次  
public void preloadHotData() {  List<String> hotKeys = hotspotTracker.getHotKeys(100);  hotKeys.forEach(key -> {  Product product = productDao.getById(key);  cache.put(key, product);  });  
}  

Quartz动态预热

  public class PreloadJob implements Job {  @Override  public void execute(JobExecutionContext context) {  // 根据业务指标动态调整预热频率  int preloadSize = calculatePreloadSize();  List<String> keys = getHotKeys(preloadSize);  preloadToCache(keys);  }  }  // 配置触发器(每天凌晨2点执行)  Trigger trigger = newTrigger()  .withSchedule(cronSchedule("0 0 2 * * ?"))  .build();  

总结与性能调优建议

Caffeine调优要点: • 监控命中率:通过cache.stats()获取hitRate,低于80%需优化淘汰策略。 • 合理设置过期时间:结合业务特征(如商品信息1小时,价格信息1分钟)。 • 堆外缓存注意事项: • 内存泄漏:确保及时释放不再使用的缓存条目。 • 序列化优化:使用Protostuff等高效序列化工具减少CPU开销。 • 热点数据预热策略: • 冷启动优化:服务启动时加载基础热数据(如首页商品)。 • 动态调整:根据实时流量监控动态增减预热数据量。

面试高频问题: • Q: Caffeine的W-TinyLFU相比传统LRU/LFU有何优势? A: W-TinyLFU通过频率统计窗口和LRU队列,既保留了高频访问数据,又能快速淘汰过时热点,适合动态变化的访问模式。 • Q: 堆外缓存可能引发什么问题? A: 数据需序列化/反序列化(CPU开销),且内存不受JVM管理(需自行监控防止OOM)。

通过本地缓存的精细化管理,系统可显著提升吞吐量,降低响应延迟,为高并发场景提供稳定支撑。


四、分布式缓存集成与高可用


1. Redis多级缓存架构

1.1 主从复制与哨兵模式

主从复制机制: • 核心原理:主节点(Master)处理写请求,数据异步复制到从节点(Slave),从节点仅支持读操作。 • 配置示例: ```bash # 主节点配置(redis.conf) requirepass masterpass

# 从节点配置  
replicaof <master-ip> 6379  
masterauth masterpass  
```  

优势:读写分离提升读吞吐量,主节点宕机时从节点可接管(需手动切换)。

哨兵模式(Sentinel): • 功能:监控主节点健康状态,自动故障转移(选举新主节点)。 • 部署方案bash # 哨兵节点配置(sentinel.conf) sentinel monitor mymaster 192.168.1.100 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000高可用流程: 1. 哨兵检测主节点不可达(超过down-after-milliseconds)。 2. 触发故障转移,选举新主节点。 3. 客户端通过哨兵获取新主节点地址。

1.2 Cluster集群分片与数据分布

数据分片原理: • 哈希槽(Hash Slot):Redis Cluster将数据划分为16384个槽,每个节点负责部分槽。 • 分片算法CRC16(key) % 16384 计算键所属槽。 • 集群部署

  # 启动集群节点  redis-server redis-7000.conf --cluster-enabled yes  # 创建集群(3主3从)  redis-cli --cluster create 192.168.1.100:7000 192.168.1.101:7001 ... --cluster-replicas 1  

节点扩缩容

  # 添加新节点  redis-cli --cluster add-node 192.168.1.105:7005 192.168.1.100:7000  # 迁移槽位  redis-cli --cluster reshard 192.168.1.100:7000  

2. 缓存与数据库同步策略

2.1 延迟双删(Double Delete)

流程

  1. 删除缓存:更新数据库前先删除缓存。

  2. 更新数据库:执行数据库写操作。

  3. 延迟删除:等待短暂时间(如500ms)后再次删除缓存。 • 代码示例

public void updateProduct(Product product) {  // 第一次删除  redisTemplate.delete("product:" + product.getId());  // 更新数据库  productDao.update(product);  // 延迟删除(异步线程池执行)  executor.schedule(() -> {  redisTemplate.delete("product:" + product.getId());  }, 500, TimeUnit.MILLISECONDS);  
}  

适用场景:应对并发写导致的脏数据,需结合业务容忍短暂不一致。

2.2 基于Binlog的异步同步(Canal+MQ)

技术栈: • Canal:解析MySQL Binlog,捕获数据变更事件。 • 消息队列:传输变更事件(如Kafka、RocketMQ)。 • 实现步骤

  1. Canal配置

    # canal.properties  
    canal.destinations = test  
    canal.instance.master.address = 127.0.0.1:3306  
  2. 监听Binlog事件

    // Canal客户端监听  
    CanalConnector connector = CanalConnectors.newClusterConnector(  "127.0.0.1:2181", "test", "", "");  
    connector.subscribe(".*\\..*");  
    Message message = connector.getWithoutAck(100);  
    // 解析消息并发送到MQ  
    kafkaTemplate.send("binlog-events", message.getEntries());  
  3. 消费MQ更新缓存

    @KafkaListener(topics = "binlog-events")  
    public void handleBinlogEvent(Event event) {  if (event.getTable().equals("product")) {  redisTemplate.delete("product:" + event.getRow().get("id"));  }  
    }  

    优势:保证最终一致性,适用于写多读少场景。


3. 分布式锁保障数据一致性

3.1 Redisson分布式锁实现

加锁与释放锁

RLock lock = redissonClient.getLock("product_lock:" + productId);  
try {  // 尝试加锁,等待时间5秒,锁有效期30秒  if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {  Product product = productDao.getById(productId);  // 业务逻辑...  }  
} finally {  lock.unlock();  
}  

锁续期机制: • Watchdog(看门狗):Redisson后台线程每隔10秒检查锁持有状态,若业务未完成则续期锁至30秒。

3.2 锁粒度控制与优化

细粒度锁:按业务ID锁定资源(如lock:order:1001),避免全局锁竞争。 • 分段锁优化

// 将库存拆分为多个段(如10个)  
int segment = productId.hashCode() % 10;  
RLock lock = redissonClient.getLock("stock_lock:" + segment);  

读写锁(ReadWriteLock)

RReadWriteLock rwLock = redissonClient.getReadWriteLock("product_rw_lock");  
rwLock.readLock().lock();  // 允许多个读  
rwLock.writeLock().lock(); // 独占写  

总结与最佳实践

Redis高可用方案选型

场景推荐方案
中小规模、高可用需求哨兵模式(Sentinel)
大规模数据、水平扩展Cluster集群分片
跨地域多活Redis + 代理层(如Twemproxy)

缓存同步策略对比

策略一致性级别适用场景
延迟双删最终一致写并发中等,容忍短暂延迟
Binlog+MQ最终一致写频繁,要求可靠同步
分布式锁强一致高并发写,需严格一致性

生产经验: • 缓存预热:服务启动时加载热点数据,结合历史访问记录预测热点。 • 监控告警:通过Prometheus监控缓存命中率、锁等待时间,设置阈值告警。 • 降级策略:缓存故障时降级为直接读数据库,避免服务雪崩。

故障案例:某电商平台因未设置锁续期机制,导致库存超卖。引入Redisson看门狗后,锁自动续期,问题得以解决。

通过合理设计分布式缓存架构与同步策略,可显著提升系统吞吐量与可用性,同时保障数据一致性,应对高并发挑战。


五、多级缓存实战场景解析


1. 电商高并发场景

1.1 商品详情页多级缓存设计

静态化 + 动态加载架构

  1. 静态化HTML:通过模板引擎(如Thymeleaf)生成静态页面,缓存至CDN或Nginx本地。

    location /product/{id} {  # 优先返回静态HTML  try_files /static/product_$id.html @dynamic_backend;  
    }  
  2. 动态加载:未命中静态页时,通过Ajax加载实时数据(价格、库存)。

    // 前端动态请求  
    fetch(`/api/product/${productId}/dynamic`).then(res => res.json());  
  3. 多级缓存策略: ◦ L1(Nginx):缓存静态HTML,TTL=10分钟。 ◦ L2(Redis):存储动态数据(JSON格式),TTL=30秒。 ◦ L3(MySQL):持久化商品基础信息。

缓存更新机制

@CachePut(value = "product", key = "#product.id")  
public Product updateProduct(Product product) {  // 更新数据库  productDao.update(product);  // 刷新静态HTML(异步任务)  staticPageService.refresh(product.getId());  return product;  
}  
1.2 库存缓存与预扣减方案

预扣减流程

  1. 缓存扣减:使用Redis原子操作扣减库存。

    // Lua脚本保证原子性  
    String script = "if redis.call('get', KEYS[1]) >= ARGV[1] then " +  "return redis.call('decrby', KEYS[1], ARGV[1]) " +  "else return -1 end";  
    Long stock = redisTemplate.execute(script, Collections.singletonList("stock:1001"), "1");  
  2. 数据库同步:异步MQ消息触发数据库库存更新。

    @Transactional  
    public void deductStock(String productId, int count) {  // 先扣Redis  if (redisStockService.deduct(productId, count)) {  // 发送MQ消息同步数据库  mqTemplate.send("stock_deduct", new StockDeductEvent(productId, count));  }  
    }  

    补偿机制

• **超时回滚**:若数据库更新失败,通过定时任务回滚Redis库存。  
• **对账系统**:每日对比Redis与数据库库存差异,修复数据不一致。  

2. 社交平台热点数据

2.1 用户Feed流缓存策略(推拉结合)

推模式(写扩散): • 场景:大V发布内容时,主动推送到所有粉丝的Feed缓存中。 • Redis实现java // 大V发帖时推送到粉丝的Feed列表 followers.forEach(follower -> redisTemplate.opsForList().leftPush("feed:" + follower, post.toJSON()) ); // 控制列表长度,保留最新1000条 redisTemplate.opsForList().trim("feed:" + follower, 0, 999);拉模式(读扩散): • 场景:普通用户读取Feed时,实时拉取关注用户的动态并合并。 • 缓存优化: ```java public List<Post> getFeed(String userId) { // 先查本地缓存 List<Post> cached = caffeineCache.getIfPresent(userId); if (cached != null) return cached;

    // 未命中则查询Redis  List<String> followees = getFollowees(userId);  List<Post> feed = followees.parallelStream()  .flatMap(f -> redisTemplate.opsForList().range("feed:" + f, 0, 100).stream())  .sorted(Comparator.comparing(Post::getTimestamp).reversed())  .limit(100)  .collect(Collectors.toList());  // 写入本地缓存  caffeineCache.put(userId, feed);  return feed;  
}  
```  
2.2 实时排行榜(Redis SortedSet)

积分更新与排名查询

  // 用户完成操作后更新积分  redisTemplate.opsForZSet().incrementScore("leaderboard", "user:123", 10);  // 查询Top 10  Set<ZSetOperations.TypedTuple<String>> topUsers = redisTemplate.opsForZSet()  .reverseRangeWithScores("leaderboard", 0, 9);  

冷热数据分离: • 热榜:Redis存储当天实时数据,TTL=24小时。 • 历史榜:每日凌晨将数据归档至MySQL,供离线分析。


3. 金融交易场景

3.1 资金账户余额缓存(强一致性保障)

同步双写策略

  1. 数据库事务:在事务中更新账户余额。

  2. 立即更新缓存:事务提交后同步更新Redis。

    @Transactional  
    public void transfer(String from, String to, BigDecimal amount) {  // 扣减转出账户  accountDao.deduct(from, amount);  // 增加转入账户  accountDao.add(to, amount);  // 同步更新缓存  redisTemplate.opsForValue().set("balance:" + from, getBalance(from));  redisTemplate.opsForValue().set("balance:" + to, getBalance(to));  
    }  

    兜底校验

• **对账服务**:每小时比对缓存与数据库余额,差异超过阈值触发告警。  
• **事务补偿**:若缓存更新失败,记录日志并异步重试。  
3.2 交易流水异步归档

削峰填谷设计

  1. 流水写入缓存:交易发生时,先写入Redis List。

    redisTemplate.opsForList().rightPush("txn_log", txn.toJSON());  
  2. 批量持久化:定时任务每5分钟批量读取并写入数据库。

    @Scheduled(fixedDelay = 5 * 60 * 1000)  
    public void archiveTxnLogs() {  List<Txn> txns = redisTemplate.opsForList().range("txn_log", 0, -1)  .stream().map(this::parseTxn).collect(Collectors.toList());  txnDao.batchInsert(txns);  redisTemplate.delete("txn_log");  
    }  

    可靠性保障

• **Redis持久化**:开启AOF确保日志不丢失。  
• **幂等写入**:为每条流水生成唯一ID,避免重复插入。  

总结与面试高频问题

场景策略对比

场景缓存核心目标一致性要求关键技术
电商高并发高可用、低延迟最终一致静态化、预扣减、异步对账
社交热点实时性、动态合并最终一致推拉结合、SortedSet、冷热分离
金融交易强一致、数据安全强一致同步双写、事务补偿、幂等设计

高频面试题: • Q:如何解决商品详情页的缓存与数据库不一致问题? A:采用延迟双删策略,结合异步对账服务修复差异。 • Q:推拉结合模式中,如何避免大V粉丝量过大导致的推送性能问题? A:分批次异步推送,或采用“活跃粉丝”策略仅推送最近在线的用户。 • Q:金融场景下,如何保证缓存与数据库的强一致性? A:通过数据库事务保证数据持久化,事务提交后同步更新缓存,结合对账机制兜底。

生产案例:某支付系统通过“同步双写+对账服务”,将余额查询的响应时间从50ms降至5ms,且全年未出现资损事件。

通过针对不同业务场景设计定制化的多级缓存方案,开发者能够在高并发、低延迟、强一致性等需求中找到平衡点,构建高性能且可靠的系统架构。


六、缓存问题解决方案


1. 缓存穿透

1.1 布隆过滤器(Bloom Filter)实现

核心原理:通过多个哈希函数将元素映射到位数组中,判断元素是否存在。 • Guava实现示例

  // 初始化布隆过滤器(预期插入10000个元素,误判率1%)  BloomFilter<String> bloomFilter = BloomFilter.create(  Funnels.stringFunnel(Charset.defaultCharset()), 10000, 0.01);  // 预热数据  List<String> validKeys = getValidKeysFromDB();  validKeys.forEach(bloomFilter::put);  // 查询拦截  public Product getProduct(String id) {  if (!bloomFilter.mightContain(id)) {  return null; // 直接拦截非法请求  }  return cache.get(id, () -> productDao.getById(id));  }  

适用场景:拦截明确不存在的数据(如无效ID、恶意攻击)。

1.2 空值缓存与短过期时间

实现逻辑:将查询结果为空的Key也缓存,避免重复穿透。 • 代码示例

public Product getProduct(String id) {  Product product = cache.get(id);  if (product == null) {  product = productDao.getById(id);  if (product == null) {  // 缓存空值,过期时间5分钟  cache.put(id, Product.EMPTY, 5, TimeUnit.MINUTES);  } else {  cache.put(id, product);  }  }  return product == Product.EMPTY ? null : product;  
}  

注意事项: • 空值需明确标记(如特殊对象),避免与正常数据混淆。 • 短过期时间(如5分钟)防止存储大量无效Key。


2. 缓存雪崩

2.1 随机过期时间

核心思路:为缓存Key设置随机过期时间,避免同时失效。 • 代码实现

public void setWithRandomExpire(String key, Object value, long baseExpire, TimeUnit unit) {  long expire = baseExpire + ThreadLocalRandom.current().nextInt(0, 300); // 随机增加0~5分钟  redisTemplate.opsForValue().set(key, value, expire, unit);  
}  

适用场景:缓存批量预热或定时刷新场景。

2.2 熔断降级与本地容灾缓存

熔断机制:当数据库压力过大时,触发熔断直接返回默认值。

  // Resilience4j熔断配置  CircuitBreakerConfig config = CircuitBreakerConfig.custom()  .failureRateThreshold(50) // 失败率阈值50%  .waitDurationInOpenState(Duration.ofSeconds(30))  .build();  CircuitBreaker circuitBreaker = CircuitBreaker.of("dbCircuitBreaker", config);  public Product getProduct(String id) {  return circuitBreaker.executeSupplier(() -> productDao.getById(id));  }  

本地容灾缓存:使用Ehcache缓存兜底数据。

public Product getProduct(String id) {  Product product = redisTemplate.get(id);  if (product == null) {  product = ehcache.get(id); // 本地缓存兜底  if (product == null) {  throw new ServiceUnavailableException("服务不可用");  }  }  return product;  
}  

3. 缓存击穿

3.1 互斥锁(Mutex Lock)

分布式锁实现:使用Redisson保证只有一个线程加载数据。

public Product getProduct(String id) {  Product product = cache.get(id);  if (product == null) {  RLock lock = redissonClient.getLock("product_lock:" + id);  try {  if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 尝试加锁  product = cache.get(id); // 双重检查  if (product == null) {  product = productDao.getById(id);  cache.put(id, product, 30, TimeUnit.MINUTES);  }  }  } finally {  lock.unlock();  }  }  return product;  
}  

优化点: • 锁粒度细化(按资源ID加锁)。 • 锁超时时间合理设置(避免死锁)。

3.2 逻辑过期时间(Logical Expiration)

实现逻辑:缓存永不过期,但存储逻辑过期时间,异步刷新。

  public class CacheWrapper<T> {  private T data;  private long expireTime; // 逻辑过期时间  public boolean isExpired() {  return System.currentTimeMillis() > expireTime;  }  }  public Product getProduct(String id) {  CacheWrapper<Product> wrapper = cache.get(id);  if (wrapper == null || wrapper.isExpired()) {  // 异步刷新缓存  executor.submit(() -> reloadProduct(id));  return wrapper != null ? wrapper.getData() : null;  }  return wrapper.getData();  }  

优势:用户无感知,始终返回数据,避免请求堆积。


总结与方案对比

问题解决方案适用场景实现复杂度
缓存穿透布隆过滤器 + 空值缓存恶意攻击、无效ID高频访问
缓存雪崩随机过期时间 + 熔断降级批量缓存失效、数据库高负载
缓存击穿互斥锁 + 逻辑过期时间热点数据失效、高并发场景

生产经验: • 监控告警:通过Prometheus监控缓存命中率、穿透率、锁竞争次数。 • 动态调整:根据实时流量调整熔断阈值和锁超时时间。 • 压测验证:定期模拟高并发场景,验证方案有效性。

面试高频问题: • Q:布隆过滤器有什么缺点? A:存在误判率(可通过增加哈希函数降低),且删除元素困难(需使用Counting Bloom Filter)。 • Q:逻辑过期时间如何保证数据最终一致? A:异步线程定期扫描过期Key并刷新,结合版本号或时间戳控制并发更新。

通过针对不同缓存问题的特性设计解决方案,系统可在高并发场景下保持稳定,兼顾性能与可靠性。


七、性能监控与调优


1. 缓存命中率分析

1.1 监控指标定义

Hit Rate(命中率): • 公式Hit Rate = (Cache Hits) / (Cache Hits + Cache Misses)健康指标:建议保持在80%以上,低于60%需优化缓存策略。 • Miss Rate(未命中率): • 公式Miss Rate = 1 - Hit Rate,突增可能预示缓存穿透或雪崩。 • Load Time(加载耗时): • 定义:缓存未命中时从数据库加载数据的平均耗时。 • 告警阈值:若Load Time > 500ms,需优化查询或引入异步加载。

1.2 Prometheus + Grafana可视化

Exporter配置(以Caffeine为例):

// 注册Caffeine指标到Micrometer  
CaffeineCache cache = Caffeine.newBuilder().recordStats().build();  
Metrics.gauge("cache.size", cache, c -> c.estimatedSize());  
Metrics.counter("cache.hits").bindTo(cache.stats().hitCount());  
Metrics.counter("cache.misses").bindTo(cache.stats().missCount());  

Grafana仪表盘

-- 查询命中率  
sum(rate(cache_hits_total[5m])) /   
(sum(rate(cache_hits_total[5m])) + sum(rate(cache_misses_total[5m])))  


2. JVM缓存调优

2.1 堆内缓存GC优化

G1垃圾收集器配置

# 启动参数  
java -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200  

优势:通过Region分区和并发标记,减少GC停顿时间。 • 适用场景:堆内存较大(>4GB)且缓存对象生命周期短。 • ZGC低延迟优化

java -Xms8G -Xmx8G -XX:+UseZGC -XX:MaxMetaspaceSize=512M  

优势:亚毫秒级停顿,适合对延迟敏感的实时系统。 • 限制:JDK 11+支持,内存需超过8GB。

2.2 堆外缓存内存泄漏排查

NMT(Native Memory Tracking)工具

  # 启动应用时开启NMT  java -XX:NativeMemoryTracking=detail -jar app.jar  # 生成内存报告  jcmd <pid> VM.native_memory detail > nmt.log  

分析重点:检查Internal部分的malloc调用是否持续增长。 • Ehcache堆外缓存监控

// 获取堆外内存使用量  
long offHeapSize = ehcache.calculateOffHeapSize();  

3. Redis性能调优

3.1 内存碎片整理

手动触发整理

# 执行内存碎片整理(阻塞操作,建议低峰期执行)  
redis-cli MEMORY PURGE  

自动整理配置

# 当碎片率超过1.5时自动整理  
config set activedefrag yes  
config set active-defrag-ignore-bytes 100mb  
config set active-defrag-threshold-lower 10  
3.2 Pipeline与Lua脚本优化

Pipeline批处理

List<Object> results = redisTemplate.executePipelined(connection -> {  for (int i = 0; i < 1000; i++) {  connection.stringCommands().set(("key:" + i).getBytes(), ("value:" + i).getBytes());  }  return null;  
});  

性能提升:减少网络往返时间(RTT),吞吐量提升5-10倍。 • Lua脚本原子操作

-- 统计在线用户数并设置过期时间  
local key = KEYS[1]  
local user = ARGV[1]  
redis.call('SADD', key, user)  
redis.call('EXPIRE', key, 3600)  
return redis.call('SCARD', key)  

优势:原子性执行复杂操作,减少多次网络交互。


总结与调优建议

缓存命中率调优: • 提升策略:增加缓存容量、优化淘汰策略、预加载热点数据。 • 告警规则:设置命中率低于70%触发告警,并自动扩容Redis集群。 • JVM内存管理: • 堆内缓存:选择G1/ZGC降低GC影响,监控Old Gen使用率。 • 堆外缓存:定期通过NMT分析内存泄漏,限制Ehcache的Offheap大小。 • Redis性能调优: • 内存优化:使用ziplist编码小规模数据,启用jemalloc内存分配器。 • 高并发写入:分片(Sharding)降低单节点压力,开启AOF持久化。

生产案例:某社交平台通过优化Lua脚本(合并10次操作为1次),Redis的QPS从5万提升至15万,CPU使用率下降40%。

持续监控与迭代

  1. 自动化巡检:每周生成缓存健康报告,包含命中率Top10的Key和碎片率。

  2. 容量规划:根据业务增长预测缓存容量,提前扩容。

  3. 压测验证:通过JMeter模拟高峰流量,验证调优效果。

通过系统化的监控与调优,多级缓存架构能够在高并发场景下保持高性能与稳定性,支撑业务快速发展。


八、面试高频题与实战案例


1. 经典面试题

1.1 Redis如何实现分布式锁?如何处理锁续期?

实现原理

  1. 加锁:使用SET key value NX EX seconds命令,保证原子性。

    -- Lua脚本确保原子性  
    if redis.call("SET", KEYS[1], ARGV[1], "NX", "EX", ARGV[2]) then  return 1  
    else  return 0  
    end  
  2. 解锁:通过Lua脚本验证锁持有者,避免误删他人锁。

    if redis.call("GET", KEYS[1]) == ARGV[1] then  return redis.call("DEL", KEYS[1])  
    else  return 0  
    end  

    锁续期(Redisson Watchdog)

• 后台线程每隔10秒检查锁是否仍被持有,若持有则续期至30秒。  
• **代码示例**:  ```java  
RLock lock = redissonClient.getLock("order_lock");  
lock.lock(30, TimeUnit.SECONDS);  // 自动触发Watchdog  
```  

面试加分点: • 误删风险:必须通过唯一客户端标识(UUID)验证锁归属。 • 锁粒度优化:按业务ID分段加锁(如lock:order:1001)。

1.2 如何设计一个支持百万QPS的缓存架构?

分层设计

  1. 客户端缓存:浏览器缓存静态资源(HTML/CSS/JS),CDN加速。

  2. 网关层缓存:Nginx/OpenResty缓存动态API响应(TTL=1秒)。

  3. 服务层缓存: ◦ 本地缓存:Caffeine(L1)缓存热点数据,TTL=500ms。 ◦ 分布式缓存:Redis Cluster(L2)分片存储,单节点QPS可达10万。

  4. 持久层优化:MySQL分库分表 + 读写分离,异步批量写入。 • 核心策略

• **热点数据预加载**:通过离线分析提前缓存高频访问数据。  
• **请求合并**:使用Redis Pipeline或Lua脚本减少网络开销。  
• **限流熔断**:Sentinel或Hystrix保护下游服务。  

2. 场景设计题

2.1 设计一个秒杀系统的多级缓存方案

架构分层

  1. 网关层: ◦ 限流:Nginx漏桶算法限制每秒10万请求。 ◦ 静态化:商品详情页HTML缓存至CDN。

  2. 服务层: ◦ 本地缓存:Caffeine缓存库存余量,TTL=100ms。 ◦ Redis集群:库存预扣减(原子操作DECRBY),扣减成功后再异步落库。

    // Lua脚本保证原子扣减  
    String script = "if redis.call('GET', KEYS[1]) >= ARGV[1] then " +  "redis.call('DECRBY', KEYS[1], ARGV[1]) " +  "return 1 else return 0 end";  
    Long result = redisTemplate.execute(script, List.of("stock:1001"), "1");  
  3. 数据库层: ◦ 异步队列:Kafka缓冲订单请求,批量写入MySQL。 ◦ 分库分表:订单表按用户ID哈希分16库,每库256表。 • 容灾设计

• **降级策略**:若Redis不可用,直接拒绝请求(非核心功能降级)。  
• **对账补偿**:定时任务对比Redis与数据库库存,修复不一致。  
2.2 如何保证缓存与数据库的最终一致性?

方案对比

方案一致性级别实现复杂度适用场景
延迟双删最终一致写并发中等,容忍短暂延迟
Canal+MQ最终一致写频繁,要求可靠同步
分布式事务强一致极高金融交易等高敏感场景
Canal+MQ实现步骤
  1. Binlog订阅:Canal解析MySQL Binlog,推送变更事件到MQ。

  2. 消费MQ更新缓存

    @KafkaListener(topics = "db_events")  
    public void handleEvent(DbEvent event) {  if (event.getTable().equals("product")) {  redisTemplate.delete("product:" + event.getId());  }  
    }  

    版本号控制

// 缓存数据携带版本号  
public class CacheValue {  private Object data;  private long version;  
}  
// 更新时校验版本号  
if (currentVersion == expectedVersion) {  updateDataAndVersion();  
}  

3. 实战案例分析

3.1 某电商大促缓存架构优化(TPS从1万到10万)

优化措施

  1. 多级缓存引入: ◦ L1:Caffeine本地缓存商品详情,TTL=200ms。 ◦ L2:Redis Cluster分片存储库存和价格,单节点QPS提升至5万。

  2. 库存预扣减优化: ◦ Redis Lua脚本原子扣减,异步MQ同步数据库。

  3. 热点数据动态预热: ◦ 基于历史访问数据,提前加载Top 1000商品到本地缓存。

  4. 限流熔断: ◦ 网关层限流(每秒10万请求),服务层熔断(失败率>30%触发)。 • 效果

• TPS从1万提升至10万,数据库负载降低70%。  
• 用户平均响应时间从200ms降至50ms。  
3.2 社交平台热点数据动态降级策略

背景:明星发帖导致瞬时流量激增,Feed流服务崩溃。 • 解决方案

  1. 热点探测:实时统计接口QPS,Top 10热点数据标记为“高危”。

  2. 动态降级: ◦ 本地缓存兜底:返回3分钟前的缓存数据,牺牲实时性保可用性。 ◦ 熔断策略:若Feed流接口QPS超过阈值,直接返回静态推荐列表。

  3. 异步更新:降级期间,后台线程异步刷新热点数据。 • 结果

• 服务可用性从80%提升至99.9%,峰值QPS支持100万。  
• 用户感知为“信息延迟”,但无服务崩溃。  

总结与面试技巧

回答框架

  1. 问题拆解:将复杂问题分解为缓存设计、一致性、性能优化等子问题。

  2. 分层设计:从客户端到数据库逐层分析,明确每层技术选型。

  3. 数据支撑:引用生产案例数据(如QPS提升比例)增强说服力。 • 高频考点

• **缓存 vs 数据库**:何时用缓存?如何保证一致性?  
• **锁 vs 无锁**:Redis锁与CAS无锁化方案的取舍。  

避坑指南: • 避免过度设计:非金融场景不必强求强一致性。 • 监控先行:没有监控的优化是盲目的,Prometheus + Grafana必备。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL的MVCC【学习笔记】

MVCC 事务的隔离级别分为四种&#xff0c;其中Read Committed和Repeatable Read隔离级别&#xff0c;部分实现就是通过MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09; 版本链 版本链是通过undo日志实现的&#xff0c; 事务每次修改…

基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记

基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记 基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记系统架构全景 一、实物演示二、OpenMV端设计要点1. 硬件配置优化2. 智能帧率控制算法3. 数据传输协议设计 三、PyTorch后端核心实现&#xff1a;YOLOv11与PaddleOCR的…

C#中常见的设计模式

文章目录 引言设计模式的分类创建型模式 (Creational Patterns)1. 单例模式 (Singleton)2. 工厂方法模式 (Factory Method)3. 抽象工厂模式 (Abstract Factory)4. 建造者模式 (Builder) 结构型模式 (Structural Patterns)5. 适配器模式 (Adapter)6. 装饰器模式 (Decorator)7. 外…

Nacos简介—3.Nacos的配置简介

大纲 1.Nacos生产集群Web端口与数据库配置 2.Nacos生产集群的Distro协议核心参数 3.Nacos打通CMDB实现跨机房的就近访问 4.Nacos基于SPI动态扩展机制来获取CMDB的数据 5.基于Nacos SPI机制开发CMDB动态扩展 6.Nacos基于CMDB来实现多机房就近访问 7.Nacos生产集群Prometh…

Jest 快照测试

以下是关于 Jest 快照测试的系统化知识总结,从基础使用到底层原理全面覆盖: 一、快照测试核心原理 1. 工作机制三阶段 #mermaid-svg-GC46t2NBvGv7RF0M {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GC46t2NBvGv…

第十六届蓝桥杯大赛软件赛省赛 C/C++ 大学B组 [京津冀]

由于官方没有公布题目的数据, 所以代码仅供参考 1. 密密摆放 题目链接&#xff1a;P12337 [蓝桥杯 2025 省 AB/Python B 第二场] 密密摆放 - 洛谷 题目描述 小蓝有一个大箱子&#xff0c;内部的长宽高分别是 200、250、240&#xff08;单位&#xff1a;毫米&#xff09;&…

Spring 学习笔记之 @Transactional 异常不回滚汇总

使用springboot时&#xff0c;只要引入spring-jdbc/jpa相关的依赖后&#xff0c;在想要启用事务的方法上加上Transactional注解就能开启事务&#xff0c;碰到异常就能自动回滚。大大的提高了编码的便捷性性&#xff0c;同时也不侵入代码&#xff0c;保持了代码的简洁性。 默认情…

React 与 Vue 虚拟 DOM 实现原理深度对比:从理论到实践

在现代前端开发中&#xff0c;React 和 Vue 作为最流行的两大框架&#xff0c;都采用了虚拟 DOM&#xff08;Virtual DOM&#xff09; 技术来优化渲染性能。虚拟 DOM 的核心思想是通过 JavaScript 对象模拟真实 DOM&#xff0c;减少直接操作 DOM 的开销&#xff0c;从而提高页面…

WordPress AI 原创文章自动生成插件 24小时全自动生成SEO原创文章 | 多语言支持 | 智能配图与排版

为什么选择Linkreate AI内容生成插件&#xff1f; ✓ 全自动化工作流程 - 从关键词挖掘到文章发布一站式完成 ✓ 多语言支持 - 轻松覆盖全球市场&#xff08;中/英等多语种&#xff09; ✓ 智能SEO优化 - 自动生成搜索引擎友好的内容结构 ✓ AI智能配图 - 每篇文章自动匹配高质…

GPU加速-系统CUDA12.5-Windows10

误区注意 查看当前系统可支持的最高版本cuda&#xff1a;nvidia-smi 说明&#xff1a; 此处显示的12.7只是驱动对应的最高版本&#xff0c;不一定是 / 也不一定需要是 当前Python使用的版本。但我们所安装的CUDA版本需要 小于等于它&#xff08;即≤12.7&#xff09;因此即使…

IOT项目——DIY 气象站

开源项目&#xff1a;ESP32 气象站 作者&#xff1a;GiovanniAggiustatutto 原文链接&#xff1a;原文 开源项目&#xff1a;太阳能 WiFi 气象站 V4.0 作者&#xff1a;opengreenenergy 原文链接&#xff1a;原文 DIY 气象站 简介1-制版2-物料 温度设备塔风向标风速计雨量计框…

5G助力智慧城市的崛起——从概念到落地的技术实践

5G助力智慧城市的崛起——从概念到落地的技术实践 引言&#xff1a;智慧城市中的“隐形脉络” 随着城市化的快速推进&#xff0c;传统的城市管理方式已经难以满足人口增长和资源优化的需求。智慧城市的概念应运而生&#xff0c;通过技术创新实现智能化、可持续发展的城市生态…

【Linux】web服务器的部署和优化

目录 nginx的安装与启用--/usr/share/nginx/html默认发布目录 nginx的主配置文件--/etc/nginx/nginx_conf nginx的端口 nginx默认发布文件--index.html nginx默认发布目录 nginx的访问控制 基于IP地址的访问控制 基于用户认证的访问控制 nginx的虚拟主机--/etc/nginx/…

结合五层网络结构讲一下用户在浏览器输入一个网址并按下回车后到底发生了什么?

文章目录 实际应用第一步&#xff1a;用户在浏览器输入 www.baidu.com 并按下回车1. 浏览器触发域名解析&#xff08;DNS查询&#xff09; 第二步&#xff1a;DNS请求的逐层封装与传输1. 应用层&#xff08;DNS协议&#xff09;2. 传输层&#xff08;UDP协议&#xff09;3. 网络…

深入理解N皇后问题:从DFS到对角线优化

N皇后问题是一个经典的算法问题&#xff0c;要求在NN的棋盘上放置N个皇后&#xff0c;使得它们互不攻击。本文将全面解析该问题的解法&#xff0c;特别聚焦于DFS算法和对角线优化的数学原理。 问题描述 在NN的国际象棋棋盘上放置N个皇后&#xff0c;要求&#xff1a; 任意两个…

Java面试场景篇:分布式锁的实现与组件详解

互联网大厂Java求职者面试&#xff1a;分布式锁的实现与组件 在一场紧张而又充满挑战的面试中&#xff0c;Java架构师马架构正面对着一位经验丰富的面试官。以下是他们之间关于分布式锁实现方式及相关问题的对话。 第一轮提问 面试官&#xff1a;请介绍一下分布式锁的概念。…

关于使用 读光-文字检测-DBNet行检测模型-中英-通用领域,版本问题

关于使用 读光-文字检测-DBNet行检测模型-中英-通用领域&#xff0c;版本问题 pip install modelscopeSuccessfully installed certifi-2025.4.26 charset-normalizer-3.4.1 colorama-0.4.6 idna-3.10 modelscope-1.25.0 requests-2.32.3 tqdm-4.67.1 urllib3-2.4.0 pip insta…

刷刷刷刷刷RCE

云曦历年考核 25年春开学考 RCCCE 开启题目进行代码审计 GET传参传入一个参数cmd&#xff0c;但对参数内容给了黑名单进行过滤 $blacklist /bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|"|\>|\<|\%|\$/i; ls、cat等都…

2024江西ICPC部分题解

题目列表 A - Maliang Learning PaintingC - LiarG - Multiples of 5H - ConvolutionJ - Magic MahjongK - Magic Tree A - Maliang Learning Painting 题目来源&#xff1a;A - Maliang Learning Painting 思路分析 这是个签到题&#xff0c;直接输出abc即可 #include<b…

Pytorch图像数据转为Tensor张量

PyTorch的所有模型&#xff08;nn.Module&#xff09;都只接受Tensor格式的输入&#xff0c;所以我们在使用图像数据集时&#xff0c;必须将图像转换为Tensor格式。PyTorch提供了torchvision.transforms模块来处理图像数据集。torchvision.transforms模块提供了一些常用的图像预…