核心内容摘要
Axure RP 零基础完全指南:从英文到全中文界面的无缝转换
Java多级缓存设计应对微博明星官宣的高并发场景
多级缓存原理与必要性
1 为什么需要多级缓存单级缓存的问题性能瓶颈所有请求都打到同一缓存层压力集中容错性差缓存层故障直接影响整体可用性网络开销分布式缓存频繁网络IO热点数据压力明星官宣等热点事件导致缓存击穿
2 多级缓存的核心思想客户端 → 本地缓存(L
→ 分布式缓存(L
→ 数据库 ↑ ↑ ↑ 最快访问 内存级 共享缓存 毫秒级响应 纳秒级访问 微秒级访问
多级缓存设计架构
1 典型四级缓存架构// 架构示意┌─────────────────────────────────────────┐ │ 客户端缓存(L
│ │(App/Web端缓存HTTP缓存)│ └─────────────────────────────────────────┘ │ ┌─────────────────────────────────────────┐ │ 本地缓存(L
│ │(Caffeine/GuavaCacheJVM进程内)│ └─────────────────────────────────────────┘ │ ┌─────────────────────────────────────────┐ │ 分布式缓存(L
│ │(RedisCluster/RedisSentinel)│ └─────────────────────────────────────────┘ │ ┌─────────────────────────────────────────┐ │ 数据库缓存/持久层(L
│ │(MySQLQueryCache/数据库连接池)│ └─────────────────────────────────────────┘
2 核心设计要点publicclassMultiLevelCacheConfig{//
本地缓存配置BeanpublicCacheString,ObjectlocalCache(){returnCaffeine.newBuilder().maximumSize(10_
// 最大容量.expireAfterWrite(5,TimeUnit.SECONDS)// 短暂过期时间.expireAfterAccess(2,TimeUnit.SECONDS).recordStats()// 记录统计信息.build();}//
分布式缓存配置BeanpublicRedisCacheManagerredisCacheManager(RedisConnectionFactoryfactory){RedisCacheConfigurationconfigRedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(
)// 比本地缓存长.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newJackson2JsonRedisSerializer(Object.class)));returnRedisCacheManager.builder(factory).cacheDefaults(config).build();}}
具体实现方案
1 缓存加载策略ComponentpublicclassMultiLevelCacheService{AutowiredprivateCacheString,ObjectlocalCache;AutowiredprivateRedisTemplateString,ObjectredisTemplate;AutowiredprivateDataServicedataService;/** * 多级缓存读取流程 */publicObjectgetWithMultiLevel(Stringkey){//
查询本地缓存 (L
ObjectvaluelocalCache.getIfPresent(key);if(value!null){recordCacheHit(local);returnvalue;}//
查询分布式缓存 (L
valueredisTemplate.opsForValue().get(key);if(value!null){// 回填本地缓存localCache.put(key,value);recordCacheHit(redis);returnvalue;}//
防止缓存击穿使用分布式锁StringlockKeylock:key;RLocklockredissonClient.getLock(lockKey);try{if(lock.tryLock(100,10,TimeUnit.MILLISECONDS)){// 双重检查valueredisTemplate.opsForValue().get(key);if(value!null){localCache.put(key,value);returnvalue;}//
查询数据库 (L
valuedataService.getFromDB(key);if(value!null){// 写入各级缓存redisTemplate.opsForValue().set(key,value,60,TimeUnit.SECONDS);localCache.put(key,value);}else{// 缓存空值防止缓存穿透cacheNullValue(key);}returnvalue;}else{// 等待其他线程加载Thread.sleep(
;returnredisTemplate.opsForValue().get(key);}}finally{lock.unlock();}}}
2 热点数据特殊处理ComponentpublicclassHotspotCacheManager{// 热点数据本地缓存更长时间privateCacheString,ObjecthotspotCacheCaffeine.newBuilder().maximumSize(
.expireAfterWrite(30,TimeUnit.SECONDS).build();// 热点Key探测privateConcurrentHashMapString,AtomicIntegerkeyAccessCounternewConcurrentHashMap();/** * 热点探测与特殊缓存 */publicObjectgetWithHotspotDetection(Stringkey){// 访问计数keyAccessCounter.computeIfAbsent(key,k-newAtomicInteger(
).incrementAndGet();// 判断是否为热点例如10秒内访问超过100次if(isHotspotKey(key)){// 从热点专用缓存获取ObjectvaluehotspotCache.getIfPresent(key);if(value!null){returnvalue;}// 热点数据预加载和特殊缓存valueloadHotspotData(key);hotspotCache.put(key,value);// 延长分布式缓存时间redisTemplate.opsForValue().set(key,value,300,TimeUnit.SECONDS);returnvalue;}// 普通数据走常规流程returnmultiLevelCacheService.getWithMultiLevel(key);}privatebooleanisHotspotKey(Stringkey){AtomicIntegercounterkeyAccessCounter.get(key);returncounter!nullcounter.get()100;}}
3 缓存一致性保证ComponentpublicclassCacheConsistencyManager{AutowiredprivateRedisPubSubredisPubSub;/** * 缓存更新时的多级同步 */TransactionalpublicvoidupdateData(Stringkey,ObjectnewValue){//
更新数据库dataService.updateDB(key,newValue);//
删除各级缓存先删后更新策略deleteMultiLevelCache(key);//
异步更新缓存cacheUpdateExecutor.execute(()-{// 延迟双删try{Thread.sleep(
;deleteMultiLevelCache(key);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}});}privatevoiddeleteMultiLevelCache(Stringkey){// 删除本地缓存localCache.invalidate(key);// 删除分布式缓存redisTemplate.delete(key);// 发布缓存失效消息其他节点监听redisPubSub.publish(cache.invalidate,key);}/** * 监听缓存失效消息 */RedisListener(topiccache.invalidate)publicvoidonCacheInvalidate(Stringkey){localCache.invalidate(key);}}
完整的多级缓存实现示例
1 缓存管理器Slf4jComponentpublicclassMultiLevelCacheManager{// 各级缓存配置DataConfigurationProperties(prefixcache.multi-level)publicstaticclassCacheConfig{privateLocalCacheConfiglocalnewLocalCacheConfig();privateRedisCacheConfigredisnewRedisCacheConfig();DatapublicstaticclassLocalCacheConfig{privateintmaximumSize10000;privatelongexpireAfterWrite5000;// msprivatelongexpireAfterAccess2000;// ms}DatapublicstaticclassRedisCacheConfig{privatelongdefaultExpire30000;// msprivatelonghotspotExpire300000;// msprivateStringkeyPrefixcache:;}}AutowiredprivateCacheConfigconfig;// 本地缓存实例privateLoadingCacheString,ObjectlocalCache;PostConstructpublicvoidinit(){localCacheCaffeine.newBuilder().maximumSize(config.getLocal().getMaximumSize()).expireAfterWrite(config.getLocal().getExpireAfterWrite(),TimeUnit.MILLISECONDS).expireAfterAccess(config.getLocal().getExpireAfterAccess(),TimeUnit.MILLISECONDS).recordStats().build(key-loadFromRedis(key));}/** * 核心获取方法 */publicObjectget(Stringkey){try{//
尝试本地缓存returnlocalCache.get(key);}catch(Exceptione){log.warn(Local cache get failed for key: {},key,e);//
降级到Redistry{ObjectvalueredisTemplate.opsForValue().get(config.getRedis().getKeyPrefix()key);if(value!null){// 异步回填本地缓存CompletableFuture.runAsync(()-localCache.put(key,value));}returnvalue;}catch(Exceptionex){log.error(Redis cache get failed for key: {},key,ex);//
最后尝试数据库returndataService.getFromDB(key);}}}/** * 带降级的批量获取适用于微博Feed流 */publicMapString,ObjectbatchGet(ListStringkeys){MapString,ObjectresultnewHashMap();ListStringmissingKeysnewArrayList();//
批量查询本地缓存for(Stringkey:keys){ObjectvaluelocalCache.getIfPresent(key);if(value!null){result.put(key,value);}else{missingKeys.add(key);}}//
批量查询Redis使用pipeline优化if(!missingKeys.isEmpty()){ListObjectredisValuesredisTemplate.executePipelined(connection-{for(Stringkey:missingKeys){connection.stringCommands().get((config.getRedis().getKeyPrefix()key).getBytes());}returnnull;});// 处理Redis结果并回填本地缓存for(inti0;imissingKeys.size();i){StringkeymissingKeys.get(i);ObjectvalueredisValues.get(i);if(value!null){result.put(key,value);localCache.put(key,value);}}}returnresult;}}
2 监控与降级ComponentpublicclassCacheMonitor{AutowiredprivateLoadingCacheString,ObjectlocalCache;AutowiredprivateMeterRegistrymeterRegistry;privateGaugelocalCacheSize;privateCountercacheHitCounter;privateCountercacheMissCounter;PostConstructpublicvoidinitMetrics(){// 监控本地缓存指标localCacheSizeGauge.builder(cache.local.size,localCache,cache-cache.estimatedSize()).register(meterRegistry);cacheHitCounterCounter.builder(cache.hit.total).tag(level,local).register(meterRegistry);cacheMissCounterCounter.builder(cache.miss.total).tag(level,local).register(meterRegistry);// 定时采集缓存统计ScheduledExecutorServiceexecutorExecutors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(this::recordStats,1,1,TimeUnit.MINUTES);}privatevoidrecordStats(){CacheStatsstatslocalCache.stats();Metrics.counter(cache.hit.rate).increment((long)(stats.hitRate()*
);Metrics.counter(cache.miss.rate).increment((long)(stats.missRate()*
);// 记录命中率到日志if(stats.hitRate()
0.
{log.warn(Local cache hit rate is low: {},stats.hitRate());}}/** * 动态调整缓存策略 */Scheduled(fixedDelay
publicvoidadjustCachePolicy(){CacheStatsstatslocalCache.stats();// 根据命中率动态调整if(stats.hitRate()
0.
{// 命中率高可以适当增加缓存时间// ...}elseif(stats.hitRate()
0.
{// 命中率低可能需要调整缓存策略// ...}}}
配置与部署建议
1 application.yml配置# 多级缓存配置cache:multi-level:local:maximum-size:10000expire-after-write:5000expire-after-access:2000redis:default-expire:30000hotspot-expire:300000key-prefix:weibo:cache:cluster:nodes:redis1:6379,redis2:6379,redis3:6379# 热点检测配置hotspot:detection:enabled:truethreshold:100# 10秒内访问次数window-seconds:10preload:true# 是否预加载# 降级配置circuit-breaker:cache:enabled:truefailure-threshold:50timeout-ms:
1
2 部署架构建议┌─────────────────────────────────────────────────────┐ │ Load Balancer (Nginx) │ └─────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌─────┐ ┌─────┐ ┌─────┐ │App 1│ │App 2│ │App 3│ (Java应用集群) │L1 │ │L1 │ │L1 │ (本地缓存) └─────┘ └─────┘ └─────┘ │ │ │ └───────────────┼───────────────┘ │ ┌─────────────────────────────────────────────────────┐ │ Redis Cluster (L2缓存) │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │Redis│ │Redis│ │Redis│ │Redis│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ │ └─────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────┐ │ 数据库集群 (MySQL集群) │ │ 主从复制 读写分离 │ └─────────────────────────────────────────────────────┘
针对微博场景的特殊优化
1 明星官宣场景处理ComponentpublicclassCelebrityAnnouncementHandler{// 预加载机制EventListenerpublicvoidhandleAnnouncementEvent(CelebrityEventevent){StringcelebrityIdevent.getCelebrityId();//
预热缓存preloadCelebrityData(celebrityId);//
动态扩容缓存容量adjustCacheCapacity(celebrityId);//
设置特殊缓存策略setSpecialCachePolicy(celebrityId);}privatevoidpreloadCelebrityData(StringcelebrityId){// 提前加载相关数据到各级缓存ListStringcacheKeysgenerateCacheKeys(celebrityId);cacheKeys.parallelStream().forEach(key-{// 从数据库加载ObjectdatadataService.getCelebrityData(key);// 写入Redis设置较长TTLredisTemplate.opsForValue().set(key,data,1,TimeUnit.HOURS);// 推送到消息队列让其他节点也预热kafkaTemplate.send(cache-preload,key);});}}
七、