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必备。

相关推荐
brzhang7 小时前
代码即图表:dbdiagram.io让数据库建模变得简单高效
前端·后端·架构
MaCa .BaKa7 小时前
35-疫苗预约管理系统(微服务)
spring boot·redis·微服务·云原生·架构·springcloud
三原8 小时前
2025 乾坤(qiankun)和 Vue3 最佳实践(提供模版)
vue.js·架构·前端框架
高桐@BILL9 小时前
1.4 大模型应用产品与技术架构
人工智能·架构·agent
超人强11 小时前
运营弹窗管理
架构
Wgllss11 小时前
按需下载!!全动态插件化框架WXDynamicPlugin解析怎么支持的
android·架构·android jetpack
国际云,接待12 小时前
[特殊字符]服务器性能优化:从硬件到AI的全栈调优指南
运维·服务器·人工智能·阿里云·性能优化·架构·云计算
JavaGuide12 小时前
腾讯Java后端一面,被速通了!
网络·http·缓存·程序员·idea·多线程·校招·java基础·并发编程·aio·计算机基础·认证授权
前端太佬13 小时前
从零到一实现扫码登录:一个前端菜鸟的踩坑实录
前端·javascript·架构