最近項目中的本地緩存,看是從Guava改成了Caffeine,據說是性能更好,既然性能更好的話,那麼就用起來吧。不過在使用過程中,發現了單個load和批量loadall方面的一些小設置,記錄一下。
一般說來,我們獲取單條記錄的時候,一般都是 cache.get(id),當數據過期,會從提前設定好的load方法中獲取數據。
同樣的,如果我們想批量獲取記錄的時候,一般都是用cache.getAll(ids),當數據過去,會從提前設定好的loadAll方法中獲取數據。
實際在測試的時候,發現,利用如下的緩存初始化方式,無論怎麼泡cache.getAll(ids)方法,發現總會從load方法中加載數據,硬生生的把批量獲取變成了單個循環獲取,這就導致幾十個redis命令一起發到redis集羣那邊,造成了網絡浪費和性能差異:
batchNumCache = Caffeine.newBuilder() .initialCapacity(cacheSize) .maximumSize(cacheSize) .refreshAfterWrite(cacheTime, TimeUnit.SECONDS) .build(new CacheLoader<Long, Map<String, Integer>>() { @Override public Map<String, Integer> load(Long batchId) throws Exception { return getBatchNumJimdb(batchId); } @Override public Map<Long, Map<String, Integer>> loadAll(Iterable<? extends Long> batchIds) throws Exception { if (Iterables.isEmpty(batchIds)) { return null; } return getAllBatchNumJimdb(Lists.newArrayList(batchIds)); } });
如上代碼可以看出,load方法會調用getBatchNumJimdb加載數據,而loadAll方法會調用getAllBatchNumJimdb加載數據,getAllBatchNumJimdb加載數據的方式就是利用redis的pipeline,一次性將請求發給redis,然後獲取返回結果。
雖然如上代碼不能按照我們既定的方式工作,那麼肯定配置有些什麼問題,後來經過諸多的ut測試,發現設置上expireAfterWrite屬性,整體就完美了。最終代碼如下:
batchNumCache = Caffeine.newBuilder() .initialCapacity(cacheSize) .maximumSize(cacheSize) .refreshAfterWrite(cacheTime, TimeUnit.SECONDS) .expireAfterWrite(cacheTime, TimeUnit.SECONDS) .build(new CacheLoader<Long, Map<String, Integer>>() { @Override public Map<String, Integer> load(Long batchId) throws Exception { return getBatchNumJimdb(batchId); } @Override public Map<Long, Map<String, Integer>> loadAll(Iterable<? extends Long> batchIds) throws Exception { if (Iterables.isEmpty(batchIds)) { return null; } return getAllBatchNumJimdb(Lists.newArrayList(batchIds)); } });
如上代碼,整體就ok了。當使用cache.get的時候,會從load加載,當使用cache.getall的時候,會從loadall加載。