成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

張開濤:京東業務數據應用級緩存示例

開發 開發工具
我們的業務數據如商品類目、店鋪、商品基本信息都可以進行適當的本地緩存,以提升性能。對于多實例的情況時不僅會使用本地緩存,還會使用分布式緩存,因此需要進行適當的API封裝以簡化緩存操作。

一、多級緩存API封裝

我們的業務數據如商品類目、店鋪、商品基本信息都可以進行適當的本地緩存,以提升性能。對于多實例的情況時不僅會使用本地緩存,還會使用分布式緩存,因此需要進行適當的API封裝以簡化緩存操作。

[[189271]]

1. 本地緩存初始化

  1. public class LocalCacheInitService extends BaseService { 
  2.    @Override 
  3.     publicvoid afterPropertiesSet() throws Exception { 
  4.         //商品類目緩存 
  5.         Cache<String, Object> categoryCache = 
  6.                CacheBuilder.newBuilder() 
  7.                         .softValues() 
  8.                         .maximumSize(1000000) 
  9.                        .expireAfterWrite(Switches.CATEGORY.getExpiresInSeconds()/ 2, TimeUnit.SECONDS) 
  10.                         .build(); 
  11.        addCache(CacheKeys.CATEGORY_KEY, categoryCache); 
  12.     } 
  13.   
  14.     privatevoid addCache(String key, Cache<??> cache) { 
  15.         localCacheService.addCache(key,cache); 
  16.     } 

本地緩存過期時間使用分布式緩存過期時間的一半,防止本地緩存數據緩存時間太長造成多實例間的數據不一致。

另外,將緩存KEY前綴與本地緩存關聯,從而匹配緩存KEY前綴就可以找到相關聯的本地緩存。

2. 寫緩存API封裝

先寫本地緩存,如果需要寫分布式緩存,則通過異步更新分布式緩存。

  1. public void set(final String key, final Object value, final intremoteCacheExpiresInSeconds) throws RuntimeException { 
  2.     if (value== null) { 
  3.         return; 
  4.     } 
  5.   
  6.     //復制值對象 
  7.     //本地緩存是引用,分布式緩存需要序列化 
  8.     //如果不復制的話,則假設之后數據改了將造成本地緩存與分布式緩存不一致 
  9.     final Object finalValue = copy(value); 
  10.     //如果配置了寫本地緩存,則根據KEY獲得相關的本地緩存,然后寫入 
  11.     if (writeLocalCache) { 
  12.        Cache localCache = getLocalCache(key); 
  13.         if(localCache != null) { 
  14.            localCache.put(key, finalValue); 
  15.         } 
  16.     } 
  17.     //如果配置了不寫分布式緩存,則直接返回 
  18.     if (!writeRemoteCache) { 
  19.         return; 
  20.     } 
  21.     //異步更新分布式緩存 
  22.     asyncTaskExecutor.execute(() -> { 
  23.         try { 
  24.             redisCache.set(key,JSONUtils.toJSON(finalValue), remoteCacheExpiresInSeconds); 
  25.         } catch(Exception e) { 
  26.             LOG.error("updateredis cache error, key : {}", key, e); 
  27.         } 
  28.     }); 

此處使用了異步更新,目的是讓用戶請求盡快返回。而因為有本地緩存,所以即使分布式緩存更新比較慢又產生了回源,也可以在本地緩存***。

3. 讀緩存API封裝

先讀本地緩存,本地緩存不***的再批量查詢分布式緩存,在查詢分布式緩存時通過分區批量查詢。

  1. private Map innerMget(List<String> keys, List<Class> types) throwsException { 
  2.    Map<String, Object> result = Maps.newHashMap(); 
  3.    List<String> missKeys = Lists.newArrayList(); 
  4.    List<Class> missTypes = Lists.newArrayList(); 
  5.     //如果配置了讀本地緩存,則先讀本地緩存 
  6.     if(readLocalCache) { 
  7.         for(int i = 0; i < keys.size(); i++) { 
  8.            String key = keys.get(i); 
  9.            Class type = types.get(i); 
  10.            Cache localCache = getLocalCache(key); 
  11.             if(localCache != null) { 
  12.                Object value = localCache.getIfPresent(key); 
  13.                result.put(key, value); 
  14.                if (value == null) { 
  15.                    missKeys.add(key); 
  16.                     missTypes.add(type); 
  17.                } 
  18.            } else { 
  19.                missKeys.add(key); 
  20.                missTypes.add(type); 
  21.            } 
  22.         } 
  23.     } 
  24.     //如果配置了不讀分布式緩存,則返回 
  25.     if(!readRemoteCache) { 
  26.         returnresult; 
  27.     } 
  28.     finalMap<String, String> missResult = Maps.newHashMap(); 
  29.   
  30.     //對KEY分區,不要一次性批量調用太大 
  31.     final List<List<String>>keysPage = Lists.partition(missKeys, 10); 
  32.    List<Future<Map<String, String>>> pageFutures = Lists.newArrayList(); 
  33.   
  34.     try { 
  35.         //批量獲取分布式緩存數據 
  36.         for(final List<String>partitionKeys : keysPage) { 
  37.            pageFutures.add(asyncTaskExecutor.submit(() -> redisCache.mget(partitionKeys))); 
  38.         } 
  39.         for(Future<Map<String,String>> future : pageFutures) { 
  40.            missResult.putAll(future.get(3000, TimeUnit.MILLISECONDS)); 
  41.         } 
  42.     } catch(Exception e) { 
  43.        pageFutures.forEach(future -> future.cancel(true)); 
  44.         throw e; 
  45.     } 
  46.     //合并result和missResult,此處實現省略 
  47.     return result; 

此處將批量讀緩存進行了分區,防止亂用批量獲取API。

二、NULL Cache

首先,定義NULL對象。

  1. private static final String NULL_STRING =new String(); 

當DB沒有數據時,寫入NULL對象到緩存

  1. //查詢DB 
  2. String value = loadDB(); 
  3. //如果DB沒有數據,則將其封裝為NULL_STRING并放入緩存 
  4. if(value == null) { 
  5.     value = NULL_STRING
  6. myCache.put(id, value); 

讀取數據時,如果發現NULL對象,則返回null,而不是回源到DB

  1. value = suitCache.getIfPresent(id); 
  2. //DB沒有數據,返回null 
  3. if(value == NULL_STRING) { 
  4.     return null; 

通過這種方式可以防止當KEY對應的數據在DB不存在時頻繁查詢DB的情況。

三、強制獲取***數據

在實際應用中,我們經常需要強制更新數據,此時就不能使用緩存數據了,可以通過配置ThreadLocal開關來決定是否強制刷新緩存(refresh方法要配合CacheLoader一起使用)。

  1. if(ForceUpdater.isForceUpdateMyInfo()) { 
  2.     myCache.refresh(skuId); 
  3. String result = myCache.get(skuId); 
  4. if(result == NULL_STRING) { 
  5.     return null; 

四、失敗統計

  1. private LoadingCache<String, AtomicInteger> failedCache = 
  2.        CacheBuilder.newBuilder() 
  3.                .softValues() 
  4.                .maximumSize(10000) 
  5.                .build(new CacheLoader<String, AtomicInteger>() { 
  6.                    @Override 
  7.                     public AtomicIntegerload(String skuId) throws Exception { 
  8.                         return new AtomicInteger(0); 
  9.                    } 
  10.                }); 

當失敗時,通過failedCache.getUnchecked(id).incrementAndGet()增加失敗次數;當成功時,使用failedCache.invalidate(id)失效緩存。通過這種方式可以控制失敗重試次數,而且又是內存敏感緩存。當內存不足時,可以清理該緩存騰出一些空間。

五、延遲報警

  1. private static LoadingCache<String, Integer> alarmCache = 
  2.        CacheBuilder.newBuilder() 
  3.                 .softValues() 
  4.                .maximumSize(10000).expireAfterAccess(1, TimeUnit.HOURS) 
  5.                .build(new CacheLoader<String, Integer>() { 
  6.                    @Override 
  7.                    public Integer load(String key) throws Exception { 
  8.                         return 0; 
  9.                    } 
  10.                }); 
  11.   
  12. //報警代碼 
  13. Integer count = 0
  14. if(redis != null) { 
  15.     StringcountStr = Objects.firstNonNull(redis.opsForValue().get(key), "0"); 
  16.     count =Integer.valueOf(countStr); 
  17. } else { 
  18.     count = alarmCache.get(key); 
  19. if(count % 5 == 0) { //5次報一次 
  20.     //報警 
  21. countcount = count + 1; 
  22. if(redis != null) { 
  23.     redis.opsForValue().set(key,String.valueOf(count), 1, TimeUnit. HOURS); 
  24. } else { 
  25.     alarmCache.put(key,count); 

如果一出問題就報警,則存在報警量非常多或者假報警,因此,可以考慮N久報警了M次,才真正報警。此時,也可以使用Cache來統計。本示例還加入了Redis分布式緩存記錄支持。

六、性能測試

筆者使用JMH 1.14進行基準性能測試,比如測試寫。

  1. @Benchmark 
  2. @Warmup(iterations = 10time = 10timeUnit =TimeUnit.SECONDS) 
  3. @Measurement(iterations = 10time = 10timeUnitTimeUnit.SECONDS) 
  4. @BenchmarkMode(Mode.Throughput) 
  5. @OutputTimeUnit(TimeUnit.SECONDS) 
  6. @Fork(1) 
  7. public void test_1_Write() { 
  8.     counterWritercounterWriter= counterWriter + 1; 
  9.     myCache.put("key"+ counterWriter, "value" + counterWriter); 

使用JMH時首先進行JVM預熱,然后進行度量,產生測試結果(本文使用吞吐量)。建議讀者按照需求進行基準性能測試來選擇適合自己的緩存框架。

【本文是51CTO專欄作者張開濤的原創文章,作者微信公眾號:開濤的博客( kaitao-1234567)】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2017-05-01 17:03:01

Java緩存分布式

2017-05-05 10:13:03

應用級緩存緩存代碼

2017-05-10 11:40:29

緩存Nginx HTTP

2017-05-18 16:07:23

回滾數據庫代碼

2017-04-18 14:49:38

應用層API代碼

2017-06-04 16:24:27

線程線程池中斷

2017-07-02 16:50:21

2017-06-16 15:16:15

2012-12-13 17:38:48

2012年度IT博客大IT博客大賽博客

2016-01-04 15:16:01

京東詳情頁實踐

2016-06-17 14:19:52

數據中心

2022-05-12 14:34:14

京東數據

2010-06-02 17:46:54

MySQL 查詢緩存

2015-07-24 12:38:00

吳靜濤

2016-11-10 14:38:44

京東深度學習

2010-10-19 08:59:40

PHP緩存技術

2024-11-01 10:37:31

2018-01-18 19:11:36

2009-08-13 17:50:49

Hibernate 3
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 97精品国产97久久久久久免费 | 精品一区二区三 | 亚洲精品久久久 | 婷婷久久五月天 | 成人网在线观看 | 久久亚洲一区二区三区四区 | 亚洲国产欧美一区 | 欧美日韩国产一区二区三区 | 免费一级黄色电影 | 九九久久精品 | 国产精品成人一区 | 日韩亚洲欧美综合 | www国产成人免费观看视频,深夜成人网 | 亚洲人的av | 日本一区二区高清视频 | 精品在线一区二区 | 高清欧美性猛交 | 99久久视频 | 一级毛片免费 | 亚洲欧美日韩久久 | 成人国产精品入口免费视频 | 亚洲精品视频免费观看 | 日本一区二区三区在线观看 | 黄色大片在线 | 人人插人人 | 国产视频1区 | 久久久久国产精品一区二区 | 成人av播放 | 国产成人精品在线 | 九九九视频 | 一区二区福利视频 | 在线中文视频 | 日韩在线不卡 | 久久久久久久久久久久久9999 | 精品啪啪| 久久久久国产精品一区 | 久久久久久久av | 成人国产综合 | 久久久久亚洲国产| 亚洲女人的天堂 | 91中文字幕在线观看 |