本地?zé)狳c(diǎn)緩存的特點(diǎn)
- 只有熱點(diǎn)數(shù)據(jù)才能進(jìn)入本地?zé)狳c(diǎn)緩存获枝;
- 本地?zé)狳c(diǎn)緩存對(duì)臟讀要非常不敏感伞访,比如商品名稱在 MySQL 中的變更,這個(gè)變更沒有更新到本地緩存中,這是 OK 的,用戶下單時(shí)的商品名稱和用戶看到的商品名稱不一樣屡律,這是 OK 的笨触;
- 本地?zé)狳c(diǎn)緩存內(nèi)存要可控:
- 當(dāng)商品信息在 MySQL 中變更時(shí)扭弧,Redis 中的過期數(shù)據(jù)只需清除即可咬清;
- 但對(duì)本地?zé)狳c(diǎn)緩存來說,很少有方法能清除掉 JVM 中的數(shù)據(jù)的淑廊,因?yàn)橐宄?JVM 中的數(shù)據(jù)逗余,需要每臺(tái)應(yīng)用服務(wù)器都清除掉數(shù)據(jù)才行,可以使用 MQ 做廣播消息的發(fā)送來實(shí)現(xiàn)蒋纬,但這種操作是得不償失的猎荠;
- 本地?zé)狳c(diǎn)緩存的生命周期不會(huì)特別長(zhǎng)坚弱,比 Redis 中 key 的生命周期要短很多,這樣才能做到被動(dòng)失效造成的臟讀非常少关摇;
本地?zé)狳c(diǎn)緩存的設(shè)計(jì)考量
- 本地?zé)狳c(diǎn)緩存的本質(zhì)其實(shí)就是維護(hù)一個(gè) HashMap 的數(shù)據(jù)結(jié)構(gòu)荒叶,但維護(hù)這個(gè)結(jié)構(gòu)是不容易的:
- 首先要支持并發(fā)讀寫,ConcurrentHashMap 的性能不夠理想输虱;
- 要考慮失效時(shí)間和內(nèi)存容量限制的淘汰機(jī)制些楣,比如10分鐘自動(dòng)失效、先進(jìn)先出宪睹、最少訪問頻次 LRU愁茁;
Guava Cache 的特點(diǎn)
- Guava Cache 可控制大小和超時(shí)時(shí)間,本質(zhì)上也是可支持并發(fā)的 HashMap亭病;
- Guava Cache 可配置 LRU(最近最少訪問的 key 優(yōu)先被淘汰) 的策略鹅很;
- Guava Cache 是線程安全的;
基于 Cuava Cache 的本地?zé)狳c(diǎn)緩存的實(shí)現(xiàn)
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
public interface CacheService {
void setCommonCache(String key, Object value);
Object getFromCommonCache(String key);
}
package com.lixinlei.miaosha.service.impl;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.lixinlei.miaosha.service.CacheService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@Service
public class CacheServiceImpl implements CacheService {
private Cache<String, Object> commonCache = null;
@PostConstruct
public void init() {
commonCache = CacheBuilder.newBuilder()
.initialCapacity(10)
.maximumSize(100)
.expireAfterWrite(60, TimeUnit.SECONDS).build();
}
@Override
public void setCommonCache(String key, Object value) {
commonCache.put(key, value);
}
@Override
public Object getFromCommonCache(String key) {
return commonCache.getIfPresent(key);
}
}
@RequestMapping(value = "/get", method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType get(@RequestParam(name = "id") Integer id) {
ItemModel itemModel = null;
// 先從本地緩存中找
itemModel = (ItemModel)cacheService.getFromCommonCache("item_" + id);
if (itemModel == null) {
// 再?gòu)?Redis 中找
itemModel = (ItemModel)redisTemplate.opsForValue().get("item_" + id);
if (itemModel == null) {
// 最后從 MySQL 中找
itemModel = itemService.getItemById(id);
redisTemplate.opsForValue().set("item_" + id, itemModel);
redisTemplate.expire("item_" + id, 10, TimeUnit.MINUTES);
}
cacheService.setCommonCache("item_" + id, itemModel);
}
ItemVO itemVO = this.convertFromItemModel(itemModel);
return CommonReturnType.create(itemVO);
}
引入本地緩存性能提升
- 相對(duì)于 Redis 緩存罪帖,在本地緩存全部命中的情況下促煮,TPS 從 2000 提升到 4000 +(2核8G);
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者